@macrostrat/column-views 3.6.1 → 3.9.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 (138) hide show
  1. package/CHANGELOG.md +144 -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-views.css +118 -89
  8. package/dist/column.cjs +12 -3
  9. package/dist/column.cjs.map +1 -1
  10. package/dist/column.d.ts +11 -0
  11. package/dist/column.js +12 -3
  12. package/dist/column.js.map +1 -1
  13. package/dist/column.module.sass.cjs +15 -15
  14. package/dist/column.module.sass.js +15 -15
  15. package/dist/correlation-chart/main.cjs +156 -42
  16. package/dist/correlation-chart/main.cjs.map +1 -1
  17. package/dist/correlation-chart/main.d.ts +40 -2
  18. package/dist/correlation-chart/main.js +138 -23
  19. package/dist/correlation-chart/main.js.map +1 -1
  20. package/dist/correlation-chart/main.module.sass.cjs +23 -20
  21. package/dist/correlation-chart/main.module.sass.cjs.map +1 -1
  22. package/dist/correlation-chart/main.module.sass.js +23 -20
  23. package/dist/correlation-chart/main.module.sass.js.map +1 -1
  24. package/dist/correlation-chart/prepare-data.cjs +0 -1
  25. package/dist/correlation-chart/prepare-data.cjs.map +1 -1
  26. package/dist/correlation-chart/prepare-data.js +0 -1
  27. package/dist/correlation-chart/prepare-data.js.map +1 -1
  28. package/dist/data-provider/store.cjs +17 -4
  29. package/dist/data-provider/store.cjs.map +1 -1
  30. package/dist/data-provider/store.d.ts +6 -1
  31. package/dist/data-provider/store.js +17 -4
  32. package/dist/data-provider/store.js.map +1 -1
  33. package/dist/data-provider/unit-selection.cjs +4 -1
  34. package/dist/data-provider/unit-selection.cjs.map +1 -1
  35. package/dist/data-provider/unit-selection.d.ts +1 -1
  36. package/dist/data-provider/unit-selection.js +4 -1
  37. package/dist/data-provider/unit-selection.js.map +1 -1
  38. package/dist/index.cjs +16 -4
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.d.ts +1 -0
  41. package/dist/index.js +5 -2
  42. package/dist/index.js.map +1 -1
  43. package/dist/maps/column-correlation/core.cjs +127 -6
  44. package/dist/maps/column-correlation/core.cjs.map +1 -1
  45. package/dist/maps/column-correlation/core.js +128 -7
  46. package/dist/maps/column-correlation/core.js.map +1 -1
  47. package/dist/maps/column-correlation/state.cjs +63 -2
  48. package/dist/maps/column-correlation/state.cjs.map +1 -1
  49. package/dist/maps/column-correlation/state.d.ts +31 -1
  50. package/dist/maps/column-correlation/state.js +63 -2
  51. package/dist/maps/column-correlation/state.js.map +1 -1
  52. package/dist/maps/layers/columns.cjs +5 -1
  53. package/dist/maps/layers/columns.cjs.map +1 -1
  54. package/dist/maps/layers/columns.js +5 -1
  55. package/dist/maps/layers/columns.js.map +1 -1
  56. package/dist/prepare-units/composite-scale.cjs +94 -8
  57. package/dist/prepare-units/composite-scale.cjs.map +1 -1
  58. package/dist/prepare-units/composite-scale.d.ts +39 -0
  59. package/dist/prepare-units/composite-scale.js +96 -10
  60. package/dist/prepare-units/composite-scale.js.map +1 -1
  61. package/dist/prepare-units/helpers.cjs +0 -1
  62. package/dist/prepare-units/helpers.cjs.map +1 -1
  63. package/dist/prepare-units/helpers.js +0 -1
  64. package/dist/prepare-units/helpers.js.map +1 -1
  65. package/dist/prepare-units/index.cjs +33 -12
  66. package/dist/prepare-units/index.cjs.map +1 -1
  67. package/dist/prepare-units/index.js +34 -13
  68. package/dist/prepare-units/index.js.map +1 -1
  69. package/dist/prepare-units/types.cjs.map +1 -1
  70. package/dist/prepare-units/types.d.ts +13 -0
  71. package/dist/prepare-units/types.js.map +1 -1
  72. package/dist/section.cjs +10 -2
  73. package/dist/section.cjs.map +1 -1
  74. package/dist/section.d.ts +5 -0
  75. package/dist/section.js +10 -2
  76. package/dist/section.js.map +1 -1
  77. package/dist/unit-details/age-range.cjs +2 -1
  78. package/dist/unit-details/age-range.cjs.map +1 -1
  79. package/dist/unit-details/age-range.js +2 -1
  80. package/dist/unit-details/age-range.js.map +1 -1
  81. package/dist/unit-details/index.d.ts +1 -1
  82. package/dist/unit-details/panel.cjs +2 -3
  83. package/dist/unit-details/panel.cjs.map +1 -1
  84. package/dist/unit-details/panel.js +1 -2
  85. package/dist/unit-details/panel.js.map +1 -1
  86. package/dist/unit-details/panel.module.sass.cjs +18 -22
  87. package/dist/unit-details/panel.module.sass.cjs.map +1 -1
  88. package/dist/unit-details/panel.module.sass.js +18 -22
  89. package/dist/unit-details/panel.module.sass.js.map +1 -1
  90. package/dist/unit-details/strat-names.cjs +1 -2
  91. package/dist/unit-details/strat-names.cjs.map +1 -1
  92. package/dist/unit-details/strat-names.js +1 -2
  93. package/dist/unit-details/strat-names.js.map +1 -1
  94. package/dist/units/boxes.cjs +20 -36
  95. package/dist/units/boxes.cjs.map +1 -1
  96. package/dist/units/boxes.js +22 -38
  97. package/dist/units/boxes.js.map +1 -1
  98. package/dist/units/composite.cjs +12 -12
  99. package/dist/units/composite.cjs.map +1 -1
  100. package/dist/units/composite.js +12 -12
  101. package/dist/units/composite.js.map +1 -1
  102. package/package.json +9 -9
  103. package/src/animated-age-window.ts +120 -0
  104. package/src/column.module.sass +6 -0
  105. package/src/column.ts +24 -0
  106. package/src/correlation-chart/main.module.sass +46 -2
  107. package/src/correlation-chart/main.ts +210 -17
  108. package/src/correlation-chart/prepare-data.ts +0 -1
  109. package/src/data-provider/store.ts +21 -2
  110. package/src/data-provider/unit-selection.ts +13 -2
  111. package/src/index.ts +1 -0
  112. package/src/maps/column-correlation/column-correlation-map.stories.ts +12 -0
  113. package/src/maps/column-correlation/core.ts +155 -9
  114. package/src/maps/column-correlation/state.ts +110 -2
  115. package/src/maps/layers/columns.ts +7 -1
  116. package/src/prepare-units/composite-scale.ts +210 -10
  117. package/src/prepare-units/helpers.ts +0 -2
  118. package/src/prepare-units/index.ts +68 -14
  119. package/src/prepare-units/types.ts +13 -0
  120. package/src/section.ts +15 -1
  121. package/src/unit-details/age-range.ts +3 -1
  122. package/src/unit-details/index.ts +7 -1
  123. package/src/unit-details/panel.module.sass +0 -8
  124. package/src/unit-details/panel.ts +6 -6
  125. package/src/unit-details/strat-names.ts +1 -1
  126. package/src/units/boxes.ts +33 -53
  127. package/src/units/composite.ts +21 -12
  128. package/dist/correlation-chart/stories/utils.d.ts +0 -6
  129. package/dist/unit-details/identifiers.cjs +0 -56
  130. package/dist/unit-details/identifiers.cjs.map +0 -1
  131. package/dist/unit-details/identifiers.d.ts +0 -31
  132. package/dist/unit-details/identifiers.js +0 -54
  133. package/dist/unit-details/identifiers.js.map +0 -1
  134. package/src/correlation-chart/stories/correlation-chart.stories.ts +0 -228
  135. package/src/correlation-chart/stories/namibia-correlation.stories.ts +0 -151
  136. package/src/correlation-chart/stories/stories.module.sass +0 -18
  137. package/src/correlation-chart/stories/utils.ts +0 -24
  138. package/src/unit-details/identifiers.ts +0 -67
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { BasicUnitComponent, ColoredUnitComponent, EnvironmentColoredUnitComponent, SimpleUnitsColumn, UnitComponent, UnitsColumn, useUnitColor, useUnitColorByEnvironment } from "./units/index.js";
2
2
  import { AgeCursor, ApproximateHeightAxis, CompositeAgeAxis, CompositeAgeAxisCore, VerticalAxisLabel } from "./age-axis.js";
