diff --git a/.vscode/settings.json b/.vscode/settings.json index b7fef250..9f2d8e1c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -14,5 +14,12 @@ "editor.codeActionsOnSave": { "source.organizeImports": "never" } - } + }, + "cSpell.enableFiletypes": [ + "!javascript", + "!javascriptreact", + "!json", + "!scss", + "!typescript" + ] } diff --git a/.yarn/sdks/typescript/lib/tsserver.js b/.yarn/sdks/typescript/lib/tsserver.js index bbb1e465..ed800750 100644 --- a/.yarn/sdks/typescript/lib/tsserver.js +++ b/.yarn/sdks/typescript/lib/tsserver.js @@ -9,6 +9,13 @@ const relPnpApiPath = "../../../../.pnp.cjs"; const absPnpApiPath = resolve(__dirname, relPnpApiPath); const absRequire = createRequire(absPnpApiPath); +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require typescript/lib/tsserver.js + require(absPnpApiPath).setup(); + } +} + const moduleWrapper = tsserver => { if (!process.versions.pnp) { return tsserver; @@ -214,11 +221,11 @@ const moduleWrapper = tsserver => { return tsserver; }; -if (existsSync(absPnpApiPath)) { - if (!process.versions.pnp) { - // Setup the environment to be able to require typescript/lib/tsserver.js - require(absPnpApiPath).setup(); - } +const [major, minor] = absRequire(`typescript/package.json`).version.split(`.`, 2).map(value => parseInt(value, 10)); +// In TypeScript@>=5.5 the tsserver uses the public TypeScript API so that needs to be patched as well. +// Ref https://github.com/microsoft/TypeScript/pull/55326 +if (major > 5 || (major === 5 && minor >= 5)) { + moduleWrapper(absRequire(`typescript`)); } // Defer to the real typescript/lib/tsserver.js your application uses diff --git a/.yarn/sdks/typescript/lib/tsserverlibrary.js b/.yarn/sdks/typescript/lib/tsserverlibrary.js index a68f028f..4d997669 100644 --- a/.yarn/sdks/typescript/lib/tsserverlibrary.js +++ b/.yarn/sdks/typescript/lib/tsserverlibrary.js @@ -9,6 +9,13 @@ const relPnpApiPath = "../../../../.pnp.cjs"; const absPnpApiPath = resolve(__dirname, relPnpApiPath); const absRequire = createRequire(absPnpApiPath); +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require typescript/lib/tsserverlibrary.js + require(absPnpApiPath).setup(); + } +} + const moduleWrapper = tsserver => { if (!process.versions.pnp) { return tsserver; @@ -214,11 +221,11 @@ const moduleWrapper = tsserver => { return tsserver; }; -if (existsSync(absPnpApiPath)) { - if (!process.versions.pnp) { - // Setup the environment to be able to require typescript/lib/tsserverlibrary.js - require(absPnpApiPath).setup(); - } +const [major, minor] = absRequire(`typescript/package.json`).version.split(`.`, 2).map(value => parseInt(value, 10)); +// In TypeScript@>=5.5 the tsserver uses the public TypeScript API so that needs to be patched as well. +// Ref https://github.com/microsoft/TypeScript/pull/55326 +if (major > 5 || (major === 5 && minor >= 5)) { + moduleWrapper(absRequire(`typescript`)); } // Defer to the real typescript/lib/tsserverlibrary.js your application uses diff --git a/deps/web-components b/deps/web-components index 09590e10..7ac2a758 160000 --- a/deps/web-components +++ b/deps/web-components @@ -1 +1 @@ -Subproject commit 09590e103921ab3f06d11f65e18c91a87e0b4671 +Subproject commit 7ac2a7585bc3f74211614e76af9243f0be1905c8 diff --git a/package.json b/package.json index 925ef895..e91996f6 100644 --- a/package.json +++ b/package.json @@ -120,10 +120,7 @@ "react-router-hash-link": "^2.4.3", "reduce-reducers": "^1.0.4", "redux": "^4.0.5", - "regl": "^1.5.0", - "resium": "^1.13.1", "sirv": "^2.0.3", - "supports-color": "^9.4.0", "swagger-ui-react": "^5.12.3", "topojson-client": "^3.0.0", "transition-hook": "^1.5.2", diff --git a/src/pages/dev/filtering/interface.ts b/src/pages/dev/filtering/interface.ts index 74462987..2894ff9d 100644 --- a/src/pages/dev/filtering/interface.ts +++ b/src/pages/dev/filtering/interface.ts @@ -4,18 +4,15 @@ import { tileserverDomain, mapboxAccessToken } from "@macrostrat-web/settings"; import hyper from "@macrostrat/hyper"; import { DevMapPage } from "@macrostrat/map-interface"; import { buildMacrostratStyle } from "@macrostrat/mapbox-styles"; -import { useStoredState } from "@macrostrat/ui-components"; -import { Select, Omnibar } from "@blueprintjs/select"; +import { Select } from "@blueprintjs/select"; import mapboxgl from "mapbox-gl"; -import { useMemo, useState, useEffect } from "react"; +import { useMemo, useState } from "react"; import styles from "./main.module.styl"; import { replaceSourcesForTileset, LineSymbolManager, } from "~/_utils/map-layers.client"; -import { - LithologyMultiSelect -} from "./lithology-selector"; +import { LithologyMultiSelect } from "./lithology-selector"; export const h = hyper.styled(styles); @@ -27,7 +24,7 @@ enum Compilation { export function VectorMapInspectorPage({ overlayStyle = _macrostratStyle, title = null, - headerElement = null + headerElement = null, }: { headerElement?: React.ReactElement; title?: string; @@ -39,7 +36,9 @@ export function VectorMapInspectorPage({ const { showLineSymbols } = state; const _overlayStyle = useMemo(() => { - return replaceSourcesForTileset(overlayStyle, state.compilation, {lithology: state.lithologies}); + return replaceSourcesForTileset(overlayStyle, state.compilation, { + lithology: state.lithologies, + }); }, [overlayStyle, state.lithologies, state.compilation]) as mapboxgl.Style; const controls = h([ @@ -55,14 +54,14 @@ export function VectorMapInspectorPage({ compilation: state.compilation, setCompilation: (compilation) => { setState({ ...state, compilation }); - } + }, }), h(LithologyMultiSelect, { selectedLithologyNames: state.lithologies, onChange: (lithologies) => { setState({ ...state, lithologies }); - } - }) + }, + }), ]); return h( @@ -78,23 +77,31 @@ export function VectorMapInspectorPage({ } const CompilationSelector = ({ compilation, setCompilation }) => { - return h(Select, { - items: Object.values(Compilation), - itemRenderer: (item: any, { handleClick }) => { - return h(MenuItem, { onClick: handleClick, text: item }); - }, - onItemSelect: (item) => { - setCompilation(item); + return h( + Select, + { + items: Object.values(Compilation), + itemRenderer: (item: any, { handleClick }) => { + return h(MenuItem, { onClick: handleClick, text: item }); + }, + onItemSelect: (item) => { + setCompilation(item); + }, + filterable: false, + activeItem: compilation, }, - filterable: false, - activeItem: compilation, - }, [ - h(Button, {text: compilation, rightIcon: "double-caret-vertical", placeholder: "Select a film" }) - ]); -} + [ + h(Button, { + text: compilation, + rightIcon: "double-caret-vertical", + placeholder: "Select a film", + }), + ] + ); +}; const _macrostratStyle = buildMacrostratStyle({ - tileserverDomain + tileserverDomain, }) as mapboxgl.Style; function isStateValid(state) { @@ -121,5 +128,5 @@ function isStateValid(state) { const defaultState = { showLineSymbols: false, compilation: "v2/carto", - lithologies: [] + lithologies: [], }; diff --git a/src/pages/map/map-interface/app-state/handlers/fetch.ts b/src/pages/map/map-interface/app-state/handlers/fetch.ts index 8df9ea0f..c97d4f2a 100644 --- a/src/pages/map/map-interface/app-state/handlers/fetch.ts +++ b/src/pages/map/map-interface/app-state/handlers/fetch.ts @@ -71,7 +71,7 @@ export async function fetchFilteredColumns( // TODO: report errors return { type: "update-column-filters", - columns: res.data, + columns: res.data.features, }; } diff --git a/src/pages/map/map-interface/app-state/handlers/index.ts b/src/pages/map/map-interface/app-state/handlers/index.ts index 1fea0884..061214e4 100644 --- a/src/pages/map/map-interface/app-state/handlers/index.ts +++ b/src/pages/map/map-interface/app-state/handlers/index.ts @@ -43,7 +43,6 @@ async function actionRunner( let coreState = s1.core; const activePage = currentPageForPathName(pathname); - console.log(pathname, "activePage", activePage); // Harvest as much information as possible from the hash string let [coreState1, filters] = getInitialStateFromHash( diff --git a/src/pages/map/map-interface/app-state/hooks.ts b/src/pages/map/map-interface/app-state/hooks.ts index 4c7c39aa..86656376 100644 --- a/src/pages/map/map-interface/app-state/hooks.ts +++ b/src/pages/map/map-interface/app-state/hooks.ts @@ -3,7 +3,6 @@ import actionRunner from "./handlers"; import { useStore, useSelector, useDispatch } from "react-redux"; import { AppState } from "."; import React from "react"; -import { useEffect } from "react"; function useActionDispatch() { return useDispatch>(); @@ -20,55 +19,8 @@ function useAppActions(): (action: AppAction) => Promise { }; } -function useFilterState() { - const { filters, filtersOpen } = useSelector((state) => state.core); - return { filters, filtersOpen }; -} - -function useSearchState() { - return useSelector((state) => { - const { searchResults, isSearching, term, inputFocus, infoDrawerOpen } = - state.core; - return { searchResults, isSearching, term, inputFocus, infoDrawerOpen }; - }); -} - -function useMenuState() { - const { menuOpen, infoDrawerOpen } = useSelector((state) => state.core); - const menu = useSelector((state) => state.menu); - return { menuOpen, infoDrawerOpen, ...menu }; -} - function useAppState(selectorFn: (state: AppState) => T): T { return useSelector(selectorFn) as T; } -interface OutsideClickI { - ref: React.RefObject; - fn: (event: Event) => void; -} - -function useOutsideClick(props: OutsideClickI) { - const { ref, fn } = props; - - useEffect(() => { - function handleOutsideClick(event) { - if (ref.current && !ref.current?.contains(event.target)) { - return fn(event); - } - } - document.addEventListener("mousedown", handleOutsideClick); - return () => { - document.removeEventListener("mousedown", handleOutsideClick); - }; - }, [ref]); -} - -export { - useAppActions, - useFilterState, - useSearchState, - useMenuState, - useAppState, - useOutsideClick, -}; +export { useAppActions, useAppState }; diff --git a/src/pages/map/map-interface/app-state/reducers/core/types.ts b/src/pages/map/map-interface/app-state/reducers/core/types.ts index 1a15a663..5fa7375c 100644 --- a/src/pages/map/map-interface/app-state/reducers/core/types.ts +++ b/src/pages/map/map-interface/app-state/reducers/core/types.ts @@ -41,7 +41,10 @@ type CLOSE_INFODRAWER = { type: "close-infodrawer" }; type TOGGLE_FILTERS = { type: "toggle-filters" }; type REMOVE_FILTER = { type: "remove-filter"; filter: any }; -type UPDATE_COLUMN_FILTERS = { type: "update-column-filters"; columns: any }; +type UPDATE_COLUMN_FILTERS = { + type: "update-column-filters"; + columns: ColumnGeoJSONRecord[]; +}; type CLEAR_FILTERS = { type: "clear-filters" }; type START_MAP_QUERY = { @@ -263,7 +266,7 @@ export interface CoreState extends MapState, AsyncRequestState { mapUse3D: boolean; filtersOpen: boolean; filters: FilterData[]; - filteredColumns: object; + filteredColumns: ColumnGeoJSONRecord[] | null; showExperimentsPanel: boolean; allColumns: ColumnGeoJSONRecord[] | null; data: []; diff --git a/src/pages/map/map-interface/components/filter-panel/index.ts b/src/pages/map/map-interface/components/filter-panel/index.ts index e0093768..e529e55d 100644 --- a/src/pages/map/map-interface/components/filter-panel/index.ts +++ b/src/pages/map/map-interface/components/filter-panel/index.ts @@ -2,8 +2,8 @@ import React, { useState } from "react"; import hyper from "@macrostrat/hyper"; import { Tag, Card, Button, Collapse, Switch } from "@blueprintjs/core"; import { - useFilterState, useAppActions, + useAppState, } from "~/pages/map/map-interface/app-state"; import { useAdmoinshments } from "./admonishments"; import styles from "./filters.module.styl"; @@ -54,7 +54,7 @@ function Filter({ filter }) { } function Filters() { - const { filters } = useFilterState(); + const { filters } = useAppState((state) => state.core.filters); const shouldFiltersBeOpen = filters.length > 0; return h("div.filter-container", [ h.if(!shouldFiltersBeOpen)("div", [ @@ -94,7 +94,7 @@ function makeFilterString(filters) { function FilterPanel() { const [open, setOpen] = useState(false); - const { filters } = useFilterState(); + const filters = useAppState((s) => s.core.filters); const runAction = useAppActions(); const admonishments = useAdmoinshments(); diff --git a/src/pages/map/map-interface/components/info-drawer/index.ts b/src/pages/map/map-interface/components/info-drawer/index.ts index b2301ba7..8bbb0567 100644 --- a/src/pages/map/map-interface/components/info-drawer/index.ts +++ b/src/pages/map/map-interface/components/info-drawer/index.ts @@ -56,8 +56,6 @@ function InfoDrawer(props) { function InfoDrawerInterior(props) { const columnInfo = useAppState((state) => state.core.columnInfo); - console.log("Column info", columnInfo); - return h(Routes, [ h(Route, { path: "/column", element: h(StratColumn, { columnInfo }) }), h(Route, { path: "*", element: h(InfoDrawerMainPanel) }), diff --git a/src/pages/map/map-interface/components/navbar/index.ts b/src/pages/map/map-interface/components/navbar/index.ts index 787eebe9..9de34b4f 100644 --- a/src/pages/map/map-interface/components/navbar/index.ts +++ b/src/pages/map/map-interface/components/navbar/index.ts @@ -3,7 +3,6 @@ import { Navbar, Button, InputGroup, Spinner, Card } from "@blueprintjs/core"; import hyper from "@macrostrat/hyper"; import { useAppActions, - useSearchState, useAppState, useContextPanelOpen, } from "../../app-state"; @@ -71,7 +70,6 @@ function ResultList({ searchResults }) { { key, onClick() { - console.log("Clicked", item); onSelectResult(item); }, }, @@ -85,7 +83,7 @@ function ResultList({ searchResults }) { } function SearchResults({ className }) { - const { searchResults } = useSearchState(); + const searchResults = useAppState((s) => s.core.searchResults); className = classNames(className, "search-results-card"); return h(Card, { className }, h(ResultList, { searchResults })); diff --git a/src/pages/map/map-interface/index.ts b/src/pages/map/map-interface/index.ts index fe6f9351..5c36deec 100644 --- a/src/pages/map/map-interface/index.ts +++ b/src/pages/map/map-interface/index.ts @@ -9,13 +9,18 @@ import "./ui-components.styl"; import { createRouterMiddleware } from "@lagunovsky/redux-react-router"; import { Provider } from "react-redux"; import { applyMiddleware, compose, createStore } from "redux"; -import reducerStack, { Action, AppState, browserHistory } from "./app-state"; +import reducerStack, { AppAction, AppState, browserHistory } from "./app-state"; + +/** Redux is used only for the main map applicaton. This heavy state-management approach is + * essentially a legacy approach, and we are moving away from this in favor of more lightweight + * state management solutions that work on individual pages. + */ const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; const routerMiddleware = createRouterMiddleware(browserHistory); // Create the data store -let store = createStore( +let store = createStore( reducerStack, composeEnhancers(applyMiddleware(routerMiddleware)) ); @@ -29,12 +34,14 @@ export default function MapApp({ routerBasename }) { h( ReduxRouter, { basename: routerBasename, store, history: browserHistory }, - [ - h(Routes, [ - //h(Route, { path: mapPagePrefix + "/sources", element: h(Sources) }), - h(Route, { path: "*", element: h(MapPage) }), - ]), - ] + [h(Routes, [h(Route, { path: "*", element: h(MapPage) })])] ) ); } + +// Extend the window type to include the Redux DevTools types +declare global { + interface Window { + __REDUX_DEVTOOLS_EXTENSION_COMPOSE__: Function | undefined; + } +} diff --git a/src/pages/map/map-interface/map-page/index.ts b/src/pages/map/map-interface/map-page/index.ts index 17ef266d..f7a18c3c 100644 --- a/src/pages/map/map-interface/map-page/index.ts +++ b/src/pages/map/map-interface/map-page/index.ts @@ -78,12 +78,7 @@ export const MapPage = ({ className: "context-panel", menuPage: menuPage ?? navMenuPage, }), - detailPanel: h(Routes, [ - h(Route, { - path: mapPagePrefix + "/loc/:lng/:lat/*", - element: h(InfoDrawerRoute), - }), - ]), + detailPanel: h(InfoDrawerHolder), detailPanelStyle: "floating", bottomPanel: h(ElevationChart, null), contextPanelOpen: contextPanelOpen || inputFocus, @@ -113,14 +108,31 @@ function MapPageRoutes() { ]); } -function InfoDrawerRoute() { +function InfoDrawerHolder() { // We could probably do this in the reducer... - const { lat, lng } = useParams(); const infoDrawerOpen = useAppState((s) => s.core.infoDrawerOpen); + const detailPanelTrans = useTransition(infoDrawerOpen, 800); + + return h([ + // This is essentially a shim implementation of React Router + h(Routes, [ + h(Route, { + path: mapPagePrefix + "/loc/:lng/:lat/*", + element: h(InfoDrawerLocationGrabber), + }), + ]), + h.if(detailPanelTrans.shouldMount)(InfoDrawer, { + className: "detail-panel", + }), + ]); +} + +function InfoDrawerLocationGrabber() { + // We could probably do this in the reducer... + const { lat, lng } = useParams(); const z = Math.round( useAppState((s) => s.core.mapPosition.target?.zoom) ?? 7 ); - const detailPanelTrans = useTransition(infoDrawerOpen, 800); const runAction = useAppActions(); // Todo: this is a pretty janky way to do state management @@ -139,10 +151,7 @@ function InfoDrawerRoute() { }); } }, [lat, lng]); - - return h.if(detailPanelTrans.shouldMount)(InfoDrawer, { - className: "detail-panel", - }); + return null; } export default MapPageRoutes; diff --git a/src/pages/map/map-interface/map-page/map-styles/index.ts b/src/pages/map/map-interface/map-page/map-styles/index.ts index 31053c96..6cc76687 100644 --- a/src/pages/map/map-interface/map-page/map-styles/index.ts +++ b/src/pages/map/map-interface/map-page/map-styles/index.ts @@ -1,5 +1,4 @@ -import { overlayStyle } from "./overlay"; - -export { overlayStyle }; export * from "./map-sources"; export * from "./line-symbols"; +export * from "./paleogeography"; +export * from "./overlay"; diff --git a/src/pages/map/map-interface/map-page/map-styles/map-sources.ts b/src/pages/map/map-interface/map-page/map-styles/map-sources.ts index 2537f59c..a8bf00da 100644 --- a/src/pages/map/map-interface/map-page/map-styles/map-sources.ts +++ b/src/pages/map/map-interface/map-page/map-styles/map-sources.ts @@ -93,7 +93,6 @@ export function MapSourcesLayer() { mergeStyles(map, styles); } else { map.on("style.load", () => { - console.log("Merging styles"); mergeStyles(map, styles); }); } diff --git a/src/pages/map/map-interface/map-page/map-styles/overlay.ts b/src/pages/map/map-interface/map-page/map-styles/overlay.ts index bec73eda..41fbdbbc 100644 --- a/src/pages/map/map-interface/map-page/map-styles/overlay.ts +++ b/src/pages/map/map-interface/map-page/map-styles/overlay.ts @@ -1,91 +1,119 @@ -import { apiV2Prefix } from "@macrostrat-web/settings"; +/** Add extra types we use in this style... */ +interface SourceExt extends mapboxgl.Source { + cluster?: boolean; + clusterRadius?: number; + generateId?: boolean; + data?: any; +} -export const overlayStyle = { - version: 8, - sources: { - // "pbdb": { - // "type": "vector", - // "tiles": [ - // `${SETTINGS.burwellTileDomain}/hexgrid/{z}/{x}/{y}.mvt` - // ], - // "tileSize": 512, - // "maxzoom": 6, - // }, - "pbdb-points": { - type: "geojson", - cluster: true, - clusterRadius: 50, - data: { - type: "FeatureCollection", - features: [], - }, - }, - "pbdb-clusters": { - type: "geojson", - generateId: true, - data: { - type: "FeatureCollection", - features: [], - }, +export function buildOverlayStyle() { + return { + version: 8, + layers: buildOverlayLayers(), + sources: overlaySources, + }; +} + +const overlaySources: { [k: string]: SourceExt } = { + // "pbdb": { + // "type": "vector", + // "tiles": [ + // `${SETTINGS.burwellTileDomain}/hexgrid/{z}/{x}/{y}.mvt` + // ], + // "tileSize": 512, + // "maxzoom": 6, + // }, + "pbdb-points": { + type: "geojson", + cluster: true, + clusterRadius: 50, + data: { + type: "FeatureCollection", + features: [], }, - info_marker: { - type: "geojson", - data: { - type: "FeatureCollection", - features: [ - { - type: "Feature", - geometry: { - type: "Point", - coordinates: [0, 0], - }, - }, - ], - }, + }, + "pbdb-clusters": { + type: "geojson", + generateId: true, + data: { + type: "FeatureCollection", + features: [], }, - columns: { - type: "geojson", - generateId: true, - data: `${apiV2Prefix}/columns?all&format=geojson_bare`, + }, + columns: { + type: "geojson", + generateId: true, + data: { + type: "FeatureCollection", + features: [], }, - filteredColumns: { - type: "geojson", - generateId: true, - data: { - type: "FeatureCollection", - features: [], - }, + }, + filteredColumns: { + type: "geojson", + generateId: true, + data: { + type: "FeatureCollection", + features: [], }, - elevationPoints: { - type: "geojson", - data: { - type: "FeatureCollection", - features: [], - }, + }, + crossSectionEndpoints: { + type: "geojson", + data: { + type: "FeatureCollection", + features: [], }, - elevationLine: { - type: "geojson", - data: { - type: "FeatureCollection", - features: [], - }, + }, + crossSectionLine: { + type: "geojson", + data: { + type: "FeatureCollection", + features: [], }, - elevationMarker: { - type: "geojson", - data: { - type: "FeatureCollection", - features: [], - }, + }, + elevationMarker: { + type: "geojson", + data: { + type: "FeatureCollection", + features: [], }, }, - layers: [ +}; + +export function buildOverlayLayers(): mapboxgl.Layer[] { + // Get CSS colors from settings + const ruleColor = getComputedStyle(document.body).getPropertyValue( + "--panel-background-color" + ); + + const centerColor = getComputedStyle(document.body).getPropertyValue( + "--panel-rule-color" + ); + + const crossSectionPointPaint = { + "circle-radius": { + stops: [ + [0, 3], + [12, 5], + ], + }, + "circle-color": centerColor, + "circle-stroke-width": { + stops: [ + [0, 2], + [12, 4], + ], + }, + "circle-stroke-color": ruleColor, + }; + + return [ { id: "column_fill", type: "fill", source: "columns", paint: { - "fill-color": "#777777", - "fill-opacity": 0.2, + "fill-color": centerColor, + "fill-opacity": 0.3, }, layout: { visibility: "none", @@ -96,11 +124,13 @@ export const overlayStyle = { type: "line", source: "columns", paint: { - "line-color": "#777777", + "line-color": ruleColor, + "line-opacity": 0.75, "line-width": { stops: [ - [0, 0.2], - [10, 1], + [0, 0.5], + [4, 1], + [10, 2], ], }, }, @@ -138,53 +168,33 @@ export const overlayStyle = { }, }, { - id: "infoMarker", - type: "symbol", - source: "info_marker", - layout: { - "icon-size": 0.65, - "icon-image": "pin", - "icon-offset": [0, -28], - visibility: "none", - "icon-allow-overlap": true, - }, - }, - { - id: "elevationLine", + id: "crossSectionLine", type: "line", - source: "elevationLine", + source: "crossSectionLine", paint: { - "line-dasharray": [4, 2], "line-width": { stops: [ - [0, 3], - [12, 5], + [0, 1], + [12, 3], ], }, - "line-color": "#ffffff", + "line-color": ruleColor, "line-opacity": 1, }, }, { - id: "elevationPoint", + id: "crossSectionEndpoint", type: "circle", - source: "elevationPoints", - paint: { - "circle-radius": 6, - "circle-color": "#ffffff", - "circle-stroke-width": 1, - "circle-stroke-color": "#333333", - }, + source: "crossSectionEndpoints", + paint: crossSectionPointPaint, }, { id: "elevationMarker", type: "circle", source: "elevationMarker", paint: { - "circle-radius": 8, + ...crossSectionPointPaint, "circle-color": "#4bc0c0", - "circle-stroke-width": 2, - "circle-stroke-color": "#dcdcdc", }, }, // { @@ -314,5 +324,5 @@ export const overlayStyle = { "circle-stroke-color": "#fff", }, }, - ], -}; + ]; +} diff --git a/src/pages/map/map-interface/map-page/map-styles/paleogeography.ts b/src/pages/map/map-interface/map-page/map-styles/paleogeography.ts new file mode 100644 index 00000000..f68e3ad8 --- /dev/null +++ b/src/pages/map/map-interface/map-page/map-styles/paleogeography.ts @@ -0,0 +1,88 @@ +import chroma from "chroma-js"; +import { mergeStyles } from "@macrostrat/mapbox-utils"; + +export function applyAgeModelStyles(baseStyle, mapStyle, opts) { + const { + model, + age, + inDarkMode = false, + tileserverDomain = "https://macrostrat.org/tiles", + } = opts; + + let mapTileURL = tileserverDomain + "/carto-slim/{z}/{x}/{y}"; + if (age != null) { + mapTileURL = + tileserverDomain + + `/carto-slim-rotated/{z}/{x}/{y}?model_id=${model}&t_step=${age}`; + } + + let color = chroma("rgb(180, 180, 200)"); + let ageSpan = 4500; + for (let interval of intervals) { + let intAgeSpan = interval.eag - interval.lag; + if (interval.eag > age && interval.lag < age && intAgeSpan < ageSpan) { + color = chroma(interval.col); + } + } + + const newBaseStyle = { + ...baseStyle, + sources: {}, + layers: [], + }; + + const overlays = { + ...mapStyle, + //layers: mapStyle.layers.filter((l) => !l.id.startsWith("column")), + }; + + let styles = mergeStyles( + newBaseStyle, + { + version: 8, + layers: [ + { + id: "plate-polygons", + type: "fill", + source: "burwell", + "source-layer": "plates", + paint: { + "fill-color": inDarkMode ? "rgb(60,60,70)" : "rgb(170,170,200)", + "fill-outline-color": inDarkMode + ? "rgb(70, 70, 80)" + : "rgb(150,150,150)", + }, + }, + { + id: "land", + type: "fill", + source: "burwell", + "source-layer": "land", + paint: { + "fill-color": inDarkMode ? "rgb(80,80,90)" : "rgb(200,200,203)", + }, + }, + // { + // id: "column_outline", + // type: "line", + // source: "burwell", + // "source-layer": "columns", + // paint: { + // "line-color": color.css(), + // "line-width": 1.5, + // "line-opacity": 0.8, + // }, + // }, + ], + }, + overlays + ); + + styles.sources.burwell = { + type: "vector", + tiles: [mapTileURL], + tileSize: 512, + }; + + return styles; +} diff --git a/src/pages/map/map-interface/map-page/map-view/__archive.ts b/src/pages/map/map-interface/map-page/map-view/__archive.ts index c4f616e1..dd03cd02 100644 --- a/src/pages/map/map-interface/map-page/map-view/__archive.ts +++ b/src/pages/map/map-interface/map-page/map-view/__archive.ts @@ -97,74 +97,3 @@ class VestigialMap extends Component { } } } - -function setMapStyle(class_, map, mapStyle, props) { - const prevMapLayers = class_.props.mapLayers; - const { mapLayers } = props; - - mapStyle.layers.forEach((layer) => { - if (map.getSource(layer.source) && map.getLayer(layer.id)) { - const visibility = map.getLayoutProperty(layer.id, "visibility"); - if (layer.source === "burwell" && layer["source-layer"] === "units") { - const showBedRock = mapLayers.has(MapLayer.BEDROCK) - ? "visible" - : "none"; - if (visibility !== showBedRock) { - map.setLayoutProperty(layer.id, "visibility", showBedRock); - } - } else if ( - layer.source === "burwell" && - layer["source-layer"] === "lines" - ) { - const showLines = mapLayers.has(MapLayer.LINES) ? "visible" : "none"; - if (visibility !== showLines) { - map.setLayoutProperty(layer.id, "visibility", showLines); - } - } else if ( - layer.source === "pbdb-points" || - layer.source === "pbdb-clusters" - ) { - // points and clusters are visible at different zooms - // currently this difference is handled by refreshPBDB() - // it's annoying but doesn't cause an infinite loop - const hasFossils = mapLayers.has(MapLayer.FOSSILS); - if ( - class_.props.mapLayers.has(MapLayer.FOSSILS) != hasFossils && - hasFossils - ) { - class_.refreshPBDB(); - } else { - map.setLayoutProperty( - layer.id, - "visibility", - hasFossils ? "visible" : "none" - ); - } - } else if (layer.source === "columns") { - const showColumns = - mapLayers.has(MapLayer.COLUMNS) && !props.filters.length - ? "visible" - : "none"; - if (visibility !== showColumns) { - map.setLayoutProperty(layer.id, "visibility", showColumns); - } - } else if (layer.source === "filteredColumns") { - const showFilteredColumns = - mapLayers.has(MapLayer.COLUMNS) && props.filters.length - ? "visible" - : "none"; - if ( - JSON.stringify(props.filteredColumns) != - JSON.stringify(class_.props.filteredColumns) - ) { - map.getSource("filteredColumns").setData(props.filteredColumns); - } - if (visibility != showFilteredColumns) { - map.setLayoutProperty(layer.id, "visibility", showFilteredColumns); - } - } - } - }); -} - -export { setMapStyle }; diff --git a/src/pages/map/map-interface/map-page/map-view/index.ts b/src/pages/map/map-interface/map-page/map-view/index.ts index 75c9cef8..6f7a0ee7 100644 --- a/src/pages/map/map-interface/map-page/map-view/index.ts +++ b/src/pages/map/map-interface/map-page/map-view/index.ts @@ -5,14 +5,13 @@ import { PositionFocusState, getFocusState, useMapLabelVisibility, - useMapPosition, useMapRef, useMapStatus, + useMapStyleOperator, } from "@macrostrat/mapbox-react"; import { MacrostratLineSymbolManager, MapSourcesLayer, - applyAgeModelStyles, buildMacrostratStyle, } from "@macrostrat/mapbox-styles"; import { getMapboxStyle, mergeStyles } from "@macrostrat/mapbox-utils"; @@ -32,6 +31,7 @@ import { MacrostratLayerManager, } from "./map"; import { getBaseMapStyle } from "@macrostrat-web/map-utils"; +import { buildOverlayStyle, applyAgeModelStyles } from "../map-styles"; const h = hyper.styled(styles); @@ -65,22 +65,24 @@ export default function MainMapView(props) { const [baseStyle, setBaseStyle] = useState(null); const mapStyle = useMemo(() => { if (baseStyle == null) return null; - const overlayStyles = buildMacrostratStyle({ + const macrostratStyle = buildMacrostratStyle({ focusedMap: focusedMapSource, tileserverDomain: SETTINGS.burwellTileDomain, }); + const overlayStyle = buildOverlayStyle(); + if (timeCursorAge != null) { - return applyAgeModelStyles(baseStyle, overlayStyles, { + return applyAgeModelStyles(baseStyle, macrostratStyle, { age: timeCursorAge, model: plateModelId ?? 1, baseStyle, - overlayStyles, + overlayStyles: overlayStyle, isDarkMode, tileserverDomain: SETTINGS.burwellTileDomain, }); } - return mergeStyles(baseStyle, overlayStyles); + return mergeStyles(baseStyle, macrostratStyle, overlayStyle); }, [baseStyle, timeCursorAge, plateModelId, isDarkMode, focusedMapSource]); useEffect(() => { @@ -133,6 +135,7 @@ export default function MainMapView(props) { return h( MapView, { + projection: { name: "globe" }, ...props, infoMarkerPosition, onMapLoaded, @@ -161,28 +164,19 @@ export default function MainMapView(props) { function ColumnDataManager() { /* Update columns map layer given columns provided by application. */ - const mapRef = useMapRef(); - const { isInitialized } = useMapStatus(); const allColumns = useAppState((state) => state.core.allColumns); - useEffect(() => { - const map = mapRef.current; - const ncols = allColumns?.length ?? 0; - if (map == null || ncols == 0) return; - // Set source data for columns - map.once("style.load", () => { - const src = map.getSource("columns"); - if (src == null) return; - src.setData({ + useMapStyleOperator( + (map) => { + const ncols = allColumns?.length ?? 0; + if (ncols == 0) return; + const source = map.getSource("columns"); + if (source == null) return; + source.setData({ type: "FeatureCollection", - features: allColumns ?? [], + features: allColumns, }); - }); - const src = map.getSource("columns"); - if (src == null) return; - src.setData({ - type: "FeatureCollection", - features: allColumns ?? [], - }); - }, [mapRef.current, allColumns, isInitialized]); + }, + [allColumns] + ); return null; } diff --git a/src/pages/map/map-interface/map-page/map-view/map.ts b/src/pages/map/map-interface/map-page/map-view/map.ts index 1238f832..623b39cc 100644 --- a/src/pages/map/map-interface/map-page/map-view/map.ts +++ b/src/pages/map/map-interface/map-page/map-view/map.ts @@ -5,7 +5,11 @@ import { useAppActions, } from "~/pages/map/map-interface/app-state"; import { ColumnProperties } from "~/pages/map/map-interface/app-state/handlers/columns"; -import { useMapRef, useMapStatus } from "@macrostrat/mapbox-react"; +import { + useMapRef, + useMapStatus, + useMapStyleOperator, +} from "@macrostrat/mapbox-react"; import { useEffect, useRef, useCallback } from "react"; import { useAppState } from "~/pages/map/map-interface/app-state"; import { getExpressionForFilters } from "./filter-helpers"; @@ -71,7 +75,6 @@ function handleFossilLayerClick( }); // Need to recolor on selection somehow - d; return { type: "get-pbdb", collection_nos: pointsInCluster, @@ -138,7 +141,6 @@ function useMapClickHandler(pbdbPoints) { // If we are viewing fossils, prioritize clicks on those if (mapLayers.has(MapLayer.FOSSILS)) { const action = handleFossilLayerClick(event, map, pbdbPoints.current); - console.log(action); if (action != null) { if (action.type === "zoom-map") { map.zoomTo(map.getZoom() + action.dz, { center: event.lngLat }); @@ -218,16 +220,13 @@ export async function refreshPBDB(map, pointsRef, filters) { } export function MacrostratLayerManager() { + /** Manager for map layers */ const mapRef = useMapRef(); - const { isStyleLoaded } = useMapStatus(); const filters = useAppState((s) => s.core.filters); const mapLayers = useAppState((s) => s.core.mapLayers); const filteredColumns = useAppState((s) => s.core.filteredColumns); const runAction = useAppActions(); - const map = mapRef.current; - // This selection tracking used to be used for PBDB but I think no longer is - const selectedFeatures = useRef({}); const pbdbPoints = useRef({}); useEffect(() => { @@ -237,74 +236,28 @@ export function MacrostratLayerManager() { runAction({ type: "map-layers-changed", mapLayers }); }, [filters, mapLayers]); - // Filters - useEffect(() => { - const map = mapRef.current; - if (map == null || !isStyleLoaded) return; - - const source = map.getSource("filteredColumns"); - if (filteredColumns != null) { - console.log(filteredColumns); - source?.setData(filteredColumns); - } - - const expr = getExpressionForFilters(filters); - map.setFilter("burwell_fill", expr); - map.setFilter("burwell_stroke", expr); - }, [filters, isStyleLoaded, mapRef.current]); - - const styleLoadedCallback = useCallback(() => { - const map = mapRef.current; - if (map == null) return; - if (!map.isStyleLoaded()) return; - const style = map.getStyle(); - for (const layer of style.layers) { - selectedFeatures.current[layer.id] = null; - - if (!("source" in layer)) continue; - - if (layer.source === "burwell" && layer["source-layer"] === "units") { - setVisibility(map, layer.id, mapLayers.has(MapLayer.BEDROCK)); - } - if (layer.source === "burwell" && layer["source-layer"] === "lines") { - setVisibility(map, layer.id, mapLayers.has(MapLayer.LINES)); - } - if ( - layer.source === "pbdb" || - layer.source === "pbdb-points" || - layer.source === "pbdb-clusters" - ) { - setVisibility(map, layer.id, mapLayers.has(MapLayer.FOSSILS)); - } - if (layer.source === "columns") { - setVisibility( - map, - layer.id, - mapLayers.has(MapLayer.COLUMNS) && filters.length === 0 - ); - } - - if (layer.source === "filteredColumns") { - setVisibility( - map, - layer.id, - mapLayers.has(MapLayer.COLUMNS) && filters.length !== 0 - ); - } - } + // Update filtered columns + useMapStyleOperator( + (map) => { + const source = map.getSource("filteredColumns") as mapboxgl.GeoJSONSource; + source?.setData({ + type: "FeatureCollection", + features: filteredColumns ?? [], + }); + }, + [filteredColumns] + ); - if (mapLayers.has(MapLayer.FOSSILS)) { - refreshPBDB(map, pbdbPoints, filters); - } - }, [mapLayers, filters]); + useMapStyleOperator( + (map) => { + const expr = getExpressionForFilters(filters); + map.setFilter("burwell_fill", expr); + map.setFilter("burwell_stroke", expr); + }, + [filters] + ); - useEffect(() => { - styleLoadedCallback(); - mapRef.current?.on("style.load", styleLoadedCallback); - return () => { - mapRef.current?.off("style.load", styleLoadedCallback); - }; - }, [mapRef.current, styleLoadedCallback]); + useStyleReloader(pbdbPoints); // Map click handler const mapClickHandler = useMapClickHandler(pbdbPoints); @@ -336,6 +289,58 @@ export function MacrostratLayerManager() { return null; } +function useStyleReloader(pbdbPoints) { + // This selection tracking used to be used for PBDB but I think no longer is + const selectedFeatures = useRef({}); + const filters = useAppState((s) => s.core.filters); + const mapLayers = useAppState((s) => s.core.mapLayers); + + return useMapStyleOperator( + (map) => { + const style = map.getStyle(); + for (const layer of style.layers) { + selectedFeatures.current[layer.id] = null; + + if (!("source" in layer)) continue; + + if (layer.source === "burwell" && layer["source-layer"] === "units") { + setVisibility(map, layer.id, mapLayers.has(MapLayer.BEDROCK)); + } + if (layer.source === "burwell" && layer["source-layer"] === "lines") { + setVisibility(map, layer.id, mapLayers.has(MapLayer.LINES)); + } + if ( + layer.source === "pbdb" || + layer.source === "pbdb-points" || + layer.source === "pbdb-clusters" + ) { + setVisibility(map, layer.id, mapLayers.has(MapLayer.FOSSILS)); + } + if (layer.source === "columns") { + setVisibility( + map, + layer.id, + mapLayers.has(MapLayer.COLUMNS) && filters.length === 0 + ); + } + + if (layer.source === "filteredColumns") { + setVisibility( + map, + layer.id, + mapLayers.has(MapLayer.COLUMNS) && filters.length !== 0 + ); + } + } + + if (mapLayers.has(MapLayer.FOSSILS)) { + refreshPBDB(map, pbdbPoints, filters); + } + }, + [mapLayers, filters] + ); +} + function setVisibility(map, layerID, visible) { const visibility = visible ? "visible" : "none"; map.setLayoutProperty(layerID, "visibility", visibility); diff --git a/src/pages/map/map-interface/map-page/menu.ts b/src/pages/map/map-interface/map-page/menu.ts index 09407b20..ded83703 100644 --- a/src/pages/map/map-interface/map-page/menu.ts +++ b/src/pages/map/map-interface/map-page/menu.ts @@ -20,7 +20,6 @@ import { useAppActions, useAppState, useHashNavigate, - useSearchState, } from "../app-state"; import { isDetailPanelRouteInternal, @@ -230,7 +229,7 @@ type MenuProps = { const Menu = (props: MenuProps) => { let { className, menuPage, baseRoute = "/" } = props; - const { inputFocus } = useSearchState(); + const inputFocus = useAppState((s) => s.core.inputFocus); const runAction = useAppActions(); const navigateHome = useHashNavigate(baseRoute); diff --git a/src/pages/map/map-interface/performance/core.ts b/src/pages/map/map-interface/performance/core.ts index 097cfb59..7b8af8b3 100644 --- a/src/pages/map/map-interface/performance/core.ts +++ b/src/pages/map/map-interface/performance/core.ts @@ -111,7 +111,6 @@ export function usePerformanceWatcher( const observerRef = useRef(); const callback = useCallback( (data: PerformanceObserverEntryList) => { - console.log("Adding performance data"); dispatch({ type: "add-performance-data", data: data.getEntries().map(buildPerformanceData), @@ -122,7 +121,6 @@ export function usePerformanceWatcher( ); useEffect(() => { - console.log("Creating performance observer"); const observer = new PerformanceObserver(callback); observer.observe({ entryTypes: ["resource"] }); observerRef.current = observer; diff --git a/tsconfig.json b/tsconfig.json index 702c53fd..3be27155 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,56 +1,15 @@ { "compilerOptions": { - "composite": true, "baseUrl": "./", - "lib": ["DOM", "DOM.Iterable", "ESNext"], - "strict": false, - "sourceMap": true, - "declaration": true, - "jsx": "react-jsx", - "skipLibCheck": true, + // Allow importing of JSON and default imports from CommonJS modules "esModuleInterop": true, - "downlevelIteration": true, - "module": "ESNext", "paths": { - "~/*": ["./src/*"], - "@macrostrat/cesium-vector-provider": [ - "./packages/cesium-vector-provider/src/" - ], - "@macrostrat/ui-components": [ - "deps/web-components/packages/ui-components/src/" - ], - "@macrostrat/column-components": [ - "deps/web-components/packages/column-components/src/" - ], - "@macrostrat/map-components": [ - "deps/web-components/packages/map-components/src/" - ], - "@macrostrat/mapbox-styles": [ - "deps/web-components/packages/mapbox-styles/src" - ], - "@macrostrat/column-views": [ - "deps/web-components/packages/column-views/src" - ], - "@macrostrat/mapbox-react": [ - "deps/web-components/packages/mapbox-react/src" - ], - "@macrostrat/mapbox-utils": [ - "deps/web-components/packages/mapbox-utils/src" - ], - "@macrostrat/map-interface": [ - "deps/web-components/packages/map-interface/src" - ], + // Workspace code "@macrostrat/cesium-viewer": ["deps/cesium-viewer/src"], - "@macrostrat-web/settings": ["packages/settings"] + "@macrostrat/cesium-martini": ["deps/cesium-martini/src"], + "@macrostrat/*": ["deps/web-components/packages/*/src"], + "@macrostrat-web/*": ["packages/*/src"], + "~/*": ["src/*"] } - }, - "include": [ - "src", - "packages/lithology-hierarchy/src/simple-hierarchy.ts", - "packages/hierarchy" - ], - "ts-node": { - "transpileOnly": true, - "esm": true } } diff --git a/yarn.lock b/yarn.lock index a8f1c11a..aa87992d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6581,12 +6581,9 @@ __metadata: react-router-hash-link: "npm:^2.4.3" reduce-reducers: "npm:^1.0.4" redux: "npm:^4.0.5" - regl: "npm:^1.5.0" - resium: "npm:^1.13.1" sass: "npm:^1.49.0" sirv: "npm:^2.0.3" stylus: "npm:^0.55.0" - supports-color: "npm:^9.4.0" swagger-ui-react: "npm:^5.12.3" topojson-client: "npm:^3.0.0" transition-hook: "npm:^1.5.2" @@ -32487,13 +32484,6 @@ __metadata: languageName: node linkType: hard -"regl@npm:^1.5.0": - version: 1.7.0 - resolution: "regl@npm:1.7.0" - checksum: 10/085b9df406c1b2564999a13d2e323158b2b9d608b52b9b2ceae209e88885fde70d5059abf3ca2474290790d2b4058288321b7d323ef4dfdb557336252ae5515c - languageName: node - linkType: hard - "regl@npm:^2.1.0": version: 2.1.0 resolution: "regl@npm:2.1.0" @@ -35304,13 +35294,6 @@ __metadata: languageName: node linkType: hard -"supports-color@npm:^9.4.0": - version: 9.4.0 - resolution: "supports-color@npm:9.4.0" - checksum: 10/cb8ff8daeaf1db642156f69a9aa545b6c01dd9c4def4f90a49f46cbf24be0c245d392fcf37acd119cd1819b99dad2cc9b7e3260813f64bcfd7f5b18b5a1eefb8 - languageName: node - linkType: hard - "supports-hyperlinks@npm:^1.0.1": version: 1.0.1 resolution: "supports-hyperlinks@npm:1.0.1"