@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
@@ -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;
@@ -11,9 +11,12 @@ import { buffer } from "@turf/buffer";
11
11
  import { booleanPointInPolygon } from "@turf/boolean-point-in-polygon";
12
12
  const CorrelationStoreContext = createContext(null);
13
13
  const computed = createComputed((state) => {
14
+ const manual = state.manualColumns != null;
14
15
  return {
15
- // We compute the focused columns based on columns and focusedLine
16
- focusedColumns: buildCorrelationColumns(state.columns, state.focusedLine)
16
+ selectionMode: manual ? "manual" : "line",
17
+ // Focused columns are derived either from an explicit manual selection or
18
+ // from the columns intersecting the line of section.
19
+ focusedColumns: manual ? buildManualColumns(state.columns, state.manualColumns) : buildCorrelationColumns(state.columns, state.focusedLine)
17
20
  };
18
21
  });
19
22
  function ColumnCorrelationProvider({
@@ -22,6 +25,7 @@ function ColumnCorrelationProvider({
22
25
  projectID,
23
26
  inProcess,
24
27
  focusedLine,
28
+ manualColumns = null,
25
29
  onSelectColumns
26
30
  }) {
27
31
  const [store] = useState(() => {
@@ -29,10 +33,15 @@ function ColumnCorrelationProvider({
29
33
  computed((set, get) => {
30
34
  return {
31
35
  focusedLine,
36
+ manualColumns,
37
+ hoveredColumn: null,
38
+ zoomColumn: null,
39
+ zoomNonce: 0,
32
40
  projectID,
33
41
  columns: null,
34
42
  onClickMap(event, point) {
35
43
  const state = get();
44
+ if (state.manualColumns != null) return;
36
45
  const shiftKeyPressed = event.originalEvent.shiftKey;
37
46
  let existingCoords = state.focusedLine?.coordinates ?? [];
38
47
  if (existingCoords.length >= 2 && !shiftKeyPressed) {
@@ -44,6 +53,29 @@ function ColumnCorrelationProvider({
44
53
  coordinates: [...existingCoords, point.coordinates]
45
54
  }
46
55
  });
56
+ },
57
+ toggleColumn(colID) {
58
+ const state = get();
59
+ const current = state.manualColumns ?? state.focusedColumns.map((d) => d.properties.col_id);
60
+ const next = current.includes(colID) ? current.filter((d) => d !== colID) : [...current, colID];
61
+ set({ manualColumns: next, focusedLine: null });
62
+ },
63
+ removeColumn(colID) {
64
+ const state = get();
65
+ const current = state.manualColumns ?? state.focusedColumns.map((d) => d.properties.col_id);
66
+ set({
67
+ manualColumns: current.filter((d) => d !== colID),
68
+ focusedLine: null
69
+ });
70
+ },
71
+ setManualColumns(colIDs) {
72
+ set({ manualColumns: colIDs, focusedLine: null });
73
+ },
74
+ setHoveredColumn(colID) {
75
+ set({ hoveredColumn: colID });
76
+ },
77
+ zoomToColumn(colID) {
78
+ set({ zoomColumn: colID, zoomNonce: get().zoomNonce + 1 });
47
79
  }
48
80
  };
49
81
  })
@@ -69,6 +101,14 @@ function useCorrelationMapStore(selector) {
69
101
  }
70
102
  return useStore(storeApi, selector);
71
103
  }
104
+ function useColumnMapLink() {
105
+ const setHoveredColumn = useCorrelationMapStore((s) => s.setHoveredColumn);
106
+ const zoomToColumn = useCorrelationMapStore((s) => s.zoomToColumn);
107
+ return {
108
+ onColumnMouseOver: setHoveredColumn,
109
+ onColumnClick: zoomToColumn
110
+ };
111
+ }
72
112
  function buildCorrelationColumns(columns, line) {
73
113
  if (columns == null || line == null || line.coordinates.length < 2) {
74
114
  return [];
@@ -78,6 +118,26 @@ function buildCorrelationColumns(columns, line) {
78
118
  line
79
119
  );
80
120
  }
121
+ function buildManualColumns(columns, ids) {
122
+ if (columns == null || ids == null) return [];
123
+ const byID = new Map(columns.map((c) => [c.properties.col_id, c]));
124
+ const result = [];
125
+ ids.forEach((id, i) => {
126
+ const col = byID.get(id);
127
+ if (col == null) return;
128
+ const c = centroid(col.geometry);
129
+ result.push({
130
+ ...col,
131
+ properties: {
132
+ ...col.properties,
133
+ centroid: c,
134
+ nearestPointOnLine: c,
135
+ distanceAlongLine: i
136
+ }
137
+ });
138
+ });
139
+ return result;
140
+ }
81
141
  function computeIntersectingColumns(columns, line) {
82
142
  if (columns == null || line == null) {
83
143
  return [];
@@ -124,6 +184,7 @@ function sorted(data, accessor) {
124
184
  }
125
185
  export {
126
186
  ColumnCorrelationProvider,
187
+ useColumnMapLink,
127
188
  useCorrelationMapStore
128
189
  };
129
190
  //# sourceMappingURL=state.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"state.js","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":[],"mappings":"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"}
1
+ {"version":3,"file":"state.js","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":[],"mappings":"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"}
@@ -42,7 +42,11 @@ function buildColumnsStyle(opts = {}) {
42
42
  let columnColor = columnBaseColor;
43
43
  const columnSelectedColor = opts.selectedColor ?? colorUtils.getCSSVariable("--selection-color", "purple");
44
44
  const lineColor = opts.lineColor ?? columnBaseColor;
45
- const isHovered = ["boolean", ["feature-state", "hover"], false];
45
+ const isHovered = [
46
+ "any",
47
+ ["boolean", ["feature-state", "hover"], false],
48
+ ["boolean", ["feature-state", "highlighted"], false]
49
+ ];
46
50
  if (typeof columnColor === "string") {
47
51
  columnColor = [
48
52
  "coalesce",
@@ -1 +1 @@
1
- {"version":3,"file":"columns.cjs","sources":["../../../src/maps/layers/columns.ts"],"sourcesContent":["import { useMapStyleOperator, useOverlayStyle } from \"@macrostrat/mapbox-react\";\nimport { setGeoJSON } from \"@macrostrat/mapbox-utils\";\nimport GeoJSON, { FeatureCollection } from \"geojson\";\nimport { GeoJSONFeature } from \"mapbox-gl\";\nimport { getCSSVariable } from \"@macrostrat/color-utils\";\nimport { buildGeoJSONSource } from \"@macrostrat/mapbox-utils\";\nimport { StyleFragment } from \"@macrostrat/mapbox-react\";\n\nexport interface ColumnsStyleOptions {\n color?: string | any[];\n selectedColor?: string | any[];\n opacity?: number | any[];\n circleRadius?: number | any[];\n lineOpacity?: number | any[];\n lineWidth?: number | any[];\n lineColor?: string | any[];\n}\n\ninterface ColumnLayerOptions extends ColumnsStyleOptions {\n enabled: boolean;\n columns: FeatureCollection | GeoJSONFeature[];\n}\n\nexport function BaseColumnsLayer({\n enabled = true,\n columns,\n ...rest\n}: ColumnLayerOptions) {\n useOverlayStyle(() => (enabled ? buildColumnsStyle(rest) : null), [enabled]);\n\n useMapStyleOperator(\n (map) => {\n if (columns == null) {\n return;\n }\n\n setGeoJSON(map, \"columns\", promoteToFeatureCollection(columns));\n },\n [columns, enabled],\n );\n return null;\n}\n\nfunction promoteToFeatureCollection(\n data: FeatureCollection | GeoJSONFeature | GeoJSONFeature[] | null,\n): FeatureCollection | null {\n if (data == null) return null;\n if (\"type\" in data && data.type == \"Feature\") {\n return {\n type: \"FeatureCollection\",\n features: [data],\n };\n }\n if (\n \"type\" in data &&\n data.type == \"FeatureCollection\" &&\n \"features\" in data\n ) {\n return data;\n }\n return {\n type: \"FeatureCollection\",\n features: data,\n };\n}\n\nexport function buildColumnsStyle(\n opts: ColumnsStyleOptions = {},\n): StyleFragment {\n const { color } = opts;\n const columnBaseColor =\n color ?? getCSSVariable(\"--text-subtle-color\", \"black\");\n let columnColor: any = columnBaseColor;\n const columnSelectedColor =\n opts.selectedColor ?? getCSSVariable(\"--selection-color\", \"purple\");\n\n const lineColor = opts.lineColor ?? columnBaseColor;\n const isHovered = [\"boolean\", [\"feature-state\", \"hover\"], false];\n // If color is in the feature state or geojson properties, use that as second choice\n if (typeof columnColor === \"string\") {\n columnColor = [\n \"coalesce\",\n [\"feature-state\", \"color\"],\n [\"get\", \"color\"],\n columnColor,\n ];\n }\n\n const fillOpacity = opts.opacity ?? 0.1;\n\n const lineOpacity = opts.lineOpacity ?? 0.5;\n const lineWidth = opts.lineWidth ?? 2;\n\n const circleRadius = opts.circleRadius ?? 4;\n\n return {\n sources: {\n columns: buildGeoJSONSource(),\n },\n version: 8,\n layers: [\n {\n id: \"columns-fill\",\n type: \"fill\",\n source: \"columns\",\n paint: {\n \"fill-color\": columnColor,\n \"fill-opacity\": fillOpacity,\n },\n },\n {\n id: \"columns-points\",\n type: \"circle\",\n source: \"columns\",\n paint: {\n \"circle-radius\": circleRadius,\n \"circle-color\": columnColor,\n \"circle-opacity\": fillOpacity,\n },\n filter: [\"==\", \"$type\", \"Point\"],\n },\n {\n id: \"columns-line\",\n type: \"line\",\n source: \"columns\",\n paint: {\n \"line-color\": [\"case\", isHovered, columnSelectedColor, lineColor],\n \"line-width\": lineWidth,\n \"line-opacity\": [\"case\", isHovered, 1.0, lineOpacity],\n },\n },\n ],\n };\n}\n\nexport type ColumnFeature = GeoJSON.Feature & { id: number };\n\nconst selectedColumnSourceID = \"selectedColumn\";\n\nexport function SelectedColumnOverlay({\n selectedColumn,\n ...styleOpts\n}: { selectedColumn: ColumnFeature | null } & SelectedColumnStyleOpts) {\n useOverlayStyle(() => buildSelectedColumnStyle(styleOpts), []);\n\n useMapStyleOperator(\n (map) => {\n setGeoJSON(\n map,\n selectedColumnSourceID,\n promoteToFeatureCollection(selectedColumn as GeoJSONFeature | null),\n );\n },\n [selectedColumn],\n );\n\n return null;\n}\n\ninterface SelectedColumnStyleOpts {\n color?: string | any[];\n}\n\nfunction buildSelectedColumnStyle(\n opts: SelectedColumnStyleOpts = {},\n): StyleFragment {\n const columnSelectedColor: any =\n opts.color ?? getCSSVariable(\"--selection-color\", \"purple\");\n\n return {\n sources: {\n selectedColumn: buildGeoJSONSource(),\n },\n version: 8,\n layers: [\n {\n id: \"selected-column-highlight\",\n type: \"fill\",\n source: selectedColumnSourceID,\n paint: {\n \"fill-color\": columnSelectedColor,\n \"fill-opacity\": 0.5,\n },\n filter: [\"==\", \"$type\", \"Polygon\"],\n },\n {\n id: \"selected-column-edge\",\n type: \"line\",\n source: selectedColumnSourceID,\n paint: {\n \"line-color\": columnSelectedColor,\n \"line-width\": 2,\n \"line-opacity\": 1.0,\n },\n filter: [\"==\", \"$type\", \"Polygon\"],\n },\n {\n id: \"selected-column-point\",\n type: \"circle\",\n source: selectedColumnSourceID,\n paint: {\n \"circle-radius\": 6,\n \"circle-color\": columnSelectedColor,\n \"circle-opacity\": 1.0,\n },\n filter: [\"==\", \"$type\", \"Point\"],\n },\n ],\n };\n}\n"],"names":["useOverlayStyle","useMapStyleOperator","setGeoJSON","getCSSVariable","buildGeoJSONSource"],"mappings":"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"}
1
+ {"version":3,"file":"columns.cjs","sources":["../../../src/maps/layers/columns.ts"],"sourcesContent":["import { useMapStyleOperator, useOverlayStyle } from \"@macrostrat/mapbox-react\";\nimport { setGeoJSON } from \"@macrostrat/mapbox-utils\";\nimport GeoJSON, { FeatureCollection } from \"geojson\";\nimport { GeoJSONFeature } from \"mapbox-gl\";\nimport { getCSSVariable } from \"@macrostrat/color-utils\";\nimport { buildGeoJSONSource } from \"@macrostrat/mapbox-utils\";\nimport { StyleFragment } from \"@macrostrat/mapbox-react\";\n\nexport interface ColumnsStyleOptions {\n color?: string | any[];\n selectedColor?: string | any[];\n opacity?: number | any[];\n circleRadius?: number | any[];\n lineOpacity?: number | any[];\n lineWidth?: number | any[];\n lineColor?: string | any[];\n}\n\ninterface ColumnLayerOptions extends ColumnsStyleOptions {\n enabled: boolean;\n columns: FeatureCollection | GeoJSONFeature[];\n}\n\nexport function BaseColumnsLayer({\n enabled = true,\n columns,\n ...rest\n}: ColumnLayerOptions) {\n useOverlayStyle(() => (enabled ? buildColumnsStyle(rest) : null), [enabled]);\n\n useMapStyleOperator(\n (map) => {\n if (columns == null) {\n return;\n }\n\n setGeoJSON(map, \"columns\", promoteToFeatureCollection(columns));\n },\n [columns, enabled],\n );\n return null;\n}\n\nfunction promoteToFeatureCollection(\n data: FeatureCollection | GeoJSONFeature | GeoJSONFeature[] | null,\n): FeatureCollection | null {\n if (data == null) return null;\n if (\"type\" in data && data.type == \"Feature\") {\n return {\n type: \"FeatureCollection\",\n features: [data],\n };\n }\n if (\n \"type\" in data &&\n data.type == \"FeatureCollection\" &&\n \"features\" in data\n ) {\n return data;\n }\n return {\n type: \"FeatureCollection\",\n features: data,\n };\n}\n\nexport function buildColumnsStyle(\n opts: ColumnsStyleOptions = {},\n): StyleFragment {\n const { color } = opts;\n const columnBaseColor =\n color ?? getCSSVariable(\"--text-subtle-color\", \"black\");\n let columnColor: any = columnBaseColor;\n const columnSelectedColor =\n opts.selectedColor ?? getCSSVariable(\"--selection-color\", \"purple\");\n\n const lineColor = opts.lineColor ?? columnBaseColor;\n // Highlight on either direct map hover or an externally-driven highlight\n // (e.g. hovering the corresponding column header elsewhere in the UI).\n const isHovered = [\n \"any\",\n [\"boolean\", [\"feature-state\", \"hover\"], false],\n [\"boolean\", [\"feature-state\", \"highlighted\"], false],\n ];\n // If color is in the feature state or geojson properties, use that as second choice\n if (typeof columnColor === \"string\") {\n columnColor = [\n \"coalesce\",\n [\"feature-state\", \"color\"],\n [\"get\", \"color\"],\n columnColor,\n ];\n }\n\n const fillOpacity = opts.opacity ?? 0.1;\n\n const lineOpacity = opts.lineOpacity ?? 0.5;\n const lineWidth = opts.lineWidth ?? 2;\n\n const circleRadius = opts.circleRadius ?? 4;\n\n return {\n sources: {\n columns: buildGeoJSONSource(),\n },\n version: 8,\n layers: [\n {\n id: \"columns-fill\",\n type: \"fill\",\n source: \"columns\",\n paint: {\n \"fill-color\": columnColor,\n \"fill-opacity\": fillOpacity,\n },\n },\n {\n id: \"columns-points\",\n type: \"circle\",\n source: \"columns\",\n paint: {\n \"circle-radius\": circleRadius,\n \"circle-color\": columnColor,\n \"circle-opacity\": fillOpacity,\n },\n filter: [\"==\", \"$type\", \"Point\"],\n },\n {\n id: \"columns-line\",\n type: \"line\",\n source: \"columns\",\n paint: {\n \"line-color\": [\"case\", isHovered, columnSelectedColor, lineColor],\n \"line-width\": lineWidth,\n \"line-opacity\": [\"case\", isHovered, 1.0, lineOpacity],\n },\n },\n ],\n };\n}\n\nexport type ColumnFeature = GeoJSON.Feature & { id: number };\n\nconst selectedColumnSourceID = \"selectedColumn\";\n\nexport function SelectedColumnOverlay({\n selectedColumn,\n ...styleOpts\n}: { selectedColumn: ColumnFeature | null } & SelectedColumnStyleOpts) {\n useOverlayStyle(() => buildSelectedColumnStyle(styleOpts), []);\n\n useMapStyleOperator(\n (map) => {\n setGeoJSON(\n map,\n selectedColumnSourceID,\n promoteToFeatureCollection(selectedColumn as GeoJSONFeature | null),\n );\n },\n [selectedColumn],\n );\n\n return null;\n}\n\ninterface SelectedColumnStyleOpts {\n color?: string | any[];\n}\n\nfunction buildSelectedColumnStyle(\n opts: SelectedColumnStyleOpts = {},\n): StyleFragment {\n const columnSelectedColor: any =\n opts.color ?? getCSSVariable(\"--selection-color\", \"purple\");\n\n return {\n sources: {\n selectedColumn: buildGeoJSONSource(),\n },\n version: 8,\n layers: [\n {\n id: \"selected-column-highlight\",\n type: \"fill\",\n source: selectedColumnSourceID,\n paint: {\n \"fill-color\": columnSelectedColor,\n \"fill-opacity\": 0.5,\n },\n filter: [\"==\", \"$type\", \"Polygon\"],\n },\n {\n id: \"selected-column-edge\",\n type: \"line\",\n source: selectedColumnSourceID,\n paint: {\n \"line-color\": columnSelectedColor,\n \"line-width\": 2,\n \"line-opacity\": 1.0,\n },\n filter: [\"==\", \"$type\", \"Polygon\"],\n },\n {\n id: \"selected-column-point\",\n type: \"circle\",\n source: selectedColumnSourceID,\n paint: {\n \"circle-radius\": 6,\n \"circle-color\": columnSelectedColor,\n \"circle-opacity\": 1.0,\n },\n filter: [\"==\", \"$type\", \"Point\"],\n },\n ],\n };\n}\n"],"names":["useOverlayStyle","useMapStyleOperator","setGeoJSON","getCSSVariable","buildGeoJSONSource"],"mappings":"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"}
@@ -40,7 +40,11 @@ function buildColumnsStyle(opts = {}) {
40
40
  let columnColor = columnBaseColor;
41
41
  const columnSelectedColor = opts.selectedColor ?? getCSSVariable("--selection-color", "purple");
42
42
  const lineColor = opts.lineColor ?? columnBaseColor;
43
- const isHovered = ["boolean", ["feature-state", "hover"], false];
43
+ const isHovered = [
44
+ "any",
45
+ ["boolean", ["feature-state", "hover"], false],
46
+ ["boolean", ["feature-state", "highlighted"], false]
47
+ ];
44
48
  if (typeof columnColor === "string") {
45
49
  columnColor = [
46
50
  "coalesce",
@@ -1 +1 @@
1
- {"version":3,"file":"columns.js","sources":["../../../src/maps/layers/columns.ts"],"sourcesContent":["import { useMapStyleOperator, useOverlayStyle } from \"@macrostrat/mapbox-react\";\nimport { setGeoJSON } from \"@macrostrat/mapbox-utils\";\nimport GeoJSON, { FeatureCollection } from \"geojson\";\nimport { GeoJSONFeature } from \"mapbox-gl\";\nimport { getCSSVariable } from \"@macrostrat/color-utils\";\nimport { buildGeoJSONSource } from \"@macrostrat/mapbox-utils\";\nimport { StyleFragment } from \"@macrostrat/mapbox-react\";\n\nexport interface ColumnsStyleOptions {\n color?: string | any[];\n selectedColor?: string | any[];\n opacity?: number | any[];\n circleRadius?: number | any[];\n lineOpacity?: number | any[];\n lineWidth?: number | any[];\n lineColor?: string | any[];\n}\n\ninterface ColumnLayerOptions extends ColumnsStyleOptions {\n enabled: boolean;\n columns: FeatureCollection | GeoJSONFeature[];\n}\n\nexport function BaseColumnsLayer({\n enabled = true,\n columns,\n ...rest\n}: ColumnLayerOptions) {\n useOverlayStyle(() => (enabled ? buildColumnsStyle(rest) : null), [enabled]);\n\n useMapStyleOperator(\n (map) => {\n if (columns == null) {\n return;\n }\n\n setGeoJSON(map, \"columns\", promoteToFeatureCollection(columns));\n },\n [columns, enabled],\n );\n return null;\n}\n\nfunction promoteToFeatureCollection(\n data: FeatureCollection | GeoJSONFeature | GeoJSONFeature[] | null,\n): FeatureCollection | null {\n if (data == null) return null;\n if (\"type\" in data && data.type == \"Feature\") {\n return {\n type: \"FeatureCollection\",\n features: [data],\n };\n }\n if (\n \"type\" in data &&\n data.type == \"FeatureCollection\" &&\n \"features\" in data\n ) {\n return data;\n }\n return {\n type: \"FeatureCollection\",\n features: data,\n };\n}\n\nexport function buildColumnsStyle(\n opts: ColumnsStyleOptions = {},\n): StyleFragment {\n const { color } = opts;\n const columnBaseColor =\n color ?? getCSSVariable(\"--text-subtle-color\", \"black\");\n let columnColor: any = columnBaseColor;\n const columnSelectedColor =\n opts.selectedColor ?? getCSSVariable(\"--selection-color\", \"purple\");\n\n const lineColor = opts.lineColor ?? columnBaseColor;\n const isHovered = [\"boolean\", [\"feature-state\", \"hover\"], false];\n // If color is in the feature state or geojson properties, use that as second choice\n if (typeof columnColor === \"string\") {\n columnColor = [\n \"coalesce\",\n [\"feature-state\", \"color\"],\n [\"get\", \"color\"],\n columnColor,\n ];\n }\n\n const fillOpacity = opts.opacity ?? 0.1;\n\n const lineOpacity = opts.lineOpacity ?? 0.5;\n const lineWidth = opts.lineWidth ?? 2;\n\n const circleRadius = opts.circleRadius ?? 4;\n\n return {\n sources: {\n columns: buildGeoJSONSource(),\n },\n version: 8,\n layers: [\n {\n id: \"columns-fill\",\n type: \"fill\",\n source: \"columns\",\n paint: {\n \"fill-color\": columnColor,\n \"fill-opacity\": fillOpacity,\n },\n },\n {\n id: \"columns-points\",\n type: \"circle\",\n source: \"columns\",\n paint: {\n \"circle-radius\": circleRadius,\n \"circle-color\": columnColor,\n \"circle-opacity\": fillOpacity,\n },\n filter: [\"==\", \"$type\", \"Point\"],\n },\n {\n id: \"columns-line\",\n type: \"line\",\n source: \"columns\",\n paint: {\n \"line-color\": [\"case\", isHovered, columnSelectedColor, lineColor],\n \"line-width\": lineWidth,\n \"line-opacity\": [\"case\", isHovered, 1.0, lineOpacity],\n },\n },\n ],\n };\n}\n\nexport type ColumnFeature = GeoJSON.Feature & { id: number };\n\nconst selectedColumnSourceID = \"selectedColumn\";\n\nexport function SelectedColumnOverlay({\n selectedColumn,\n ...styleOpts\n}: { selectedColumn: ColumnFeature | null } & SelectedColumnStyleOpts) {\n useOverlayStyle(() => buildSelectedColumnStyle(styleOpts), []);\n\n useMapStyleOperator(\n (map) => {\n setGeoJSON(\n map,\n selectedColumnSourceID,\n promoteToFeatureCollection(selectedColumn as GeoJSONFeature | null),\n );\n },\n [selectedColumn],\n );\n\n return null;\n}\n\ninterface SelectedColumnStyleOpts {\n color?: string | any[];\n}\n\nfunction buildSelectedColumnStyle(\n opts: SelectedColumnStyleOpts = {},\n): StyleFragment {\n const columnSelectedColor: any =\n opts.color ?? getCSSVariable(\"--selection-color\", \"purple\");\n\n return {\n sources: {\n selectedColumn: buildGeoJSONSource(),\n },\n version: 8,\n layers: [\n {\n id: \"selected-column-highlight\",\n type: \"fill\",\n source: selectedColumnSourceID,\n paint: {\n \"fill-color\": columnSelectedColor,\n \"fill-opacity\": 0.5,\n },\n filter: [\"==\", \"$type\", \"Polygon\"],\n },\n {\n id: \"selected-column-edge\",\n type: \"line\",\n source: selectedColumnSourceID,\n paint: {\n \"line-color\": columnSelectedColor,\n \"line-width\": 2,\n \"line-opacity\": 1.0,\n },\n filter: [\"==\", \"$type\", \"Polygon\"],\n },\n {\n id: \"selected-column-point\",\n type: \"circle\",\n source: selectedColumnSourceID,\n paint: {\n \"circle-radius\": 6,\n \"circle-color\": columnSelectedColor,\n \"circle-opacity\": 1.0,\n },\n filter: [\"==\", \"$type\", \"Point\"],\n },\n ],\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"columns.js","sources":["../../../src/maps/layers/columns.ts"],"sourcesContent":["import { useMapStyleOperator, useOverlayStyle } from \"@macrostrat/mapbox-react\";\nimport { setGeoJSON } from \"@macrostrat/mapbox-utils\";\nimport GeoJSON, { FeatureCollection } from \"geojson\";\nimport { GeoJSONFeature } from \"mapbox-gl\";\nimport { getCSSVariable } from \"@macrostrat/color-utils\";\nimport { buildGeoJSONSource } from \"@macrostrat/mapbox-utils\";\nimport { StyleFragment } from \"@macrostrat/mapbox-react\";\n\nexport interface ColumnsStyleOptions {\n color?: string | any[];\n selectedColor?: string | any[];\n opacity?: number | any[];\n circleRadius?: number | any[];\n lineOpacity?: number | any[];\n lineWidth?: number | any[];\n lineColor?: string | any[];\n}\n\ninterface ColumnLayerOptions extends ColumnsStyleOptions {\n enabled: boolean;\n columns: FeatureCollection | GeoJSONFeature[];\n}\n\nexport function BaseColumnsLayer({\n enabled = true,\n columns,\n ...rest\n}: ColumnLayerOptions) {\n useOverlayStyle(() => (enabled ? buildColumnsStyle(rest) : null), [enabled]);\n\n useMapStyleOperator(\n (map) => {\n if (columns == null) {\n return;\n }\n\n setGeoJSON(map, \"columns\", promoteToFeatureCollection(columns));\n },\n [columns, enabled],\n );\n return null;\n}\n\nfunction promoteToFeatureCollection(\n data: FeatureCollection | GeoJSONFeature | GeoJSONFeature[] | null,\n): FeatureCollection | null {\n if (data == null) return null;\n if (\"type\" in data && data.type == \"Feature\") {\n return {\n type: \"FeatureCollection\",\n features: [data],\n };\n }\n if (\n \"type\" in data &&\n data.type == \"FeatureCollection\" &&\n \"features\" in data\n ) {\n return data;\n }\n return {\n type: \"FeatureCollection\",\n features: data,\n };\n}\n\nexport function buildColumnsStyle(\n opts: ColumnsStyleOptions = {},\n): StyleFragment {\n const { color } = opts;\n const columnBaseColor =\n color ?? getCSSVariable(\"--text-subtle-color\", \"black\");\n let columnColor: any = columnBaseColor;\n const columnSelectedColor =\n opts.selectedColor ?? getCSSVariable(\"--selection-color\", \"purple\");\n\n const lineColor = opts.lineColor ?? columnBaseColor;\n // Highlight on either direct map hover or an externally-driven highlight\n // (e.g. hovering the corresponding column header elsewhere in the UI).\n const isHovered = [\n \"any\",\n [\"boolean\", [\"feature-state\", \"hover\"], false],\n [\"boolean\", [\"feature-state\", \"highlighted\"], false],\n ];\n // If color is in the feature state or geojson properties, use that as second choice\n if (typeof columnColor === \"string\") {\n columnColor = [\n \"coalesce\",\n [\"feature-state\", \"color\"],\n [\"get\", \"color\"],\n columnColor,\n ];\n }\n\n const fillOpacity = opts.opacity ?? 0.1;\n\n const lineOpacity = opts.lineOpacity ?? 0.5;\n const lineWidth = opts.lineWidth ?? 2;\n\n const circleRadius = opts.circleRadius ?? 4;\n\n return {\n sources: {\n columns: buildGeoJSONSource(),\n },\n version: 8,\n layers: [\n {\n id: \"columns-fill\",\n type: \"fill\",\n source: \"columns\",\n paint: {\n \"fill-color\": columnColor,\n \"fill-opacity\": fillOpacity,\n },\n },\n {\n id: \"columns-points\",\n type: \"circle\",\n source: \"columns\",\n paint: {\n \"circle-radius\": circleRadius,\n \"circle-color\": columnColor,\n \"circle-opacity\": fillOpacity,\n },\n filter: [\"==\", \"$type\", \"Point\"],\n },\n {\n id: \"columns-line\",\n type: \"line\",\n source: \"columns\",\n paint: {\n \"line-color\": [\"case\", isHovered, columnSelectedColor, lineColor],\n \"line-width\": lineWidth,\n \"line-opacity\": [\"case\", isHovered, 1.0, lineOpacity],\n },\n },\n ],\n };\n}\n\nexport type ColumnFeature = GeoJSON.Feature & { id: number };\n\nconst selectedColumnSourceID = \"selectedColumn\";\n\nexport function SelectedColumnOverlay({\n selectedColumn,\n ...styleOpts\n}: { selectedColumn: ColumnFeature | null } & SelectedColumnStyleOpts) {\n useOverlayStyle(() => buildSelectedColumnStyle(styleOpts), []);\n\n useMapStyleOperator(\n (map) => {\n setGeoJSON(\n map,\n selectedColumnSourceID,\n promoteToFeatureCollection(selectedColumn as GeoJSONFeature | null),\n );\n },\n [selectedColumn],\n );\n\n return null;\n}\n\ninterface SelectedColumnStyleOpts {\n color?: string | any[];\n}\n\nfunction buildSelectedColumnStyle(\n opts: SelectedColumnStyleOpts = {},\n): StyleFragment {\n const columnSelectedColor: any =\n opts.color ?? getCSSVariable(\"--selection-color\", \"purple\");\n\n return {\n sources: {\n selectedColumn: buildGeoJSONSource(),\n },\n version: 8,\n layers: [\n {\n id: \"selected-column-highlight\",\n type: \"fill\",\n source: selectedColumnSourceID,\n paint: {\n \"fill-color\": columnSelectedColor,\n \"fill-opacity\": 0.5,\n },\n filter: [\"==\", \"$type\", \"Polygon\"],\n },\n {\n id: \"selected-column-edge\",\n type: \"line\",\n source: selectedColumnSourceID,\n paint: {\n \"line-color\": columnSelectedColor,\n \"line-width\": 2,\n \"line-opacity\": 1.0,\n },\n filter: [\"==\", \"$type\", \"Polygon\"],\n },\n {\n id: \"selected-column-point\",\n type: \"circle\",\n source: selectedColumnSourceID,\n paint: {\n \"circle-radius\": 6,\n \"circle-color\": columnSelectedColor,\n \"circle-opacity\": 1.0,\n },\n filter: [\"==\", \"$type\", \"Point\"],\n },\n ],\n };\n}\n"],"names":[],"mappings":"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"}
@@ -9,10 +9,11 @@ function buildCompositeScaleInfo(inputScales, unconformityHeight) {
9
9
  let totalHeight = unconformityHeight / 2;
10
10
  let lastSectionTopHeight = 0;
11
11
  const packages2 = [];
12
- for (const group of inputScales) {
12
+ for (let i = 0; i < inputScales.length; i++) {
13
+ const group = inputScales[i];
13
14
  const { domain, scale } = group;
14
15
  const [b_age, t_age] = domain;
15
- const key = `package-${b_age}-${t_age}`;
16
+ const key = `package-${i}`;
16
17
  packages2.push({
17
18
  ...createPackageScale(group, totalHeight),
18
19
  key,
@@ -79,6 +80,7 @@ function buildSectionScale(data, opts) {
79
80
  minPixelScale = 0.2,
80
81
  axisType,
81
82
  minSectionHeight,
83
+ visibleWindow,
82
84
  scale,
83
85
  hybridScale
84
86
  } = opts;
@@ -97,7 +99,7 @@ function buildSectionScale(data, opts) {
97
99
  }
98
100
  if (scale == null) {
99
101
  if (_pixelScale == null) {
100
- const avgAgeRange = findAverageUnitHeight(data, axisType);
102
+ const avgAgeRange = findAverageUnitHeight(data, axisType, visibleWindow);
101
103
  _pixelScale = Math.max(targetUnitHeight / avgAgeRange, minPixelScale);
102
104
  }
103
105
  let height = dAge * _pixelScale;
@@ -112,6 +114,78 @@ function buildSectionScale(data, opts) {
112
114
  0
113
115
  );
114
116
  }
117
+ function resolveWindowScales(sections, focalWindow, floor) {
118
+ const [b_focal, t_focal] = focalWindow;
119
+ const scales = /* @__PURE__ */ new Map();
120
+ for (const section of sections) {
121
+ const t_age = Math.max(section.t_age, t_focal);
122
+ const b_age = Math.min(section.b_age, b_focal);
123
+ if (b_age <= t_age) continue;
124
+ const natural = section.scaleInfo.pixelScale;
125
+ if (!(natural > 0)) continue;
126
+ const isClipped = b_age < section.b_age || t_age > section.t_age;
127
+ const needed = isClipped && floor > 0 ? floor / (b_age - t_age) : 0;
128
+ scales.set(section, Math.max(natural, needed));
129
+ }
130
+ return scales;
131
+ }
132
+ function padWindowByPixels(sections, window, padding, scaleFor) {
133
+ if (!(padding > 0) || sections.length === 0) return window;
134
+ const [b_age, t_age] = window;
135
+ return [
136
+ spendOutward(sections, b_age, 1, padding, scaleFor),
137
+ spendOutward(sections, t_age, -1, padding, scaleFor)
138
+ ];
139
+ }
140
+ function spendOutward(sections, edge, direction, padding, scaleFor) {
141
+ const beyond = sections.filter(
142
+ (s) => direction * (s.b_age - edge) > 0 || direction * (s.t_age - edge) > 0
143
+ ).sort((a, b) => direction * (nearEdge(a, direction) - nearEdge(b, direction)));
144
+ let bound = edge;
145
+ let budget = padding;
146
+ for (const section of beyond) {
147
+ const pixelScale = scaleFor(section);
148
+ if (!(pixelScale > 0)) continue;
149
+ const from = direction > 0 ? Math.max(section.t_age, bound) : Math.min(section.b_age, bound);
150
+ const available = direction > 0 ? section.b_age - from : from - section.t_age;
151
+ if (available <= 0) continue;
152
+ const cost = available * pixelScale;
153
+ if (budget <= cost) return from + direction * (budget / pixelScale);
154
+ bound = direction > 0 ? section.b_age : section.t_age;
155
+ budget -= cost;
156
+ }
157
+ return bound;
158
+ }
159
+ function nearEdge(section, direction) {
160
+ return direction > 0 ? section.t_age : section.b_age;
161
+ }
162
+ function trimSectionsToWindow(sections, window, scaleFor) {
163
+ const [b_win, t_win] = window;
164
+ const out = [];
165
+ for (const section of sections) {
166
+ const t_age = Math.max(section.t_age, t_win);
167
+ const b_age = Math.min(section.b_age, b_win);
168
+ if (b_age <= t_age) continue;
169
+ const pixelScale = scaleFor(section);
170
+ if (b_age === section.b_age && t_age === section.t_age && pixelScale === section.scaleInfo.pixelScale) {
171
+ out.push(section);
172
+ continue;
173
+ }
174
+ out.push({
175
+ ...section,
176
+ t_age,
177
+ b_age,
178
+ units: section.units.filter((u) => utils.agesOverlap(u, { t_age, b_age })),
179
+ scaleInfo: createPackageScale({
180
+ domain: [b_age, t_age],
181
+ pixelScale,
182
+ pixelHeight: (b_age - t_age) * pixelScale,
183
+ scale: null
184
+ })
185
+ });
186
+ }
187
+ return out;
188
+ }
115
189
  function createPackageScale(def, offset = 0) {
116
190
  const { domain, pixelScale, pixelHeight, scale, heightScale } = def;
117
191
  if (scale == null && pixelScale == null) {
@@ -159,12 +233,21 @@ function findSectionHeightRange(data, axisType) {
159
233
  return [b_pos, t_pos];
160
234
  }
161
235
  }
162
- function findAverageUnitHeight(data, axisType) {
163
- const unitHeights = data.map((d) => {
236
+ function findAverageUnitHeight(data, axisType, visibleWindow) {
237
+ const durations = (clip) => data.map((d) => {
164
238
  const [b_pos, t_pos] = utils.getUnitHeightRange(d, axisType);
165
- return Math.abs(b_pos - t_pos);
166
- });
167
- return unitHeights.reduce((a, b) => a + b, 0) / unitHeights.length;
239
+ if (!clip) return Math.abs(b_pos - t_pos);
240
+ const [b_win, t_win] = visibleWindow;
241
+ return Math.max(
242
+ 0,
243
+ Math.min(b_pos, b_win) - Math.max(t_pos, t_win)
244
+ );
245
+ }).filter((d) => d > 0);
246
+ const useWindow = visibleWindow != null && axisType === columnComponents.ColumnAxisType.AGE;
247
+ let heights = useWindow ? durations(true) : [];
248
+ if (heights.length === 0) heights = durations(false);
249
+ if (heights.length === 0) return 1;
250
+ return heights.reduce((a, b) => a + b, 0) / heights.length;
168
251
  }
169
252
  function createCompositeScale(sections, interpolateUnconformities = false) {
170
253
  const scales = [];
@@ -280,4 +363,7 @@ exports.computeSectionHeights = computeSectionHeights;
280
363
  exports.createCompositeScale = createCompositeScale;
281
364
  exports.createPackageScale = createPackageScale;
282
365
  exports.finalizeSectionHeights = finalizeSectionHeights;
366
+ exports.padWindowByPixels = padWindowByPixels;
367
+ exports.resolveWindowScales = resolveWindowScales;
368
+ exports.trimSectionsToWindow = trimSectionsToWindow;
283
369
  //# sourceMappingURL=composite-scale.cjs.map