3
3
  import { prepareColumnUnits, usePreparedColumnUnits } from "./prepare-units/index.js";
4
+ import { useAnimatedAgeWindow } from "./animated-age-window.js";
4
5
  import { Column, ColumnBasicInfo, ColumnContainer } from "./column.js";
5
6
  import { BoundaryAgeModelOverlay, ComputedSurfacesOverlay } from "./age-model-overlay.js";
6
7
  import { ColumnNotes } from "./notes.js";
@@ -8,10 +9,10 @@ import { AgeField, AgeLabel, AgeRange, Duration, IntervalProportions, getAge, ge
8
9
  import { AnnotatedUnitsColumn, ColumnNotesProvider, CompositeUnitComponent, CompositeUnitsColumn, LabelTrackerProvider, SectionLabelsColumn, TrackedLabeledUnit } from "./units/composite.js";
9
10
  import { BaseColumnsLayer, SelectedColumnOverlay, buildColumnsStyle } from "./maps/layers/columns.js";
10
11
  import { BaseMeasurementsColumn, TruncatedList, groupNotesByPixelDistance, mergeHeightRanges, standardizeMeasurementHeight } from "./facets/measurements/base.js";
11
- import { ClickableText, Identifier, UnitIdentifier } from "./unit-details/identifiers.js";
12
+ import { ClickableText, Identifier, UnitIdentifier } from "@macrostrat/data-components";
12
13
  import { ColumnCenters, ColumnFeatures, ColumnKeyboardNavigation, Columns, CurrentColumn, MeasurementsLayer } from "./maps/column-navigation/svg/layers.js";
13
14
  import { ColumnCorrelationMap } from "./maps/column-correlation/core.js";
14
- import { ColumnCorrelationProvider, useCorrelationMapStore } from "./maps/column-correlation/state.js";
15
+ import { ColumnCorrelationProvider, useColumnMapLink, useCorrelationMapStore } from "./maps/column-correlation/state.js";
15
16
  import { ColumnHoverInteraction, ColumnNavigationMap, ColumnSelectionManager, ColumnsNavigationLayer } from "./maps/column-navigation/mapbox/index.js";
16
17
  import { ColumnNavProvider, useColumnNav } from "./data-provider/column-nav.js";
17
18
  import { ColumnNavigationSVGMap, ResizableMapFrame } from "./maps/column-navigation/svg/index.js";
@@ -147,7 +148,9 @@ export {
147
148
  standardizeMeasurementHeight,
148
149
  stringifyLine,
149
150
  unitsOverlap,
151
+ useAnimatedAgeWindow,
150
152
  useAtomOverlayPosition,
153
+ useColumnMapLink,
151
154
  useColumnNav,
152
155
  useColumnRef,
153
156
  useColumnUnitsMap,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -24,11 +24,40 @@ function ColumnCorrelationMap(props) {
24
24
  h__default.default(ColumnsLayer, { color: columnColor }),
25
25
  h__default.default(SelectedColumnsLayer),
26
26
  h__default.default(MapClickHandler),
27
+ h__default.default(ColumnInteractionHandler),
28
+ h__default.default(HoveredColumnHighlight),
29
+ h__default.default(ColumnZoomer, { padding }),
27
30
  h__default.default(SectionLine, { padding }),
28
31
  children
29
32
  ]
30
33
  );
31
34
  }
