@macrostrat/column-views 3.6.0 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/dist/column-views.css +118 -89
- package/dist/column.cjs +1 -2
- package/dist/column.cjs.map +1 -1
- package/dist/column.js +1 -2
- package/dist/column.js.map +1 -1
- package/dist/column.module.sass.cjs +15 -15
- package/dist/column.module.sass.js +15 -15
- package/dist/correlation-chart/main.cjs +136 -34
- package/dist/correlation-chart/main.cjs.map +1 -1
- package/dist/correlation-chart/main.d.ts +33 -2
- package/dist/correlation-chart/main.js +118 -15
- package/dist/correlation-chart/main.js.map +1 -1
- package/dist/correlation-chart/main.module.sass.cjs +23 -20
- package/dist/correlation-chart/main.module.sass.cjs.map +1 -1
- package/dist/correlation-chart/main.module.sass.js +23 -20
- package/dist/correlation-chart/main.module.sass.js.map +1 -1
- package/dist/correlation-chart/prepare-data.cjs +0 -1
- package/dist/correlation-chart/prepare-data.cjs.map +1 -1
- package/dist/correlation-chart/prepare-data.js +0 -1
- package/dist/correlation-chart/prepare-data.js.map +1 -1
- package/dist/data-provider/store.cjs +10 -2
- package/dist/data-provider/store.cjs.map +1 -1
- package/dist/data-provider/store.d.ts +2 -2
- package/dist/data-provider/store.js +10 -2
- package/dist/data-provider/store.js.map +1 -1
- package/dist/data-provider/unit-selection.cjs +4 -1
- package/dist/data-provider/unit-selection.cjs.map +1 -1
- package/dist/data-provider/unit-selection.d.ts +1 -1
- package/dist/data-provider/unit-selection.js +4 -1
- package/dist/data-provider/unit-selection.js.map +1 -1
- package/dist/index.cjs +14 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3 -2
- package/dist/maps/column-correlation/core.cjs +127 -6
- package/dist/maps/column-correlation/core.cjs.map +1 -1
- package/dist/maps/column-correlation/core.js +128 -7
- package/dist/maps/column-correlation/core.js.map +1 -1
- package/dist/maps/column-correlation/state.cjs +63 -2
- package/dist/maps/column-correlation/state.cjs.map +1 -1
- package/dist/maps/column-correlation/state.d.ts +31 -1
- package/dist/maps/column-correlation/state.js +63 -2
- package/dist/maps/column-correlation/state.js.map +1 -1
- package/dist/maps/layers/columns.cjs +5 -1
- package/dist/maps/layers/columns.cjs.map +1 -1
- package/dist/maps/layers/columns.js +5 -1
- package/dist/maps/layers/columns.js.map +1 -1
- package/dist/prepare-units/helpers.cjs +4 -2
- package/dist/prepare-units/helpers.cjs.map +1 -1
- package/dist/prepare-units/helpers.js +4 -2
- package/dist/prepare-units/helpers.js.map +1 -1
- package/dist/section.cjs +8 -2
- package/dist/section.cjs.map +1 -1
- package/dist/section.d.ts +2 -0
- package/dist/section.js +8 -2
- package/dist/section.js.map +1 -1
- package/dist/unit-details/age-range.cjs +2 -1
- package/dist/unit-details/age-range.cjs.map +1 -1
- package/dist/unit-details/age-range.js +2 -1
- package/dist/unit-details/age-range.js.map +1 -1
- package/dist/unit-details/index.d.ts +1 -1
- package/dist/unit-details/panel.cjs +2 -3
- package/dist/unit-details/panel.cjs.map +1 -1
- package/dist/unit-details/panel.js +1 -2
- package/dist/unit-details/panel.js.map +1 -1
- package/dist/unit-details/panel.module.sass.cjs +18 -22
- package/dist/unit-details/panel.module.sass.cjs.map +1 -1
- package/dist/unit-details/panel.module.sass.js +18 -22
- package/dist/unit-details/panel.module.sass.js.map +1 -1
- package/dist/unit-details/strat-names.cjs +1 -2
- package/dist/unit-details/strat-names.cjs.map +1 -1
- package/dist/unit-details/strat-names.js +1 -2
- package/dist/unit-details/strat-names.js.map +1 -1
- package/dist/units/boxes.cjs +2 -1
- package/dist/units/boxes.cjs.map +1 -1
- package/dist/units/boxes.js +2 -1
- package/dist/units/boxes.js.map +1 -1
- package/package.json +7 -7
- package/src/column.module.sass +6 -0
- package/src/correlation-chart/main.module.sass +46 -2
- package/src/correlation-chart/main.ts +180 -9
- package/src/correlation-chart/prepare-data.ts +0 -1
- package/src/data-provider/store.ts +11 -1
- package/src/data-provider/unit-selection.ts +13 -2
- package/src/maps/column-correlation/column-correlation-map.stories.ts +12 -0
- package/src/maps/column-correlation/core.ts +155 -9
- package/src/maps/column-correlation/state.ts +110 -2
- package/src/maps/layers/columns.ts +7 -1
- package/src/prepare-units/helpers.ts +4 -3
- package/src/section.ts +9 -1
- package/src/unit-details/age-range.ts +3 -1
- package/src/unit-details/index.ts +7 -1
- package/src/unit-details/panel.module.sass +0 -8
- package/src/unit-details/panel.ts +6 -6
- package/src/unit-details/strat-names.ts +1 -1
- package/src/units/boxes.ts +4 -2
- package/dist/correlation-chart/stories/utils.d.ts +0 -6
- package/dist/unit-details/identifiers.cjs +0 -56
- package/dist/unit-details/identifiers.cjs.map +0 -1
- package/dist/unit-details/identifiers.d.ts +0 -31
- package/dist/unit-details/identifiers.js +0 -54
- package/dist/unit-details/identifiers.js.map +0 -1
- package/src/correlation-chart/stories/correlation-chart.stories.ts +0 -228
- package/src/correlation-chart/stories/namibia-correlation.stories.ts +0 -151
- package/src/correlation-chart/stories/stories.module.sass +0 -18
- package/src/correlation-chart/stories/utils.ts +0 -24
- package/src/unit-details/identifiers.ts +0 -67
|
@@ -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
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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":";;;;;;;;;AAwBO,SAAS,qBAAqB,OAA4B;AAC/D,QAAM,EAAE,UAAU,IAAI,UAAU,aAAa,WAAW,GAAG,SAAS;AAEpE,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,GAAG;AAAA,MACH,SAAS;AAAA,MACT,YAAY;AAAA,IAAA;AAAA,IAEd;AAAA,MACE,EAAE,cAAc,EAAE,OAAO,aAAa;AAAA,MACtC,EAAE,oBAAoB;AAAA,MACtB,EAAE,eAAe;AAAA,MACjB,EAAE,wBAAwB;AAAA,MAC1B,EAAE,sBAAsB;AAAA,MACxB,EAAE,cAAc,EAAE,SAAS;AAAA,MAC3B,EAAE,aAAa,EAAE,SAAS;AAAA,MAC1B;AAAA,IAAA;AAAA,EACF;AAEJ;AAEA,SAAS,aAAa,EAAE,WAAgC;AAEtD,QAAM,aAAa,uBAAuB,CAAC,UAAU,MAAM,UAAU;AACrE,QAAM,YAAY,uBAAuB,CAAC,UAAU,MAAM,SAAS;AACnE,QAAM,UAAU,uBAAuB,CAAC,UAAU,MAAM,OAAO;AAE/D,QAAM,SAAS,QAAQ,MAAM;AAC3B,QAAI,cAAc,QAAQ,WAAW,KAAM,QAAO;AAClD,UAAM,MAAM,QAAQ,KAAK,CAAC,MAAM,EAAE,YAAY,WAAW,UAAU;AACnE,QAAI,KAAK,YAAY,KAAM,QAAO;AAClC,UAAM,IAAI,IAAI,aAAA;AACd,sBAAkB,IAAI,UAAU,CAAC,UAAU,EAAE,OAAO,KAAyB,CAAC;AAC9E,WAAO,EAAE,YAAY,OAAO;AAAA,EAE9B,GAAG,CAAC,YAAY,WAAW,OAAO,CAAC;AAEnC,eAAa,EAAE,QAAQ,SAAS,UAAU,GAAG;AAC7C,SAAO;AACT;AAEA,SAAS,kBAAkB,UAAe,IAA+B;AACvE,QAAM,OAAO,CAAC,WAAgB;AAC5B,QAAI,OAAO,OAAO,CAAC,MAAM,UAAU;AACjC,SAAG,MAAM;AAAA,IACX,OAAO;AACL,iBAAW,KAAK,OAAQ,MAAK,CAAC;AAAA,IAChC;AAAA,EACF;AACA,MAAI,UAAU,eAAe,KAAM,MAAK,SAAS,WAAW;AAC9D;AAEA,MAAM,eAAe,CAAC,gBAAgB,gBAAgB;AAEtD,SAAS,kBAAkB;AACzB,QAAM,aAAa,uBAAuB,CAAC,UAAU,MAAM,UAAU;AAErE;AAAA,IACE,CAAC,MAAM;AACL,iBAAW,GAAG,EAAE,MAAM,SAAS,aAAa,EAAE,OAAO,QAAA,GAAW;AAAA,IAClE;AAAA,IACA,CAAC,UAAU;AAAA,EAAA;AAGb,SAAO;AACT;AAEA,SAAS,2BAA2B;AAElC,QAAM,eAAe,uBAAuB,CAAC,UAAU,MAAM,YAAY;AACzE,QAAM,mBAAmB;AAAA,IACvB,CAAC,UAAU,MAAM;AAAA,EAAA;AAEnB,QAAM,WAAW;AAAA,IACf,CAAC,UAAU,MAAM,kBAAkB;AAAA,EAAA;AAErC,QAAM,aAAa,OAA+B,IAAI;AAGtD;AAAA,IACE,CAAC,QAAQ;AACP,UAAI,CAAC,SAAU;AACf,YAAM,UAAU,CAAC,MAAM;AACrB,cAAM,QAAQ,EAAE,WAAW,CAAC,GAAG,YAAY;AAC3C,YAAI,SAAS,KAAM,cAAa,KAAK;AAAA,MACvC;AACA,UAAI,GAAG,SAAS,cAAc,OAAO;AACrC,aAAO,MAAM,IAAI,IAAI,SAAS,cAAc,OAAO;AAAA,IACrD;AAAA,IACA,CAAC,UAAU,YAAY;AAAA,EAAA;AAIzB;AAAA,IACE,CAAC,QAAQ;AACP,YAAM,aAAa,MAAM;AACvB,YAAI,WAAW,WAAW,MAAM;AAC9B,cAAI;AAAA,YACF,EAAE,QAAQ,WAAW,IAAI,WAAW,QAAA;AAAA,YACpC,EAAE,OAAO,MAAA;AAAA,UAAM;AAEjB,qBAAW,UAAU;AAAA,QACvB;AAAA,MACF;AACA,YAAM,SAAS,CAAC,MAAM;AACpB,cAAM,IAAI,EAAE,WAAW,CAAC;AACxB,YAAI,KAAK,KAAM;AACf,YAAI,WAAW,YAAY,EAAE,IAAI;AAC/B,qBAAA;AACA,cAAI,gBAAgB,EAAE,QAAQ,WAAW,IAAI,EAAE,GAAA,GAAM,EAAE,OAAO,KAAA,CAAM;AACpE,qBAAW,UAAU,EAAE;AAAA,QACzB;AACA,yBAAiB,EAAE,YAAY,UAAU,IAAI;AAC7C,YAAI,UAAA,EAAY,MAAM,SAAS;AAAA,MACjC;AACA,YAAM,UAAU,MAAM;AACpB,mBAAA;AACA,yBAAiB,IAAI;AACrB,YAAI,UAAA,EAAY,MAAM,SAAS;AAAA,MACjC;AACA,UAAI,GAAG,aAAa,cAAc,MAAM;AACxC,UAAI,GAAG,cAAc,cAAc,OAAO;AAC1C,aAAO,MAAM;AACX,YAAI,IAAI,aAAa,cAAc,MAAM;AACzC,YAAI,IAAI,cAAc,cAAc,OAAO;AAC3C,mBAAA;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,gBAAgB;AAAA,EAAA;AAGnB,SAAO;AACT;AAEA,SAAS,yBAAyB;AAGhC,QAAM,gBAAgB,uBAAuB,CAAC,UAAU,MAAM,aAAa;AAC3E,QAAM,UAAU,uBAAuB,CAAC,UAAU,MAAM,OAAO;AAC/D,QAAM,aAAa,OAA+B,IAAI;AAEtD;AAAA,IACE,CAAC,QAAQ;AACP,UAAI,WAAW,WAAW,MAAM;AAC9B,YAAI;AAAA,UACF,EAAE,QAAQ,WAAW,IAAI,WAAW,QAAA;AAAA,UACpC,EAAE,aAAa,MAAA;AAAA,QAAM;AAEvB,mBAAW,UAAU;AAAA,MACvB;AACA,UAAI,iBAAiB,QAAQ,WAAW,KAAM;AAC9C,YAAM,MAAM,QAAQ,KAAK,CAAC,MAAM,EAAE,YAAY,WAAW,aAAa;AACtE,YAAM,KAAM,KAAa,MAAM,KAAK,YAAY;AAChD,UAAI,MAAM,KAAM;AAChB,UAAI,gBAAgB,EAAE,QAAQ,WAAW,MAAM,EAAE,aAAa,MAAM;AACpE,iBAAW,UAAU;AAAA,IACvB;AAAA,IACA,CAAC,eAAe,OAAO;AAAA,EAAA;AAGzB,SAAO;AACT;AAEA,SAAS,uBAAuB;AAC9B,kBAAgB,MAAM,sBAAsB,EAAE;AAE9C,QAAM,iBAAiB;AAAA,IACrB,CAAC,UAAU,MAAM;AAAA,EAAA;AAGnB;AAAA,IACE,CAAC,QAAQ;AACP,UAAI,WAAW;AAEf,YAAM,OAA0B;AAAA,QAC9B,MAAM;AAAA,QACN;AAAA,MAAA;AAGF,YAAM,qBAA8B;AAAA,QAClC,MAAM;AAAA,QACN,UAAU;AAAA,UACR,MAAM;AAAA,UACN,aAAa,SAAS;AAAA,YACpB,CAAC,QAAQ,IAAI,WAAW,SAAS,SAAS;AAAA,UAAA;AAAA,QAC5C;AAAA,QAEF,YAAY,CAAA;AAAA,MAAC;AAGf,iBAAW,KAAK,oBAAoB,IAAI;AACxC,iBAAW,KAAK,6BAA6B;AAAA,QAC3C,MAAM;AAAA,QACN,UAAU,CAAC,kBAAkB;AAAA,MAAA,CAC9B;AAAA,IACH;AAAA,IACA,CAAC,cAAc;AAAA,EAAA;AAEjB,SAAO;AACT;AAEA,SAAS,aAAa,EAAE,UAAU,MAAM,SAAS;AAC/C,QAAM,UAAU,uBAAuB,CAAC,UAAU,MAAM,OAAO;AAC/D,SAAO,EAAE,kBAAkB,EAAE,SAAS,OAAO,SAAS;AACxD;AAEA,MAAM,uBAA8B;AAAA,EAClC,SAAS;AAAA,EACT,SAAS;AAAA,IACP,oBAAoB,mBAAA;AAAA,IACpB,6BAA6B,mBAAA;AAAA,EAAmB;AAAA,EAElD,QAAQ;AAAA,IACN;AAAA,MACE,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,QACL,cAAc;AAAA,MAAA;AAAA,IAChB;AAAA,IAEF;AAAA,MACE,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,QACL,cAAc;AAAA,QACd,cAAc;AAAA,QACd,kBAAkB,CAAC,GAAG,CAAC;AAAA,MAAA;AAAA,IACzB;AAAA,IAEF;AAAA,MACE,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,QACL,iBAAiB;AAAA,QACjB,gBAAgB;AAAA,MAAA;AAAA,IAClB;AAAA,EACF;AAEJ;AAEA,MAAM,qBAA4B;AAAA,EAChC,SAAS;AAAA,EACT,SAAS;AAAA,IACP,iBAAiB,mBAAA;AAAA,IACjB,kBAAkB,mBAAA;AAAA,IAClB,uBAAuB,mBAAA;AAAA,EAAmB;AAAA,EAE5C,QAAQ,wBAAA;AACV;AAEA,SAAS,YAAY,EAAE,WAAgC;AACrD,kBAAgB,MAAM,oBAAoB,EAAE;AAC5C,QAAM,cAAc,uBAAuB,CAAC,UAAU,MAAM,WAAW;AACvE,QAAM,iBAAiB;AAAA,IACrB,CAAC,UAAU,MAAM;AAAA,EAAA;AAInB;AAAA,IACE,CAAC,QAAQ;AACP,UAAI,eAAe,MAAM;AAEvB,mBAAW,KAAK,oBAAoB,sBAAsB;AAC1D,mBAAW,KAAK,yBAAyB,sBAAsB;AAC/D;AAAA,MACF;AACA,YAAM,OAA0B;AAAA,QAC9B,MAAM;AAAA,QACN,UAAU;AAAA,UACR;AAAA,YACE,MAAM;AAAA,YACN,UAAU;AAAA,YACV,YAAY,EAAE,IAAI,cAAA;AAAA,UAAc;AAAA,QAClC;AAAA,MACF;AAGF,iBAAW,KAAK,oBAAoB,IAAI;AACxC,iBAAW,KAAK,yBAAyB;AAAA,QACvC,MAAM;AAAA,QACN,UAAU,YAAY,YAAY,IAAI,CAAC,WAAW;AAAA,UAChD,MAAM;AAAA,UACN,UAAU,EAAE,MAAM,SAAS,aAAa,MAAA;AAAA,UACxC,YAAY,CAAA;AAAA,QAAC,EACb;AAAA,MAAA,CACH;AAAA,IACH;AAAA,IACA,CAAC,WAAW;AAAA,EAAA;AAGd,QAAM,SAAS,QAAQ,MAAM;AAE3B,UAAM,SACJ,eAAe,QAAQ,YAAY,YAAY,UAAU,IACrD,YAAY,cACZ,eAAe,IAAI,CAAC,MAAM,EAAE,WAAW,SAAS,SAAS,WAAW;AAC1E,QAAI,OAAO,SAAS,EAAG,QAAO;AAC9B,UAAMA,UAAS,IAAI,aAAA;AACnB,eAAW,SAAS,QAAQ;AAC1BA,cAAO,OAAO,KAAyB;AAAA,IACzC;AACA,WAAOA;AAAAA,EACT,GAAG,CAAC,aAAa,cAAc,CAAC;AAEhC,eAAa,EAAE,QAAQ,SAAS,aAAa,MAAM;AAEnD,SAAO;AACT;AAEA,MAAM,yBAA4C;AAAA,EAChD,MAAM;AAAA,EACN,UAAU,CAAA;AACZ;"}
|
|
@@ -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
|
-
|
|
21
|
-
|
|
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
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.cjs","sources":["../../../src/maps/column-correlation/state.ts"],"sourcesContent":["import { LineString, Point } from \"geojson\";\nimport { create, StoreApi, useStore } from \"zustand\";\nimport type { ColumnGeoJSONRecord } from \"@macrostrat/api-types\";\n// Turf intersection\nimport { lineIntersect } from \"@turf/line-intersect\";\nimport distance from \"@turf/distance\";\nimport { nearestPointOnLine } from \"@turf/nearest-point-on-line\";\nimport { centroid } from \"@turf/centroid\";\nimport {\n createContext,\n useState,\n useContext,\n ReactNode,\n useEffect,\n} from \"react\";\nimport h from \"@macrostrat/hyper\";\nimport { createComputed } from \"zustand-computed\";\nimport { useMacrostratColumns } from \"@macrostrat/data-provider\";\nimport { buffer } from \"@turf/buffer\";\nimport { booleanPointInPolygon } from \"@turf/boolean-point-in-polygon\";\n\nexport interface CorrelationMapInput {\n columns: ColumnGeoJSONRecord[];\n focusedLine: LineString | null;\n}\n\nexport interface CorrelationMapStore extends CorrelationMapInput {\n onClickMap: (event: mapboxgl.MapMouseEvent, point: Point) => void;\n projectID?: number;\n}\n\nexport interface CorrelationProviderProps extends CorrelationMapInput {\n projectID?: number;\n inProcess?: boolean;\n columns: ColumnGeoJSONRecord[] | null;\n children: ReactNode;\n onSelectColumns?: (\n columns: FocusedColumnGeoJSONRecord[],\n line: LineString | null,\n ) => void;\n}\n\nconst CorrelationStoreContext =\n createContext<StoreApi<CorrelationMapStore> | null>(null);\n\ntype ComputedStore = {\n focusedColumns: FocusedColumnGeoJSONRecord[];\n};\n\n/** A computed store that will automatically update when the state changes */\nconst computed = createComputed((state: CorrelationMapStore): ComputedStore => {\n return {\n // We compute the focused columns based on columns and focusedLine\n focusedColumns: buildCorrelationColumns(state.columns, state.focusedLine),\n };\n}) as any;\n\nexport function ColumnCorrelationProvider({\n children,\n columns,\n projectID,\n inProcess,\n focusedLine,\n onSelectColumns,\n}: CorrelationProviderProps) {\n const [store] = useState(() => {\n return create<CorrelationMapStore & ComputedStore>(\n computed((set, get): CorrelationMapStore => {\n return {\n focusedLine,\n projectID,\n columns: null,\n onClickMap(event: mapboxgl.MapMouseEvent, point: Point) {\n const state = get();\n // Check if shift key is pressed\n const shiftKeyPressed = event.originalEvent.shiftKey;\n let existingCoords = state.focusedLine?.coordinates ?? [];\n\n if (existingCoords.length >= 2 && !shiftKeyPressed) {\n // Reset the line to zero length\n existingCoords = [];\n }\n set({\n focusedLine: {\n type: \"LineString\",\n coordinates: [...existingCoords, point.coordinates],\n },\n });\n },\n };\n }),\n );\n });\n\n // Set up the store\n /** TODO: move the fetching of all columns to within the map */\n const _columns = useMacrostratColumns(projectID, inProcess);\n useEffect(() => {\n if (_columns != null) {\n store.setState({ columns: _columns });\n }\n }, [_columns]);\n\n // Kind of an awkward way to do this but we need to allow the selector to run\n const focusedColumns = useStore(store, (state) => state.focusedColumns);\n const _focusedLine = useStore(store, (state) => state.focusedLine);\n\n useEffect(() => {\n onSelectColumns?.(focusedColumns, _focusedLine);\n }, [focusedColumns, _focusedLine]);\n\n return h(CorrelationStoreContext.Provider, { value: store }, children);\n}\n\nexport function useCorrelationMapStore(\n selector: (state: CorrelationMapStore & ComputedStore) => any,\n) {\n const storeApi = useContext(CorrelationStoreContext);\n if (storeApi == null) {\n throw new Error(\"Missing CorrelationMapProvider\");\n }\n return useStore(storeApi, selector);\n}\n\nfunction buildCorrelationColumns(\n columns: ColumnGeoJSONRecord[],\n line: LineString,\n): FocusedColumnGeoJSONRecord[] {\n if (columns == null || line == null || line.coordinates.length < 2) {\n return [];\n }\n return orderColumnsByDistance(\n computeIntersectingColumns(columns, line),\n line,\n );\n}\n\nfunction computeIntersectingColumns(\n columns: ColumnGeoJSONRecord[],\n line: LineString,\n): ColumnGeoJSONRecord[] {\n if (columns == null || line == null) {\n return [];\n }\n\n /** eODP-focused process. Find buffers around line and then find columns intersecting that buffer */\n const bufferedLine = buffer(line, 1, { units: \"degrees\" });\n\n const nearbyPoints = columns.filter((col) => {\n if (col.geometry?.type != \"Point\") {\n return false;\n }\n return booleanPointInPolygon(col.geometry, bufferedLine);\n });\n\n const intersectingPolygons = columns.filter((col) => {\n const poly = col.geometry;\n\n // Some in-process datasets seem to have null geometries\n if (poly == null) return false;\n if (poly.type != \"Polygon\" && poly.type != \"MultiPolygon\") return false;\n\n const intersection = lineIntersect(line, poly);\n return intersection.features.length > 0;\n });\n\n return [...intersectingPolygons, ...nearbyPoints];\n}\n\ninterface FocusedColumnGeoJSONRecord extends ColumnGeoJSONRecord {\n properties: {\n centroid: Point;\n nearestPointOnLine: Point;\n distanceAlongLine: number;\n } & ColumnGeoJSONRecord[\"properties\"];\n}\n\nfunction orderColumnsByDistance(\n columns: ColumnGeoJSONRecord[],\n line: LineString,\n): FocusedColumnGeoJSONRecord[] {\n const centroids = columns.map((col) => centroid(col.geometry));\n const projectedPoints = centroids.map((point) =>\n nearestPointOnLine(line, point),\n );\n const distances = projectedPoints.map((point) =>\n distance(point.geometry.coordinates, line.coordinates[0]),\n );\n\n let newColumns = columns.map((col, i) => {\n return {\n ...col,\n properties: {\n ...col.properties,\n centroid: centroids[i],\n nearestPointOnLine: projectedPoints[i],\n distanceAlongLine: distances[i],\n },\n };\n });\n\n return sorted(newColumns, (d) => d.properties.distanceAlongLine);\n}\n\nfunction sorted(data, accessor: (d) => number) {\n return data.sort((a, b) => accessor(a) - accessor(b));\n}\n"],"names":["createContext","createComputed","useState","create","useMacrostratColumns","useEffect","useStore","h","useContext","buffer","booleanPointInPolygon","lineIntersect","centroid","nearestPointOnLine","distance"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"state.cjs","sources":["../../../src/maps/column-correlation/state.ts"],"sourcesContent":["import { LineString, Point } from \"geojson\";\nimport { create, StoreApi, useStore } from \"zustand\";\nimport type { ColumnGeoJSONRecord } from \"@macrostrat/api-types\";\n// Turf intersection\nimport { lineIntersect } from \"@turf/line-intersect\";\nimport distance from \"@turf/distance\";\nimport { nearestPointOnLine } from \"@turf/nearest-point-on-line\";\nimport { centroid } from \"@turf/centroid\";\nimport {\n createContext,\n useState,\n useContext,\n ReactNode,\n useEffect,\n} from \"react\";\nimport h from \"@macrostrat/hyper\";\nimport { createComputed } from \"zustand-computed\";\nimport { useMacrostratColumns } from \"@macrostrat/data-provider\";\nimport { buffer } from \"@turf/buffer\";\nimport { booleanPointInPolygon } from \"@turf/boolean-point-in-polygon\";\n\nexport type CorrelationSelectionMode = \"line\" | \"manual\";\n\nexport interface CorrelationMapInput {\n columns: ColumnGeoJSONRecord[];\n focusedLine: LineString | null;\n /** When set, columns are selected manually (by clicking) in this order,\n * rather than derived from a line of section. */\n manualColumns?: number[] | null;\n}\n\nexport interface CorrelationMapStore extends CorrelationMapInput {\n onClickMap: (event: mapboxgl.MapMouseEvent, point: Point) => void;\n /** Toggle a column in/out of the manual selection (used in \"manual\" mode) */\n toggleColumn: (colID: number) => void;\n /** Remove a column from the selection. In \"line\" mode this converts the\n * current selection to a manual one (a line can't represent an arbitrary\n * subset), so removing a column switches modes. */\n removeColumn: (colID: number) => void;\n /** Replace the manual column selection with an explicit ordered list (used\n * e.g. for drag-and-drop reordering). Switches to manual mode. */\n setManualColumns: (colIDs: number[]) => void;\n setHoveredColumn: (colID: number | null) => void;\n hoveredColumn: number | null;\n /** Request the map to frame a particular column (e.g. on header click). */\n zoomToColumn: (colID: number | null) => void;\n zoomColumn: number | null;\n /** Incremented on each zoom request so repeat clicks re-frame the column. */\n zoomNonce: number;\n projectID?: number;\n}\n\nexport interface CorrelationProviderProps extends CorrelationMapInput {\n projectID?: number;\n inProcess?: boolean;\n columns: ColumnGeoJSONRecord[] | null;\n children: ReactNode;\n onSelectColumns?: (\n columns: FocusedColumnGeoJSONRecord[],\n line: LineString | null,\n ) => void;\n}\n\nconst CorrelationStoreContext =\n createContext<StoreApi<CorrelationMapStore> | null>(null);\n\ntype ComputedStore = {\n focusedColumns: FocusedColumnGeoJSONRecord[];\n selectionMode: CorrelationSelectionMode;\n};\n\n/** A computed store that will automatically update when the state changes */\nconst computed = createComputed((state: CorrelationMapStore): ComputedStore => {\n const manual = state.manualColumns != null;\n return {\n selectionMode: manual ? \"manual\" : \"line\",\n // Focused columns are derived either from an explicit manual selection or\n // from the columns intersecting the line of section.\n focusedColumns: manual\n ? buildManualColumns(state.columns, state.manualColumns)\n : buildCorrelationColumns(state.columns, state.focusedLine),\n };\n}) as any;\n\nexport function ColumnCorrelationProvider({\n children,\n columns,\n projectID,\n inProcess,\n focusedLine,\n manualColumns = null,\n onSelectColumns,\n}: CorrelationProviderProps) {\n const [store] = useState(() => {\n return create<CorrelationMapStore & ComputedStore>(\n computed((set, get): CorrelationMapStore => {\n return {\n focusedLine,\n manualColumns,\n hoveredColumn: null,\n zoomColumn: null,\n zoomNonce: 0,\n projectID,\n columns: null,\n onClickMap(event: mapboxgl.MapMouseEvent, point: Point) {\n const state = get();\n // In manual-selection mode the map click is handled per-column\n if (state.manualColumns != null) return;\n // Check if shift key is pressed\n const shiftKeyPressed = event.originalEvent.shiftKey;\n let existingCoords = state.focusedLine?.coordinates ?? [];\n\n if (existingCoords.length >= 2 && !shiftKeyPressed) {\n // Reset the line to zero length\n existingCoords = [];\n }\n set({\n focusedLine: {\n type: \"LineString\",\n coordinates: [...existingCoords, point.coordinates],\n },\n });\n },\n toggleColumn(colID: number) {\n const state = get();\n const current =\n state.manualColumns ??\n state.focusedColumns.map((d) => d.properties.col_id);\n const next = current.includes(colID)\n ? current.filter((d) => d !== colID)\n : [...current, colID];\n set({ manualColumns: next, focusedLine: null });\n },\n removeColumn(colID: number) {\n const state = get();\n // Seed from the current selection (line-derived or manual)\n const current =\n state.manualColumns ??\n state.focusedColumns.map((d) => d.properties.col_id);\n set({\n manualColumns: current.filter((d) => d !== colID),\n focusedLine: null,\n });\n },\n setManualColumns(colIDs: number[]) {\n set({ manualColumns: colIDs, focusedLine: null });\n },\n setHoveredColumn(colID: number | null) {\n set({ hoveredColumn: colID });\n },\n zoomToColumn(colID: number | null) {\n set({ zoomColumn: colID, zoomNonce: get().zoomNonce + 1 });\n },\n };\n }),\n );\n });\n\n // Set up the store\n /** TODO: move the fetching of all columns to within the map */\n const _columns = useMacrostratColumns(projectID, inProcess);\n useEffect(() => {\n if (_columns != null) {\n store.setState({ columns: _columns });\n }\n }, [_columns]);\n\n // Kind of an awkward way to do this but we need to allow the selector to run\n const focusedColumns = useStore(store, (state) => state.focusedColumns);\n const _focusedLine = useStore(store, (state) => state.focusedLine);\n\n useEffect(() => {\n onSelectColumns?.(focusedColumns, _focusedLine);\n }, [focusedColumns, _focusedLine]);\n\n return h(CorrelationStoreContext.Provider, { value: store }, children);\n}\n\nexport function useCorrelationMapStore(\n selector: (state: CorrelationMapStore & ComputedStore) => any,\n) {\n const storeApi = useContext(CorrelationStoreContext);\n if (storeApi == null) {\n throw new Error(\"Missing CorrelationMapProvider\");\n }\n return useStore(storeApi, selector);\n}\n\nexport interface ColumnMapLink {\n onColumnMouseOver: (colID: number | null) => void;\n onColumnClick: (colID: number) => void;\n}\n\n/** Wire a correlation chart's column-level events to the correlation map:\n * hovering a column highlights it on the map, and clicking a column header\n * frames it. Spread the result into `CorrelationChart`. Must be used within a\n * `ColumnCorrelationProvider`. */\nexport function useColumnMapLink(): ColumnMapLink {\n const setHoveredColumn = useCorrelationMapStore((s) => s.setHoveredColumn);\n const zoomToColumn = useCorrelationMapStore((s) => s.zoomToColumn);\n return {\n onColumnMouseOver: setHoveredColumn,\n onColumnClick: zoomToColumn,\n };\n}\n\nfunction buildCorrelationColumns(\n columns: ColumnGeoJSONRecord[],\n line: LineString,\n): FocusedColumnGeoJSONRecord[] {\n if (columns == null || line == null || line.coordinates.length < 2) {\n return [];\n }\n return orderColumnsByDistance(\n computeIntersectingColumns(columns, line),\n line,\n );\n}\n\nfunction buildManualColumns(\n columns: ColumnGeoJSONRecord[],\n ids: number[],\n): FocusedColumnGeoJSONRecord[] {\n /** Build focused columns from an explicit, ordered list of column IDs. The\n * ordering line simply connects them in the order they were selected. */\n if (columns == null || ids == null) return [];\n const byID = new Map(columns.map((c) => [c.properties.col_id, c]));\n const result: FocusedColumnGeoJSONRecord[] = [];\n ids.forEach((id, i) => {\n const col = byID.get(id);\n if (col == null) return;\n const c = centroid(col.geometry);\n result.push({\n ...col,\n properties: {\n ...col.properties,\n centroid: c,\n nearestPointOnLine: c,\n distanceAlongLine: i,\n },\n });\n });\n return result;\n}\n\nfunction computeIntersectingColumns(\n columns: ColumnGeoJSONRecord[],\n line: LineString,\n): ColumnGeoJSONRecord[] {\n if (columns == null || line == null) {\n return [];\n }\n\n /** eODP-focused process. Find buffers around line and then find columns intersecting that buffer */\n const bufferedLine = buffer(line, 1, { units: \"degrees\" });\n\n const nearbyPoints = columns.filter((col) => {\n if (col.geometry?.type != \"Point\") {\n return false;\n }\n return booleanPointInPolygon(col.geometry, bufferedLine);\n });\n\n const intersectingPolygons = columns.filter((col) => {\n const poly = col.geometry;\n\n // Some in-process datasets seem to have null geometries\n if (poly == null) return false;\n if (poly.type != \"Polygon\" && poly.type != \"MultiPolygon\") return false;\n\n const intersection = lineIntersect(line, poly);\n return intersection.features.length > 0;\n });\n\n return [...intersectingPolygons, ...nearbyPoints];\n}\n\ninterface FocusedColumnGeoJSONRecord extends ColumnGeoJSONRecord {\n properties: {\n centroid: Point;\n nearestPointOnLine: Point;\n distanceAlongLine: number;\n } & ColumnGeoJSONRecord[\"properties\"];\n}\n\nfunction orderColumnsByDistance(\n columns: ColumnGeoJSONRecord[],\n line: LineString,\n): FocusedColumnGeoJSONRecord[] {\n const centroids = columns.map((col) => centroid(col.geometry));\n const projectedPoints = centroids.map((point) =>\n nearestPointOnLine(line, point),\n );\n const distances = projectedPoints.map((point) =>\n distance(point.geometry.coordinates, line.coordinates[0]),\n );\n\n let newColumns = columns.map((col, i) => {\n return {\n ...col,\n properties: {\n ...col.properties,\n centroid: centroids[i],\n nearestPointOnLine: projectedPoints[i],\n distanceAlongLine: distances[i],\n },\n };\n });\n\n return sorted(newColumns, (d) => d.properties.distanceAlongLine);\n}\n\nfunction sorted(data, accessor: (d) => number) {\n return data.sort((a, b) => accessor(a) - accessor(b));\n}\n"],"names":["createContext","createComputed","useState","create","useMacrostratColumns","useEffect","useStore","h","useContext","centroid","buffer","booleanPointInPolygon","lineIntersect","nearestPointOnLine","distance"],"mappings":"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"}
|
|
@@ -2,12 +2,32 @@ import { LineString, Point } from 'geojson';
|
|
|
2
2
|
import { StoreApi } from 'zustand';
|
|
3
3
|
import { ColumnGeoJSONRecord } from '@macrostrat/api-types';
|
|
4
4
|
import { ReactNode } from 'react';
|
|
5
|
+
export type CorrelationSelectionMode = "line" | "manual";
|
|
5
6
|
export interface CorrelationMapInput {
|
|
6
7
|
columns: ColumnGeoJSONRecord[];
|
|
7
8
|
focusedLine: LineString | null;
|
|
9
|
+
/** When set, columns are selected manually (by clicking) in this order,
|
|
10
|
+
* rather than derived from a line of section. */
|
|
11
|
+
manualColumns?: number[] | null;
|
|
8
12
|
}
|
|
9
13
|
export interface CorrelationMapStore extends CorrelationMapInput {
|
|
10
14
|
onClickMap: (event: mapboxgl.MapMouseEvent, point: Point) => void;
|
|
15
|
+
/** Toggle a column in/out of the manual selection (used in "manual" mode) */
|
|
16
|
+
toggleColumn: (colID: number) => void;
|
|
17
|
+
/** Remove a column from the selection. In "line" mode this converts the
|
|
18
|
+
* current selection to a manual one (a line can't represent an arbitrary
|
|
19
|
+
* subset), so removing a column switches modes. */
|
|
20
|
+
removeColumn: (colID: number) => void;
|
|
21
|
+
/** Replace the manual column selection with an explicit ordered list (used
|
|
22
|
+
* e.g. for drag-and-drop reordering). Switches to manual mode. */
|
|
23
|
+
setManualColumns: (colIDs: number[]) => void;
|
|
24
|
+
setHoveredColumn: (colID: number | null) => void;
|
|
25
|
+
hoveredColumn: number | null;
|
|
26
|
+
/** Request the map to frame a particular column (e.g. on header click). */
|
|
27
|
+
zoomToColumn: (colID: number | null) => void;
|
|
28
|
+
zoomColumn: number | null;
|
|
29
|
+
/** Incremented on each zoom request so repeat clicks re-frame the column. */
|
|
30
|
+
zoomNonce: number;
|
|
11
31
|
projectID?: number;
|
|
12
32
|
}
|
|
13
33
|
export interface CorrelationProviderProps extends CorrelationMapInput {
|
|
@@ -19,9 +39,19 @@ export interface CorrelationProviderProps extends CorrelationMapInput {
|
|
|
19
39
|
}
|
|
20
40
|
type ComputedStore = {
|
|
21
41
|
focusedColumns: FocusedColumnGeoJSONRecord[];
|
|
42
|
+
selectionMode: CorrelationSelectionMode;
|
|
22
43
|
};
|
|
23
|
-
export declare function ColumnCorrelationProvider({ children, columns, projectID, inProcess, focusedLine, onSelectColumns, }: CorrelationProviderProps): import('react').FunctionComponentElement<import('react').ProviderProps<StoreApi<CorrelationMapStore> | null>>;
|
|
44
|
+
export declare function ColumnCorrelationProvider({ children, columns, projectID, inProcess, focusedLine, manualColumns, onSelectColumns, }: CorrelationProviderProps): import('react').FunctionComponentElement<import('react').ProviderProps<StoreApi<CorrelationMapStore> | null>>;
|
|
24
45
|
export declare function useCorrelationMapStore(selector: (state: CorrelationMapStore & ComputedStore) => any): any;
|
|
46
|
+
export interface ColumnMapLink {
|
|
47
|
+
onColumnMouseOver: (colID: number | null) => void;
|
|
48
|
+
onColumnClick: (colID: number) => void;
|
|
49
|
+
}
|
|
50
|
+
/** Wire a correlation chart's column-level events to the correlation map:
|
|
51
|
+
* hovering a column highlights it on the map, and clicking a column header
|
|
52
|
+
* frames it. Spread the result into `CorrelationChart`. Must be used within a
|
|
53
|
+
* `ColumnCorrelationProvider`. */
|
|
54
|
+
export declare function useColumnMapLink(): ColumnMapLink;
|
|
25
55
|
interface FocusedColumnGeoJSONRecord extends ColumnGeoJSONRecord {
|
|
26
56
|
properties: {
|
|
27
57
|
centroid: Point;
|