35
+ function ColumnZoomer({ padding }) {
36
+ const zoomColumn = state.useCorrelationMapStore((state2) => state2.zoomColumn);
37
+ const zoomNonce = state.useCorrelationMapStore((state2) => state2.zoomNonce);
38
+ const columns2 = state.useCorrelationMapStore((state2) => state2.columns);
39
+ const bounds = React.useMemo(() => {
40
+ if (zoomColumn == null || columns2 == null) return null;
41
+ const col = columns2.find((c) => c.properties?.col_id === zoomColumn);
42
+ if (col?.geometry == null) return null;
43
+ const b = new mapboxGl.LngLatBounds();
44
+ forEachCoordinate(col.geometry, (coord) => b.extend(coord));
45
+ return b.isEmpty() ? null : b;
46
+ }, [zoomColumn, zoomNonce, columns2]);
47
+ mapboxReact.useMapEaseTo({ bounds, padding: padding * 2 });
48
+ return null;
49
+ }
50
+ function forEachCoordinate(geometry, fn) {
51
+ const walk = (coords) => {
52
+ if (typeof coords[0] === "number") {
53
+ fn(coords);
54
+ } else {
55
+ for (const c of coords) walk(c);
56
+ }
57
+ };
58
+ if (geometry?.coordinates != null) walk(geometry.coordinates);
59
+ }
60
+ const columnLayers = ["columns-fill", "columns-points"];
32
61
  function MapClickHandler() {
33
62
  const onClickMap = state.useCorrelationMapStore((state2) => state2.onClickMap);
34
63
  mapboxReact.useMapClickHandler(
@@ -39,6 +68,90 @@ function MapClickHandler() {
39
68
  );
40
69
  return null;
41
70
  }
71
+ function ColumnInteractionHandler() {
72
+ const toggleColumn = state.useCorrelationMapStore((state2) => state2.toggleColumn);
73
+ const setHoveredColumn = state.useCorrelationMapStore(
74
+ (state2) => state2.setHoveredColumn
75
+ );
76
+ const isManual = state.useCorrelationMapStore(
77
+ (state2) => state2.selectionMode === "manual"
78
+ );
79
+ const hoveredRef = React.useRef(null);
80
+ mapboxReact.useMapStyleOperator(
81
+ (map) => {
82
+ if (!isManual) return;
83
+ const onClick = (e) => {
84
+ const colID = e.features?.[0]?.properties?.col_id;
85
+ if (colID != null) toggleColumn(colID);
86
+ };
87
+ map.on("click", columnLayers, onClick);
88
+ return () => map.off("click", columnLayers, onClick);
89
+ },
90
+ [isManual, toggleColumn]
91
+ );
92
+ mapboxReact.useMapStyleOperator(
93
+ (map) => {
94
+ const clearHover = () => {
95
+ if (hoveredRef.current != null) {
96
+ map.setFeatureState(
97
+ { source: "columns", id: hoveredRef.current },
98
+ { hover: false }
99
+ );
100
+ hoveredRef.current = null;
101
+ }
102
+ };
103
+ const onMove = (e) => {
104
+ const f = e.features?.[0];
105
+ if (f == null) return;
106
+ if (hoveredRef.current !== f.id) {
107
+ clearHover();
108
+ map.setFeatureState({ source: "columns", id: f.id }, { hover: true });
109
+ hoveredRef.current = f.id;
110
+ }
111
+ setHoveredColumn(f.properties?.col_id ?? null);
112
+ map.getCanvas().style.cursor = "pointer";
113
+ };
114
+ const onLeave = () => {
115
+ clearHover();
116
+ setHoveredColumn(null);
117
+ map.getCanvas().style.cursor = "";
118
+ };
119
+ map.on("mousemove", columnLayers, onMove);
120
+ map.on("mouseleave", columnLayers, onLeave);
121
+ return () => {
122
+ map.off("mousemove", columnLayers, onMove);
123
+ map.off("mouseleave", columnLayers, onLeave);
124
+ clearHover();
125
+ };
126
+ },
127
+ [setHoveredColumn]
128
+ );
129
+ return null;
130
+ }
131
+ function HoveredColumnHighlight() {
132
+ const hoveredColumn = state.useCorrelationMapStore((state2) => state2.hoveredColumn);
133
+ const columns2 = state.useCorrelationMapStore((state2) => state2.columns);
134
+ const appliedRef = React.useRef(null);
135
+ mapboxReact.useMapStyleOperator(
136
+ (map) => {
137
+ if (appliedRef.current != null) {
138
+ map.setFeatureState(
139
+ { source: "columns", id: appliedRef.current },
140
+ { highlighted: false }
141
+ );
142
+ appliedRef.current = null;
143
+ }
144
+ if (hoveredColumn == null || columns2 == null) return;
145
+ const col = columns2.find((c) => c.properties?.col_id === hoveredColumn);
146
+ const id = col?.id ?? col?.properties?.col_id;
147
+ if (id == null) return;
148
+ map.setFeatureState({ source: "columns", id }, { highlighted: true });
149
+ appliedRef.current = id;
150
+ },
151
+ [hoveredColumn, columns2]
152
+ );
153
+ return null;
154
+ }
42
155
  function SelectedColumnsLayer() {
43
156
  mapboxReact.useOverlayStyle(() => selectedColumnsStyle, []);
44
157
  const focusedColumns = state.useCorrelationMapStore(
@@ -123,9 +236,14 @@ const lineOfSectionStyle = {
123
236
  function SectionLine({ padding }) {
124
237
  mapboxReact.useOverlayStyle(() => lineOfSectionStyle, []);
125
238
  const focusedLine = state.useCorrelationMapStore((state2) => state2.focusedLine);
239
+ const focusedColumns = state.useCorrelationMapStore(
240
+ (state2) => state2.focusedColumns
241
+ );
126
242
  mapboxReact.useMapStyleOperator(
127
243
  (map) => {
128
244
  if (focusedLine == null) {
245
+ mapboxUtils.setGeoJSON(map, "crossSectionLine", emptyFeatureCollection);
246
+ mapboxUtils.setGeoJSON(map, "crossSectionEndpoints", emptyFeatureCollection);
129
247
  return;
130
248
  }
131
249
  const data = {
@@ -151,17 +269,20 @@ function SectionLine({ padding }) {
151
269
  [focusedLine]
152
270
  );
153
271
  const bounds = React.useMemo(() => {
154
- if (focusedLine == null || focusedLine?.coordinates.length < 2) {
155
- return null;
156
- }
157
- let bounds2 = new mapboxGl.LngLatBounds();
158
- for (let coord of focusedLine.coordinates) {
272
+ const coords = focusedLine != null && focusedLine.coordinates.length >= 2 ? focusedLine.coordinates : focusedColumns.map((c) => c.properties.centroid.geometry.coordinates);
273
+ if (coords.length < 2) return null;
274
+ const bounds2 = new mapboxGl.LngLatBounds();
275
+ for (const coord of coords) {
159
276
  bounds2.extend(coord);
160
277
  }
161
278
  return bounds2;
162
- }, [focusedLine]);
279
+ }, [focusedLine, focusedColumns]);
163
280
  mapboxReact.useMapEaseTo({ bounds, padding, trackResize: true });
164
281
  return null;
165
282
  }
283
+ const emptyFeatureCollection = {
284
+ type: "FeatureCollection",
285
+ features: []
286
+ };
166
287
  exports.ColumnCorrelationMap = ColumnCorrelationMap;
167
288
  //# sourceMappingURL=core.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"core.cjs","sources":["../../../src/maps/column-correlation/core.ts"],"sourcesContent":["import {\n useMapClickHandler,\n useMapEaseTo,\n useMapStyleOperator,\n useOverlayStyle,\n} from \"@macrostrat/mapbox-react\";\nimport { LngLatBounds, Style } from \"mapbox-gl\";\nimport h from \"@macrostrat/hyper\";\nimport { Feature, FeatureCollection } from \"geojson\";\nimport { ReactNode, useMemo } from \"react\";\nimport { setGeoJSON, buildGeoJSONSource } from \"@macrostrat/mapbox-utils\";\n\nimport { useCorrelationMapStore } from \"./state\";\nimport { InsetMap, type InsetMapProps } from \"../inset-map\";\nimport { BaseColumnsLayer } from \"../layers\";\nimport { buildCrossSectionLayers } from \"@macrostrat/map-styles\";\n\nexport interface CorrelationMapProps extends InsetMapProps {\n padding?: number;\n children?: ReactNode;\n accessToken?: string;\n columnColor?: string;\n}\n\nexport function ColumnCorrelationMap(props: CorrelationMapProps) {\n const { padding = 50, children, columnColor, projectID, ...rest } = props;\n\n return h(\n InsetMap,\n {\n ...rest,\n boxZoom: false,\n dragRotate: false,\n },\n [\n h(ColumnsLayer, { color: columnColor }),\n h(SelectedColumnsLayer),\n h(MapClickHandler),\n h(SectionLine, { padding }),\n children,\n ],\n );\n}\n\nfunction MapClickHandler() {\n const onClickMap = useCorrelationMapStore((state) => state.onClickMap);\n\n useMapClickHandler(\n (e) => {\n onClickMap(e, { type: \"Point\", coordinates: e.lngLat.toArray() });\n },\n [onClickMap],\n );\n\n return null;\n}\n\nfunction SelectedColumnsLayer() {\n useOverlayStyle(() => selectedColumnsStyle, []);\n\n const focusedColumns = useCorrelationMapStore(\n (state) => state.focusedColumns,\n );\n\n useMapStyleOperator(\n (map) => {\n let features = focusedColumns;\n\n const data: FeatureCollection = {\n type: \"FeatureCollection\",\n features,\n };\n\n const columnCentroidLine: Feature = {\n type: \"Feature\",\n geometry: {\n type: \"LineString\",\n coordinates: features.map(\n (col) => col.properties.centroid.geometry.coordinates,\n ),\n },\n properties: {},\n };\n\n setGeoJSON(map, \"selected-columns\", data);\n setGeoJSON(map, \"selected-column-centroids\", {\n type: \"FeatureCollection\",\n features: [columnCentroidLine],\n });\n },\n [focusedColumns],\n );\n return null;\n}\n\nfunction ColumnsLayer({ enabled = true, color }) {\n const columns = useCorrelationMapStore((state) => state.columns);\n return h(BaseColumnsLayer, { enabled, color, columns });\n}\n\nconst selectedColumnsStyle: Style = {\n version: 8,\n sources: {\n \"selected-columns\": buildGeoJSONSource(),\n \"selected-column-centroids\": buildGeoJSONSource(),\n },\n layers: [\n {\n id: \"selected-columns-fill\",\n type: \"fill\",\n source: \"selected-columns\",\n paint: {\n \"fill-color\": \"rgba(255, 0, 0, 0.1)\",\n },\n },\n {\n id: \"selected-column-centroids-line\",\n type: \"line\",\n source: \"selected-column-centroids\",\n paint: {\n \"line-color\": \"rgba(255, 0, 0, 0.8)\",\n \"line-width\": 2,\n \"line-dasharray\": [2, 2],\n },\n },\n {\n id: \"selected-column-centroids-points\",\n type: \"circle\",\n source: \"selected-column-centroids\",\n paint: {\n \"circle-radius\": 4,\n \"circle-color\": \"rgba(255, 0, 0, 0.8)\",\n },\n },\n ],\n};\n\nconst lineOfSectionStyle: Style = {\n version: 8,\n sources: {\n elevationMarker: buildGeoJSONSource(),\n crossSectionLine: buildGeoJSONSource(),\n crossSectionEndpoints: buildGeoJSONSource(),\n },\n layers: buildCrossSectionLayers(),\n};\n\nfunction SectionLine({ padding }: { padding: number }) {\n useOverlayStyle(() => lineOfSectionStyle, []);\n const focusedLine = useCorrelationMapStore((state) => state.focusedLine);\n\n // Setup focused line\n useMapStyleOperator(\n (map) => {\n if (focusedLine == null) {\n return;\n }\n const data: FeatureCollection = {\n type: \"FeatureCollection\",\n features: [\n {\n type: \"Feature\",\n geometry: focusedLine,\n properties: { id: \"focusedLine\" },\n },\n ],\n };\n\n setGeoJSON(map, \"crossSectionLine\", data);\n setGeoJSON(map, \"crossSectionEndpoints\", {\n type: \"FeatureCollection\",\n features: focusedLine.coordinates.map((coord) => ({\n type: \"Feature\",\n geometry: { type: \"Point\", coordinates: coord },\n properties: {},\n })),\n });\n },\n [focusedLine],\n );\n\n const bounds = useMemo(() => {\n if (focusedLine == null || focusedLine?.coordinates.length < 2) {\n return null;\n }\n let bounds = new LngLatBounds();\n for (let coord of focusedLine.coordinates) {\n bounds.extend(coord);\n }\n return bounds;\n }, [focusedLine]);\n\n useMapEaseTo({ bounds, padding, trackResize: true });\n\n return null;\n}\n"],"names":["h","InsetMap","useCorrelationMapStore","state","useMapClickHandler","useOverlayStyle","useMapStyleOperator","setGeoJSON","columns","BaseColumnsLayer","buildGeoJSONSource","buildCrossSectionLayers","useMemo","bounds","LngLatBounds","useMapEaseTo"],"mappings":"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"}
1
+ {"version":3,"file":"core.cjs","sources":["../../../src/maps/column-correlation/core.ts"],"sourcesContent":["import {\n useMapClickHandler,\n useMapEaseTo,\n useMapStyleOperator,\n useOverlayStyle,\n} from \"@macrostrat/mapbox-react\";\nimport { LngLatBounds, Style } from \"mapbox-gl\";\nimport h from \"@macrostrat/hyper\";\nimport { Feature, FeatureCollection } from \"geojson\";\nimport { ReactNode, useMemo, useRef } from \"react\";\nimport { setGeoJSON, buildGeoJSONSource } from \"@macrostrat/mapbox-utils\";\n\nimport { useCorrelationMapStore } from \"./state\";\nimport { InsetMap, type InsetMapProps } from \"../inset-map\";\nimport { BaseColumnsLayer } from \"../layers\";\nimport { buildCrossSectionLayers } from \"@macrostrat/map-styles\";\n\nexport interface CorrelationMapProps extends InsetMapProps {\n padding?: number;\n children?: ReactNode;\n accessToken?: string;\n columnColor?: string;\n}\n\nexport function ColumnCorrelationMap(props: CorrelationMapProps) {\n const { padding = 50, children, columnColor, projectID, ...rest } = props;\n\n return h(\n InsetMap,\n {\n ...rest,\n boxZoom: false,\n dragRotate: false,\n },\n [\n h(ColumnsLayer, { color: columnColor }),\n h(SelectedColumnsLayer),\n h(MapClickHandler),\n h(ColumnInteractionHandler),\n h(HoveredColumnHighlight),\n h(ColumnZoomer, { padding }),\n h(SectionLine, { padding }),\n children,\n ],\n );\n}\n\nfunction ColumnZoomer({ padding }: { padding: number }) {\n /** Frame a single column when `zoomColumn` is set (e.g. header click). */\n const zoomColumn = useCorrelationMapStore((state) => state.zoomColumn);\n const zoomNonce = useCorrelationMapStore((state) => state.zoomNonce);\n const columns = useCorrelationMapStore((state) => state.columns);\n\n const bounds = useMemo(() => {\n if (zoomColumn == null || columns == null) return null;\n const col = columns.find((c) => c.properties?.col_id === zoomColumn);\n if (col?.geometry == null) return null;\n const b = new LngLatBounds();\n forEachCoordinate(col.geometry, (coord) => b.extend(coord as [number, number]));\n return b.isEmpty() ? null : b;\n // zoomNonce forces a fresh bounds object so repeat clicks re-frame\n }, [zoomColumn, zoomNonce, columns]);\n\n useMapEaseTo({ bounds, padding: padding * 2 });\n return null;\n}\n\nfunction forEachCoordinate(geometry: any, fn: (coord: number[]) => void) {\n const walk = (coords: any) => {\n if (typeof coords[0] === \"number\") {\n fn(coords);\n } else {\n for (const c of coords) walk(c);\n }\n };\n if (geometry?.coordinates != null) walk(geometry.coordinates);\n}\n\nconst columnLayers = [\"columns-fill\", \"columns-points\"];\n\nfunction MapClickHandler() {\n const onClickMap = useCorrelationMapStore((state) => state.onClickMap);\n\n useMapClickHandler(\n (e) => {\n onClickMap(e, { type: \"Point\", coordinates: e.lngLat.toArray() });\n },\n [onClickMap],\n );\n\n return null;\n}\n\nfunction ColumnInteractionHandler() {\n /** Per-column click (manual selection) and hover highlighting. */\n const toggleColumn = useCorrelationMapStore((state) => state.toggleColumn);\n const setHoveredColumn = useCorrelationMapStore(\n (state) => state.setHoveredColumn,\n );\n const isManual = useCorrelationMapStore(\n (state) => state.selectionMode === \"manual\",\n );\n const hoveredRef = useRef<number | string | null>(null);\n\n // Click a column to toggle it in/out of the selection (manual mode only)\n useMapStyleOperator(\n (map) => {\n if (!isManual) return;\n const onClick = (e) => {\n const colID = e.features?.[0]?.properties?.col_id;\n if (colID != null) toggleColumn(colID);\n };\n map.on(\"click\", columnLayers, onClick);\n return () => map.off(\"click\", columnLayers, onClick);\n },\n [isManual, toggleColumn],\n );\n\n // Hover highlighting (via the columns layer's `hover` feature-state)\n useMapStyleOperator(\n (map) => {\n const clearHover = () => {\n if (hoveredRef.current != null) {\n map.setFeatureState(\n { source: \"columns\", id: hoveredRef.current },\n { hover: false },\n );\n hoveredRef.current = null;\n }\n };\n const onMove = (e) => {\n const f = e.features?.[0];\n if (f == null) return;\n if (hoveredRef.current !== f.id) {\n clearHover();\n map.setFeatureState({ source: \"columns\", id: f.id }, { hover: true });\n hoveredRef.current = f.id;\n }\n setHoveredColumn(f.properties?.col_id ?? null);\n map.getCanvas().style.cursor = \"pointer\";\n };\n const onLeave = () => {\n clearHover();\n setHoveredColumn(null);\n map.getCanvas().style.cursor = \"\";\n };\n map.on(\"mousemove\", columnLayers, onMove);\n map.on(\"mouseleave\", columnLayers, onLeave);\n return () => {\n map.off(\"mousemove\", columnLayers, onMove);\n map.off(\"mouseleave\", columnLayers, onLeave);\n clearHover();\n };\n },\n [setHoveredColumn],\n );\n\n return null;\n}\n\nfunction HoveredColumnHighlight() {\n /** Reflects the store's `hoveredColumn` on the map (e.g. when a column is\n * hovered elsewhere in the UI) using the `highlighted` feature-state. */\n const hoveredColumn = useCorrelationMapStore((state) => state.hoveredColumn);\n const columns = useCorrelationMapStore((state) => state.columns);\n const appliedRef = useRef<number | string | null>(null);\n\n useMapStyleOperator(\n (map) => {\n if (appliedRef.current != null) {\n map.setFeatureState(\n { source: \"columns\", id: appliedRef.current },\n { highlighted: false },\n );\n appliedRef.current = null;\n }\n if (hoveredColumn == null || columns == null) return;\n const col = columns.find((c) => c.properties?.col_id === hoveredColumn);\n const id = (col as any)?.id ?? col?.properties?.col_id;\n if (id == null) return;\n map.setFeatureState({ source: \"columns\", id }, { highlighted: true });\n appliedRef.current = id;\n },\n [hoveredColumn, columns],\n );\n\n return null;\n}\n\nfunction SelectedColumnsLayer() {\n useOverlayStyle(() => selectedColumnsStyle, []);\n\n const focusedColumns = useCorrelationMapStore(\n (state) => state.focusedColumns,\n );\n\n useMapStyleOperator(\n (map) => {\n let features = focusedColumns;\n\n const data: FeatureCollection = {\n type: \"FeatureCollection\",\n features,\n };\n\n const columnCentroidLine: Feature = {\n type: \"Feature\",\n geometry: {\n type: \"LineString\",\n coordinates: features.map(\n (col) => col.properties.centroid.geometry.coordinates,\n ),\n },\n properties: {},\n };\n\n setGeoJSON(map, \"selected-columns\", data);\n setGeoJSON(map, \"selected-column-centroids\", {\n type: \"FeatureCollection\",\n features: [columnCentroidLine],\n });\n },\n [focusedColumns],\n );\n return null;\n}\n\nfunction ColumnsLayer({ enabled = true, color }) {\n const columns = useCorrelationMapStore((state) => state.columns);\n return h(BaseColumnsLayer, { enabled, color, columns });\n}\n\nconst selectedColumnsStyle: Style = {\n version: 8,\n sources: {\n \"selected-columns\": buildGeoJSONSource(),\n \"selected-column-centroids\": buildGeoJSONSource(),\n },\n layers: [\n {\n id: \"selected-columns-fill\",\n type: \"fill\",\n source: \"selected-columns\",\n paint: {\n \"fill-color\": \"rgba(255, 0, 0, 0.1)\",\n },\n },\n {\n id: \"selected-column-centroids-line\",\n type: \"line\",\n source: \"selected-column-centroids\",\n paint: {\n \"line-color\": \"rgba(255, 0, 0, 0.8)\",\n \"line-width\": 2,\n \"line-dasharray\": [2, 2],\n },\n },\n {\n id: \"selected-column-centroids-points\",\n type: \"circle\",\n source: \"selected-column-centroids\",\n paint: {\n \"circle-radius\": 4,\n \"circle-color\": \"rgba(255, 0, 0, 0.8)\",\n },\n },\n ],\n};\n\nconst lineOfSectionStyle: Style = {\n version: 8,\n sources: {\n elevationMarker: buildGeoJSONSource(),\n crossSectionLine: buildGeoJSONSource(),\n crossSectionEndpoints: buildGeoJSONSource(),\n },\n layers: buildCrossSectionLayers(),\n};\n\nfunction SectionLine({ padding }: { padding: number }) {\n useOverlayStyle(() => lineOfSectionStyle, []);\n const focusedLine = useCorrelationMapStore((state) => state.focusedLine);\n const focusedColumns = useCorrelationMapStore(\n (state) => state.focusedColumns,\n );\n\n // Setup focused line (only drawn in line-of-section mode)\n useMapStyleOperator(\n (map) => {\n if (focusedLine == null) {\n // Clear any previously-drawn line (e.g. after switching to manual mode)\n setGeoJSON(map, \"crossSectionLine\", emptyFeatureCollection);\n setGeoJSON(map, \"crossSectionEndpoints\", emptyFeatureCollection);\n return;\n }\n const data: FeatureCollection = {\n type: \"FeatureCollection\",\n features: [\n {\n type: \"Feature\",\n geometry: focusedLine,\n properties: { id: \"focusedLine\" },\n },\n ],\n };\n\n setGeoJSON(map, \"crossSectionLine\", data);\n setGeoJSON(map, \"crossSectionEndpoints\", {\n type: \"FeatureCollection\",\n features: focusedLine.coordinates.map((coord) => ({\n type: \"Feature\",\n geometry: { type: \"Point\", coordinates: coord },\n properties: {},\n })),\n });\n },\n [focusedLine],\n );\n\n const bounds = useMemo(() => {\n // In line mode, frame the line; in manual mode, frame the selected columns\n const coords: number[][] =\n focusedLine != null && focusedLine.coordinates.length >= 2\n ? focusedLine.coordinates\n : focusedColumns.map((c) => c.properties.centroid.geometry.coordinates);\n if (coords.length < 2) return null;\n const bounds = new LngLatBounds();\n for (const coord of coords) {\n bounds.extend(coord as [number, number]);\n }\n return bounds;\n }, [focusedLine, focusedColumns]);\n\n useMapEaseTo({ bounds, padding, trackResize: true });\n\n return null;\n}\n\nconst emptyFeatureCollection: FeatureCollection = {\n type: \"FeatureCollection\",\n features: [],\n};\n"],"names":["h","InsetMap","useCorrelationMapStore","state","columns","useMemo","LngLatBounds","useMapEaseTo","useMapClickHandler","useRef","useMapStyleOperator","useOverlayStyle","setGeoJSON","BaseColumnsLayer","buildGeoJSONSource","buildCrossSectionLayers","bounds"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { useOverlayStyle, useMapStyleOperator, useMapClickHandler, useMapEaseTo } from "@macrostrat/mapbox-react";
2
2
  import { LngLatBounds } from "mapbox-gl";
3
3
  import h from "@macrostrat/hyper";
4
- import { useMemo } from "react";
4
+ import { useRef, useMemo } from "react";
5
5
  import { setGeoJSON, buildGeoJSONSource } from "@macrostrat/mapbox-utils";
6
6
  import { useCorrelationMapStore } from "./state.js";
7
7
  import { InsetMap } from "../inset-map/index.js";
@@ -20,11 +20,40 @@ function ColumnCorrelationMap(props) {
20
20
  h(ColumnsLayer, { color: columnColor }),
21
21
  h(SelectedColumnsLayer),
22
22
  h(MapClickHandler),
23
+ h(ColumnInteractionHandler),
24
+ h(HoveredColumnHighlight),
25
+ h(ColumnZoomer, { padding }),
23
26
  h(SectionLine, { padding }),
24
27
  children
25
28
  ]
26
29
  );
27
30
  }
31
+ function ColumnZoomer({ padding }) {
32
+ const zoomColumn = useCorrelationMapStore((state) => state.zoomColumn);
33
+ const zoomNonce = useCorrelationMapStore((state) => state.zoomNonce);
34
+ const columns = useCorrelationMapStore((state) => state.columns);
35
+ const bounds = useMemo(() => {
36
+ if (zoomColumn == null || columns == null) return null;
37
+ const col = columns.find((c) => c.properties?.col_id === zoomColumn);
38
+ if (col?.geometry == null) return null;
39
+ const b = new LngLatBounds();
40
+ forEachCoordinate(col.geometry, (coord) => b.extend(coord));
41
+ return b.isEmpty() ? null : b;
42
+ }, [zoomColumn, zoomNonce, columns]);
43
+ useMapEaseTo({ bounds, padding: padding * 2 });
44
+ return null;
45
+ }
46
+ function forEachCoordinate(geometry, fn) {
47
+ const walk = (coords) => {
48
+ if (typeof coords[0] === "number") {
49
+ fn(coords);
50
+ } else {
51
+ for (const c of coords) walk(c);
52
+ }
53
+ };
54
+ if (geometry?.coordinates != null) walk(geometry.coordinates);
55
+ }
56
+ const columnLayers = ["columns-fill", "columns-points"];
28
57
  function MapClickHandler() {
29
58
  const onClickMap = useCorrelationMapStore((state) => state.onClickMap);
30
59
  useMapClickHandler(
@@ -35,6 +64,90 @@ function MapClickHandler() {
35
64
  );
36
65
  return null;
37
66
  }
67
+ function ColumnInteractionHandler() {
68
+ const toggleColumn = useCorrelationMapStore((state) => state.toggleColumn);
69
+ const setHoveredColumn = useCorrelationMapStore(
70
+ (state) => state.setHoveredColumn
71
+ );
72
+ const isManual = useCorrelationMapStore(
73
+ (state) => state.selectionMode === "manual"
74
+ );
75
+ const hoveredRef = useRef(null);
76
+ useMapStyleOperator(
77
+ (map) => {
78
+ if (!isManual) return;
79
+ const onClick = (e) => {
80
+ const colID = e.features?.[0]?.properties?.col_id;
81
+ if (colID != null) toggleColumn(colID);
82
+ };
83
+ map.on("click", columnLayers, onClick);
84
+ return () => map.off("click", columnLayers, onClick);
85
+ },
86
+ [isManual, toggleColumn]
87
+ );
88
+ useMapStyleOperator(
89
+ (map) => {
90
+ const clearHover = () => {
91
+ if (hoveredRef.current != null) {
92
+ map.setFeatureState(
93
+ { source: "columns", id: hoveredRef.current },
94
+ { hover: false }
95
+ );
96
+ hoveredRef.current = null;
97
+ }
98
+ };
99
+ const onMove = (e) => {
100
+ const f = e.features?.[0];
101
+ if (f == null) return;
102
+ if (hoveredRef.current !== f.id) {
103
+ clearHover();
104
+ map.setFeatureState({ source: "columns", id: f.id }, { hover: true });
105
+ hoveredRef.current = f.id;
106
+ }
107
+ setHoveredColumn(f.properties?.col_id ?? null);
108
+ map.getCanvas().style.cursor = "pointer";
109
+ };
110
+ const onLeave = () => {
111
+ clearHover();
112
+ setHoveredColumn(null);
113
+ map.getCanvas().style.cursor = "";
114
+ };
115
+ map.on("mousemove", columnLayers, onMove);
116
+ map.on("mouseleave", columnLayers, onLeave);
117
+ return () => {
118
+ map.off("mousemove", columnLayers, onMove);
119
+ map.off("mouseleave", columnLayers, onLeave);
120
+ clearHover();
121
+ };
122
+ },
123
+ [setHoveredColumn]
124
+ );
125
+ return null;
126
+ }
127
+ function HoveredColumnHighlight() {
128
+ const hoveredColumn = useCorrelationMapStore((state) => state.hoveredColumn);
129
+ const columns = useCorrelationMapStore((state) => state.columns);
130
+ const appliedRef = useRef(null);
131
+ useMapStyleOperator(
132
+ (map) => {
133
+ if (appliedRef.current != null) {
134
+ map.setFeatureState(
135
+ { source: "columns", id: appliedRef.current },
136
+ { highlighted: false }
137
+ );
138
+ appliedRef.current = null;
139
+ }
140
+ if (hoveredColumn == null || columns == null) return;
141
+ const col = columns.find((c) => c.properties?.col_id === hoveredColumn);
142
+ const id = col?.id ?? col?.properties?.col_id;
143
+ if (id == null) return;
144
+ map.setFeatureState({ source: "columns", id }, { highlighted: true });
145
+ appliedRef.current = id;
146
+ },
147
+ [hoveredColumn, columns]
148
+ );
149
+ return null;
150
+ }
38
151
  function SelectedColumnsLayer() {
39
152
  useOverlayStyle(() => selectedColumnsStyle, []);
40
153
  const focusedColumns = useCorrelationMapStore(
@@ -119,9 +232,14 @@ const lineOfSectionStyle = {
119
232
  function SectionLine({ padding }) {
120
233
  useOverlayStyle(() => lineOfSectionStyle, []);
121
234
  const focusedLine = useCorrelationMapStore((state) => state.focusedLine);
235
+ const focusedColumns = useCorrelationMapStore(
236
+ (state) => state.focusedColumns
237
+ );
122
238
  useMapStyleOperator(
123
239
  (map) => {
124
240
  if (focusedLine == null) {
241
+ setGeoJSON(map, "crossSectionLine", emptyFeatureCollection);
242
+ setGeoJSON(map, "crossSectionEndpoints", emptyFeatureCollection);
125
243
  return;
126
244
  }
127
245
  const data = {
@@ -147,18 +265,21 @@ function SectionLine({ padding }) {
147
265
  [focusedLine]
148
266
  );
149
267
  const bounds = useMemo(() => {
150
- if (focusedLine == null || focusedLine?.coordinates.length < 2) {
151
- return null;
152
- }
153
- let bounds2 = new LngLatBounds();
154
- for (let coord of focusedLine.coordinates) {
268
+ const coords = focusedLine != null && focusedLine.coordinates.length >= 2 ? focusedLine.coordinates : focusedColumns.map((c) => c.properties.centroid.geometry.coordinates);
269
+ if (coords.length < 2) return null;
270
+ const bounds2 = new LngLatBounds();
271
+ for (const coord of coords) {
155
272
  bounds2.extend(coord);
156
273
  }
157
274
  return bounds2;
158
- }, [focusedLine]);
275
+ }, [focusedLine, focusedColumns]);
159
276
  useMapEaseTo({ bounds, padding, trackResize: true });
160
277
  return null;
161
278
  }
279
+ const emptyFeatureCollection = {
280
+ type: "FeatureCollection",
281
+ features: []
282
+ };
162
283
  export {
163
284
  ColumnCorrelationMap
164
285
  };
@@ -1 +1 @@
1
- {"version":3,"file":"core.js","sources":["../../../src/maps/column-correlation/core.ts"],"sourcesContent":["import {\n useMapClickHandler,\n useMapEaseTo,\n useMapStyleOperator,\n useOverlayStyle,\n} from \"@macrostrat/mapbox-react\";\nimport { LngLatBounds, Style } from \"mapbox-gl\";\nimport h from \"@macrostrat/hyper\";\nimport { Feature, FeatureCollection } from \"geojson\";\nimport { ReactNode, useMemo } from \"react\";\nimport { setGeoJSON, buildGeoJSONSource } from \"@macrostrat/mapbox-utils\";\n\nimport { useCorrelationMapStore } from \"./state\";\nimport { InsetMap, type InsetMapProps } from \"../inset-map\";\nimport { BaseColumnsLayer } from \"../layers\";\nimport { buildCrossSectionLayers } from \"@macrostrat/map-styles\";\n\nexport interface CorrelationMapProps extends InsetMapProps {\n padding?: number;\n children?: ReactNode;\n accessToken?: string;\n columnColor?: string;\n}\n\nexport function ColumnCorrelationMap(props: CorrelationMapProps) {\n const { padding = 50, children, columnColor, projectID, ...rest } = props;\n\n return h(\n InsetMap,\n {\n ...rest,\n boxZoom: false,\n dragRotate: false,\n },\n [\n h(ColumnsLayer, { color: columnColor }),\n h(SelectedColumnsLayer),\n h(MapClickHandler),\n h(SectionLine, { padding }),\n children,\n ],\n );\n}\n\nfunction MapClickHandler() {\n const onClickMap = useCorrelationMapStore((state) => state.onClickMap);\n\n useMapClickHandler(\n (e) => {\n onClickMap(e, { type: \"Point\", coordinates: e.lngLat.toArray() });\n },\n [onClickMap],\n );\n\n return null;\n}\n\nfunction SelectedColumnsLayer() {\n useOverlayStyle(() => selectedColumnsStyle, []);\n\n const focusedColumns = useCorrelationMapStore(\n (state) => state.focusedColumns,\n );\n\n useMapStyleOperator(\n (map) => {\n let features = focusedColumns;\n\n const data: FeatureCollection = {\n type: \"FeatureCollection\",\n features,\n };\n\n const columnCentroidLine: Feature = {\n type: \"Feature\",\n geometry: {\n type: \"LineString\",\n coordinates: features.map(\n (col) => col.properties.centroid.geometry.coordinates,\n ),\n },\n properties: {},\n };\n\n setGeoJSON(map, \"selected-columns\", data);\n setGeoJSON(map, \"selected-column-centroids\", {\n type: \"FeatureCollection\",\n features: [columnCentroidLine],\n });\n },\n [focusedColumns],\n );\n return null;\n}\n\nfunction ColumnsLayer({ enabled = true, color }) {\n const columns = useCorrelationMapStore((state) => state.columns);\n return h(BaseColumnsLayer, { enabled, color, columns });\n}\n\nconst selectedColumnsStyle: Style = {\n version: 8,\n sources: {\n \"selected-columns\": buildGeoJSONSource(),\n \"selected-column-centroids\": buildGeoJSONSource(),\n },\n layers: [\n {\n id: \"selected-columns-fill\",\n type: \"fill\",\n source: \"selected-columns\",\n paint: {\n \"fill-color\": \"rgba(255, 0, 0, 0.1)\",\n },\n },\n {\n id: \"selected-column-centroids-line\",\n type: \"line\",\n source: \"selected-column-centroids\",\n paint: {\n \"line-color\": \"rgba(255, 0, 0, 0.8)\",\n \"line-width\": 2,\n \"line-dasharray\": [2, 2],\n },\n },\n {\n id: \"selected-column-centroids-points\",\n type: \"circle\",\n source: \"selected-column-centroids\",\n paint: {\n \"circle-radius\": 4,\n \"circle-color\": \"rgba(255, 0, 0, 0.8)\",\n },\n },\n ],\n};\n\nconst lineOfSectionStyle: Style = {\n version: 8,\n sources: {\n elevationMarker: buildGeoJSONSource(),\n crossSectionLine: buildGeoJSONSource(),\n crossSectionEndpoints: buildGeoJSONSource(),\n },\n layers: buildCrossSectionLayers(),\n};\n\nfunction SectionLine({ padding }: { padding: number }) {\n useOverlayStyle(() => lineOfSectionStyle, []);\n const focusedLine = useCorrelationMapStore((state) => state.focusedLine);\n\n // Setup focused line\n useMapStyleOperator(\n (map) => {\n if (focusedLine == null) {\n return;\n }\n const data: FeatureCollection = {\n type: \"FeatureCollection\",\n features: [\n {\n type: \"Feature\",\n geometry: focusedLine,\n properties: { id: \"focusedLine\" },\n },\n ],\n };\n\n setGeoJSON(map, \"crossSectionLine\", data);\n setGeoJSON(map, \"crossSectionEndpoints\", {\n type: \"FeatureCollection\",\n features: focusedLine.coordinates.map((coord) => ({\n type: \"Feature\",\n geometry: { type: \"Point\", coordinates: coord },\n properties: {},\n })),\n });\n },\n [focusedLine],\n );\n\n const bounds = useMemo(() => {\n if (focusedLine == null || focusedLine?.coordinates.length < 2) {\n return null;\n }\n let bounds = new LngLatBounds();\n for (let coord of focusedLine.coordinates) {\n bounds.extend(coord);\n }\n return bounds;\n }, [focusedLine]);\n\n useMapEaseTo({ bounds, padding, trackResize: true });\n\n return null;\n}\n"],"names":["bounds"],"mappings":"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"}
1
+ {"version":3,"file":"core.js","sources":["../../../src/maps/column-correlation/core.ts"],"sourcesContent":["import {\n useMapClickHandler,\n useMapEaseTo,\n useMapStyleOperator,\n useOverlayStyle,\n} from \"@macrostrat/mapbox-react\";\nimport { LngLatBounds, Style } from \"mapbox-gl\";\nimport h from \"@macrostrat/hyper\";\nimport { Feature, FeatureCollection } from \"geojson\";\nimport { ReactNode, useMemo, useRef } from \"react\";\nimport { setGeoJSON, buildGeoJSONSource } from \"@macrostrat/mapbox-utils\";\n\nimport { useCorrelationMapStore } from \"./state\";\nimport { InsetMap, type InsetMapProps } from \"../inset-map\";\nimport { BaseColumnsLayer } from \"../layers\";\nimport { buildCrossSectionLayers } from \"@macrostrat/map-styles\";\n\nexport interface CorrelationMapProps extends InsetMapProps {\n padding?: number;\n children?: ReactNode;\n accessToken?: string;\n columnColor?: string;\n}\n\nexport function ColumnCorrelationMap(props: CorrelationMapProps) {\n const { padding = 50, children, columnColor, projectID, ...rest } = props;\n\n return h(\n InsetMap,\n {\n ...rest,\n boxZoom: false,\n dragRotate: false,\n },\n [\n h(ColumnsLayer, { color: columnColor }),\n h(SelectedColumnsLayer),\n h(MapClickHandler),\n h(ColumnInteractionHandler),\n h(HoveredColumnHighlight),\n h(ColumnZoomer, { padding }),\n h(SectionLine, { padding }),\n children,\n ],\n );\n}\n\nfunction ColumnZoomer({ padding }: { padding: number }) {\n /** Frame a single column when `zoomColumn` is set (e.g. header click). */\n const zoomColumn = useCorrelationMapStore((state) => state.zoomColumn);\n const zoomNonce = useCorrelationMapStore((state) => state.zoomNonce);\n const columns = useCorrelationMapStore((state) => state.columns);\n\n const bounds = useMemo(() => {\n if (zoomColumn == null || columns == null) return null;\n const col = columns.find((c) => c.properties?.col_id === zoomColumn);\n if (col?.geometry == null) return null;\n const b = new LngLatBounds();\n forEachCoordinate(col.geometry, (coord) => b.extend(coord as [number, number]));\n return b.isEmpty() ? null : b;\n // zoomNonce forces a fresh bounds object so repeat clicks re-frame\n }, [zoomColumn, zoomNonce, columns]);\n\n useMapEaseTo({ bounds, padding: padding * 2 });\n return null;\n}\n\nfunction forEachCoordinate(geometry: any, fn: (coord: number[]) => void) {\n const walk = (coords: any) => {\n if (typeof coords[0] === \"number\") {\n fn(coords);\n } else {\n for (const c of coords) walk(c);\n }\n };\n if (geometry?.coordinates != null) walk(geometry.coordinates);\n}\n\nconst columnLayers = [\"columns-fill\", \"columns-points\"];\n\nfunction MapClickHandler() {\n const onClickMap = useCorrelationMapStore((state) => state.onClickMap);\n\n useMapClickHandler(\n (e) => {\n onClickMap(e, { type: \"Point\", coordinates: e.lngLat.toArray() });\n },\n [onClickMap],\n );\n\n return null;\n}\n\nfunction ColumnInteractionHandler() {\n /** Per-column click (manual selection) and hover highlighting. */\n const toggleColumn = useCorrelationMapStore((state) => state.toggleColumn);\n const setHoveredColumn = useCorrelationMapStore(\n (state) => state.setHoveredColumn,\n );\n const isManual = useCorrelationMapStore(\n (state) => state.selectionMode === \"manual\",\n );\n const hoveredRef = useRef<number | string | null>(null);\n\n // Click a column to toggle it in/out of the selection (manual mode only)\n useMapStyleOperator(\n (map) => {\n if (!isManual) return;\n const onClick = (e) => {\n const colID = e.features?.[0]?.properties?.col_id;\n if (colID != null) toggleColumn(colID);\n };\n map.on(\"click\", columnLayers, onClick);\n return () => map.off(\"click\", columnLayers, onClick);\n },\n [isManual, toggleColumn],\n );\n\n // Hover highlighting (via the columns layer's `hover` feature-state)\n useMapStyleOperator(\n (map) => {\n const clearHover = () => {\n if (hoveredRef.current != null) {\n map.setFeatureState(\n { source: \"columns\", id: hoveredRef.current },\n { hover: false },\n );\n hoveredRef.current = null;\n }\n };\n const onMove = (e) => {\n const f = e.features?.[0];\n if (f == null) return;\n if (hoveredRef.current !== f.id) {\n clearHover();\n map.setFeatureState({ source: \"columns\", id: f.id }, { hover: true });\n hoveredRef.current = f.id;\n }\n setHoveredColumn(f.properties?.col_id ?? null);\n map.getCanvas().style.cursor = \"pointer\";\n };\n const onLeave = () => {\n clearHover();\n setHoveredColumn(null);\n map.getCanvas().style.cursor = \"\";\n };\n map.on(\"mousemove\", columnLayers, onMove);\n map.on(\"mouseleave\", columnLayers, onLeave);\n return () => {\n map.off(\"mousemove\", columnLayers, onMove);\n map.off(\"mouseleave\", columnLayers, onLeave);\n clearHover();\n };\n },\n [setHoveredColumn],\n );\n\n return null;\n}\n\nfunction HoveredColumnHighlight() {\n /** Reflects the store's `hoveredColumn` on the map (e.g. when a column is\n * hovered elsewhere in the UI) using the `highlighted` feature-state. */\n const hoveredColumn = useCorrelationMapStore((state) => state.hoveredColumn);\n const columns = useCorrelationMapStore((state) => state.columns);\n const appliedRef = useRef<number | string | null>(null);\n\n useMapStyleOperator(\n (map) => {\n if (appliedRef.current != null) {\n map.setFeatureState(\n { source: \"columns\", id: appliedRef.current },\n { highlighted: false },\n );\n appliedRef.current = null;\n }\n if (hoveredColumn == null || columns == null) return;\n const col = columns.find((c) => c.properties?.col_id === hoveredColumn);\n const id = (col as any)?.id ?? col?.properties?.col_id;\n if (id == null) return;\n map.setFeatureState({ source: \"columns\", id }, { highlighted: true });\n appliedRef.current = id;\n },\n [hoveredColumn, columns],\n );\n\n return null;\n}\n\nfunction SelectedColumnsLayer() {\n useOverlayStyle(() => selectedColumnsStyle, []);\n\n const focusedColumns = useCorrelationMapStore(\n (state) => state.focusedColumns,\n );\n\n useMapStyleOperator(\n (map) => {\n let features = focusedColumns;\n\n const data: FeatureCollection = {\n type: \"FeatureCollection\",\n features,\n };\n\n const columnCentroidLine: Feature = {\n type: \"Feature\",\n geometry: {\n type: \"LineString\",\n coordinates: features.map(\n (col) => col.properties.centroid.geometry.coordinates,\n ),\n },\n properties: {},\n };\n\n setGeoJSON(map, \"selected-columns\", data);\n setGeoJSON(map, \"selected-column-centroids\", {\n type: \"FeatureCollection\",\n features: [columnCentroidLine],\n });\n },\n [focusedColumns],\n );\n return null;\n}\n\nfunction ColumnsLayer({ enabled = true, color }) {\n const columns = useCorrelationMapStore((state) => state.columns);\n return h(BaseColumnsLayer, { enabled, color, columns });\n}\n\nconst selectedColumnsStyle: Style = {\n version: 8,\n sources: {\n \"selected-columns\": buildGeoJSONSource(),\n \"selected-column-centroids\": buildGeoJSONSource(),\n },\n layers: [\n {\n id: \"selected-columns-fill\",\n type: \"fill\",\n source: \"selected-columns\",\n paint: {\n \"fill-color\": \"rgba(255, 0, 0, 0.1)\",\n },\n },\n {\n id: \"selected-column-centroids-line\",\n type: \"line\",\n source: \"selected-column-centroids\",\n paint: {\n \"line-color\": \"rgba(255, 0, 0, 0.8)\",\n \"line-width\": 2,\n \"line-dasharray\": [2, 2],\n },\n },\n {\n id: \"selected-column-centroids-points\",\n type: \"circle\",\n source: \"selected-column-centroids\",\n paint: {\n \"circle-radius\": 4,\n \"circle-color\": \"rgba(255, 0, 0, 0.8)\",\n },\n },\n ],\n};\n\nconst lineOfSectionStyle: Style = {\n version: 8,\n sources: {\n elevationMarker: buildGeoJSONSource(),\n crossSectionLine: buildGeoJSONSource(),\n crossSectionEndpoints: buildGeoJSONSource(),\n },\n layers: buildCrossSectionLayers(),\n};\n\nfunction SectionLine({ padding }: { padding: number }) {\n useOverlayStyle(() => lineOfSectionStyle, []);\n const focusedLine = useCorrelationMapStore((state) => state.focusedLine);\n const focusedColumns = useCorrelationMapStore(\n (state) => state.focusedColumns,\n );\n\n // Setup focused line (only drawn in line-of-section mode)\n useMapStyleOperator(\n (map) => {\n if (focusedLine == null) {\n // Clear any previously-drawn line (e.g. after switching to manual mode)\n setGeoJSON(map, \"crossSectionLine\", emptyFeatureCollection);\n setGeoJSON(map, \"crossSectionEndpoints\", emptyFeatureCollection);\n return;\n }\n const data: FeatureCollection = {\n type: \"FeatureCollection\",\n features: [\n {\n type: \"Feature\",\n geometry: focusedLine,\n properties: { id: \"focusedLine\" },\n },\n ],\n };\n\n setGeoJSON(map, \"crossSectionLine\", data);\n setGeoJSON(map, \"crossSectionEndpoints\", {\n type: \"FeatureCollection\",\n features: focusedLine.coordinates.map((coord) => ({\n type: \"Feature\",\n geometry: { type: \"Point\", coordinates: coord },\n properties: {},\n })),\n });\n },\n [focusedLine],\n );\n\n const bounds = useMemo(() => {\n // In line mode, frame the line; in manual mode, frame the selected columns\n const coords: number[][] =\n focusedLine != null && focusedLine.coordinates.length >= 2\n ? focusedLine.coordinates\n : focusedColumns.map((c) => c.properties.centroid.geometry.coordinates);\n if (coords.length < 2) return null;\n const bounds = new LngLatBounds();\n for (const coord of coords) {\n bounds.extend(coord as [number, number]);\n }\n return bounds;\n }, [focusedLine, focusedColumns]);\n\n useMapEaseTo({ bounds, padding, trackResize: true });\n\n return null;\n}\n\nconst emptyFeatureCollection: FeatureCollection = {\n type: \"FeatureCollection\",\n features: [],\n};\n"],"names":["bounds"],"mappings":"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"}
@@ -16,9 +16,12 @@ const distance__default = /* @__PURE__ */ _interopDefault(distance);
16
16
  const h__default = /* @__PURE__ */ _interopDefault(h);
17
17
  const CorrelationStoreContext = React.createContext(null);
18
18
  const computed = zustandComputed.createComputed((state) => {
19
+ const manual = state.manualColumns != null;
19
20
  return {
20
- // We compute the focused columns based on columns and focusedLine
21
- focusedColumns: buildCorrelationColumns(state.columns, state.focusedLine)
21
+ selectionMode: manual ? "manual" : "line",
22
+ // Focused columns are derived either from an explicit manual selection or
23
+ // from the columns intersecting the line of section.
24
+ focusedColumns: manual ? buildManualColumns(state.columns, state.manualColumns) : buildCorrelationColumns(state.columns, state.focusedLine)
22
25
  };
23
26
  });
24
27
  function ColumnCorrelationProvider({
@@ -27,6 +30,7 @@ function ColumnCorrelationProvider({
27
30
  projectID,
28
31
  inProcess,
29
32
  focusedLine,
33
+ manualColumns = null,
30
34
  onSelectColumns
31
35
  }) {
32
36
  const [store] = React.useState(() => {
@@ -34,10 +38,15 @@ function ColumnCorrelationProvider({
34
38
  computed((set, get) => {
35
39
  return {
36
40
  focusedLine,
41
+ manualColumns,
42
+ hoveredColumn: null,
43
+ zoomColumn: null,
44
+ zoomNonce: 0,
37
45
  projectID,
38
46
  columns: null,
39
47
  onClickMap(event, point) {
40
48
  const state = get();
49
+ if (state.manualColumns != null) return;
41
50
  const shiftKeyPressed = event.originalEvent.shiftKey;
42
51
  let existingCoords = state.focusedLine?.coordinates ?? [];
43
52
  if (existingCoords.length >= 2 && !shiftKeyPressed) {
@@ -49,6 +58,29 @@ function ColumnCorrelationProvider({
49
58
  coordinates: [...existingCoords, point.coordinates]
50
59
  }
51
60
  });
61
+ },
62
+ toggleColumn(colID) {
63
+ const state = get();
64
+ const current = state.manualColumns ?? state.focusedColumns.map((d) => d.properties.col_id);
65
+ const next = current.includes(colID) ? current.filter((d) => d !== colID) : [...current, colID];
66
+ set({ manualColumns: next, focusedLine: null });
67
+ },
68
+ removeColumn(colID) {
69
+ const state = get();
70
+ const current = state.manualColumns ?? state.focusedColumns.map((d) => d.properties.col_id);
71
+ set({
72
+ manualColumns: current.filter((d) => d !== colID),
73
+ focusedLine: null
74
+ });
75
+ },
76
+ setManualColumns(colIDs) {
77
+ set({ manualColumns: colIDs, focusedLine: null });
78
+ },
79
+ setHoveredColumn(colID) {
80
+ set({ hoveredColumn: colID });
81
+ },
82
+ zoomToColumn(colID) {
83
+ set({ zoomColumn: colID, zoomNonce: get().zoomNonce + 1 });
52
84
  }
53
85
  };
54
86
  })
@@ -74,6 +106,14 @@ function useCorrelationMapStore(selector) {
74
106
  }
75
107
  return zustand.useStore(storeApi, selector);
76
108
  }
109
+ function useColumnMapLink() {
110
+ const setHoveredColumn = useCorrelationMapStore((s) => s.setHoveredColumn);
111
+ const zoomToColumn = useCorrelationMapStore((s) => s.zoomToColumn);
112
+ return {
113
+ onColumnMouseOver: setHoveredColumn,
114
+ onColumnClick: zoomToColumn
115
+ };
116
+ }
77
117
  function buildCorrelationColumns(columns, line) {
78
118
  if (columns == null || line == null || line.coordinates.length < 2) {
79
119
  return [];
@@ -83,6 +123,26 @@ function buildCorrelationColumns(columns, line) {
83
123
  line
84
124
  );
85
125
  }
126
+ function buildManualColumns(columns, ids) {
127
+ if (columns == null || ids == null) return [];
128
+ const byID = new Map(columns.map((c) => [c.properties.col_id, c]));
129
+ const result = [];
130
+ ids.forEach((id, i) => {
131
+ const col = byID.get(id);
132
+ if (col == null) return;
133
+ const c = centroid.centroid(col.geometry);
134
+ result.push({
135
+ ...col,
136
+ properties: {
137
+ ...col.properties,
138
+ centroid: c,
139
+ nearestPointOnLine: c,
140
+ distanceAlongLine: i
141
+ }
142
+ });
143
+ });
144
+ return result;
145
+ }
86
146
  function computeIntersectingColumns(columns, line) {
87
147
  if (columns == null || line == null) {
88
148
  return [];
@@ -128,5 +188,6 @@ function sorted(data, accessor) {
128
188
  return data.sort((a, b) => accessor(a) - accessor(b));
129
189
  }
130
190
  exports.ColumnCorrelationProvider = ColumnCorrelationProvider;
191
+ exports.useColumnMapLink = useColumnMapLink;
131
192
  exports.useCorrelationMapStore = useCorrelationMapStore;
132
193
  //# sourceMappingURL=state.cjs.map