terriajs
Version:
Geospatial data visualization platform.
278 lines • 16.1 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { action } from "mobx";
import { observer } from "mobx-react";
import Slider from "rc-slider";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import styled from "styled-components";
import SplitDirection from "terriajs-cesium/Source/Scene/SplitDirection";
import CatalogMemberMixin from "../../../ModelMixins/CatalogMemberMixin";
import MappableMixin from "../../../ModelMixins/MappableMixin";
import Cesium from "../../../Models/Cesium";
import ViewerMode, { MapViewers, getViewerType, isViewerMode, setViewerMode } from "../../../Models/ViewerMode";
import Box from "../../../Styled/Box";
import Button, { RawButton } from "../../../Styled/Button";
import Checkbox from "../../../Styled/Checkbox";
import { GLYPHS, StyledIcon } from "../../../Styled/Icon";
import Spacing from "../../../Styled/Spacing";
import Text, { TextSpan } from "../../../Styled/Text";
import { useViewState } from "../../Context";
import { useRefForTerria } from "../../Hooks/useRefForTerria";
import MenuPanel from "../../StandardUserInterface/customizable/MenuPanel";
import Styles from "./setting-panel.scss";
const sides = {
left: "settingPanel.terrain.left",
both: "settingPanel.terrain.both",
right: "settingPanel.terrain.right"
};
const SettingPanel = observer(() => {
const { t } = useTranslation();
const viewState = useViewState();
const { terria } = viewState;
const settingButtonRef = useRefForTerria(SETTING_PANEL_NAME, viewState);
const [hoverBaseMap, setHoverBaseMap] = useState();
const activeMap = hoverBaseMap ?? terria.mainViewer.baseMap;
const activeMapName = activeMap ? mapDisplayName(activeMap) : "(None)";
const selectBaseMap = (baseMap, event) => {
event.stopPropagation();
if (!MappableMixin.isMixedInto(baseMap))
return;
const currentViewerMode = terria.mainViewer.viewerMode;
const newViewerMode = baseMap.preferredViewerMode
? getViewerType(baseMap.preferredViewerMode)
: undefined;
terria.mainViewer.setBaseMap(baseMap).then(() => {
const switchedViewerMode = newViewerMode &&
currentViewerMode &&
newViewerMode !== currentViewerMode &&
terria.mainViewer.viewerMode === newViewerMode;
if (switchedViewerMode) {
notifyViewerModeSwitch(baseMap, currentViewerMode, newViewerMode, terria, t);
}
});
// We store the user's chosen basemap for future use, but it's up to the instance to decide
// whether to use that at start up.
if (baseMap) {
saveBaseMapPreference(terria, baseMap);
}
};
const mouseEnterBaseMap = (baseMap) => {
setHoverBaseMap(baseMap.item);
};
const mouseLeaveBaseMap = () => {
setHoverBaseMap(undefined);
};
const selectViewer = action((viewer, event) => {
const mainViewer = terria.mainViewer;
event.stopPropagation();
showTerrainOnSide(sides.both, undefined);
setViewerMode(viewer, mainViewer);
// Ensure a base map that is compatible with the new viewer mode
const prevBaseMap = mainViewer.baseMap;
mainViewer.useViewerCompatibleBaseMap().then((baseMapChanged) => {
if (baseMapChanged && mainViewer.baseMap) {
saveBaseMapPreference(terria, mainViewer.baseMap);
if (prevBaseMap) {
notifyBaseMapSwitch(terria, mainViewer, prevBaseMap, mainViewer.baseMap, t);
}
}
});
// We store the user's chosen viewer mode for future use.
terria.setLocalProperty("viewermode", viewer);
terria.currentViewer.notifyRepaintRequired();
});
const showTerrainOnSide = action((side, event) => {
event?.stopPropagation();
switch (side) {
case sides.left:
terria.terrainSplitDirection = SplitDirection.LEFT;
terria.showSplitter = true;
break;
case sides.right:
terria.terrainSplitDirection = SplitDirection.RIGHT;
terria.showSplitter = true;
break;
case sides.both:
terria.terrainSplitDirection = SplitDirection.NONE;
break;
}
terria.currentViewer.notifyRepaintRequired();
});
const toggleDepthTestAgainstTerrainEnabled = action((event) => {
event.stopPropagation();
terria.depthTestAgainstTerrainEnabled =
!terria.depthTestAgainstTerrainEnabled;
terria.currentViewer.notifyRepaintRequired();
});
const onBaseMaximumScreenSpaceErrorChange = (bmsse) => {
terria.setBaseMaximumScreenSpaceError(bmsse);
terria.setLocalProperty("baseMaximumScreenSpaceError", bmsse.toString());
};
const toggleUseNativeResolution = () => {
terria.setUseNativeResolution(!terria.useNativeResolution);
terria.setLocalProperty("useNativeResolution", terria.useNativeResolution);
};
const qualityLabels = {
0: t("settingPanel.qualityLabels.maximumPerformance"),
1: t("settingPanel.qualityLabels.balancedPerformance"),
2: t("settingPanel.qualityLabels.lowerPerformance")
};
const currentViewer = terria.mainViewer.viewerMode === ViewerMode.Cesium
? terria.mainViewer.viewerOptions.useTerrain
? "3d"
: "3dsmooth"
: "2d";
const useNativeResolution = terria.useNativeResolution;
const nativeResolutionLabel = t("settingPanel.nativeResolutionLabel", {
resolution1: useNativeResolution
? t("settingPanel.native")
: t("settingPanel.screen"),
resolution2: useNativeResolution
? t("settingPanel.screen")
: t("settingPanel.native")
});
const dropdownTheme = {
inner: Styles.dropdownInner,
icon: "map"
};
const isCesiumWithTerrain = terria.mainViewer.viewerMode === ViewerMode.Cesium &&
terria.mainViewer.viewerOptions.useTerrain &&
terria.currentViewer &&
terria.currentViewer instanceof Cesium &&
terria.currentViewer.scene &&
terria.currentViewer.scene.globe;
const supportsDepthTestAgainstTerrain = isCesiumWithTerrain;
const depthTestAgainstTerrainEnabled = supportsDepthTestAgainstTerrain && terria.depthTestAgainstTerrainEnabled;
const depthTestAgainstTerrainLabel = depthTestAgainstTerrainEnabled
? t("settingPanel.terrain.showUndergroundFeatures")
: t("settingPanel.terrain.hideUndergroundFeatures");
if (terria.configParameters.useCesiumIonTerrain ||
terria.configParameters.cesiumTerrainUrl) {
MapViewers["3d"].available = true;
}
const supportsSide = isCesiumWithTerrain;
let currentSide = sides.both;
if (supportsSide) {
switch (terria.terrainSplitDirection) {
case SplitDirection.LEFT:
currentSide = sides.left;
break;
case SplitDirection.RIGHT:
currentSide = sides.right;
break;
}
}
const timelineStack = terria.timelineStack;
const alwaysShowTimelineLabel = timelineStack.alwaysShowingTimeline
? t("settingPanel.timeline.alwaysShowLabel")
: t("settingPanel.timeline.hideLabel");
const baseMapStatusMessage = terria.mainViewer.baseMap &&
getBaseMapStatusMessage(terria.mainViewer.baseMap, t);
return (
//@ts-expect-error - not yet ready to tackle tsfying MenuPanel
_jsx(MenuPanel, { theme: dropdownTheme, btnRef: settingButtonRef, btnTitle: t("settingPanel.btnTitle"), btnText: t("settingPanel.btnText"), viewState: viewState, smallScreen: viewState.useSmallScreenInterface, isOpen: viewState.settingsPanelIsVisible, onOpenChanged: action((isOpen) => {
viewState.settingsPanelIsVisible = isOpen;
}), children: _jsxs(Box, { paddedRatio: 3, column: true, children: [_jsx(Box, { paddedVertically: 1, children: _jsx(Text, { as: "label", children: t("settingPanel.mapView") }) }), _jsx(FlexGrid, { gap: 1, elementsNo: 3, children: Object.entries(MapViewers).map(([key, viewerMode]) => (_jsx(SettingsButton, { isActive: key === currentViewer, onClick: (event) => selectViewer(key, event), children: _jsx(Text, { mini: true, children: t(viewerMode.label) }) }, key))) }), !!supportsSide && (_jsxs(_Fragment, { children: [_jsx(Spacing, { bottom: 2 }), _jsxs(Box, { column: true, children: [_jsx(Box, { paddedVertically: 1, children: _jsx(Text, { as: "label", children: t("settingPanel.terrain.sideLabel") }) }), _jsx(FlexGrid, { gap: 1, elementsNo: 3, children: Object.values(sides).map((side) => (_jsx(SettingsButton, { isActive: side === currentSide, onClick: (event) => showTerrainOnSide(side, event), children: _jsx(Text, { mini: true, children: t(side) }) }, side))) })] }), !!supportsDepthTestAgainstTerrain && (_jsxs(_Fragment, { children: [_jsx(Spacing, { bottom: 2 }), _jsx(Checkbox, { textProps: { small: true }, id: "depthTestAgainstTerrain", title: depthTestAgainstTerrainLabel, isChecked: depthTestAgainstTerrainEnabled, onChange: toggleDepthTestAgainstTerrainEnabled, children: _jsx(TextSpan, { children: t("settingPanel.terrain.hideUnderground") }) })] }))] })), _jsxs(_Fragment, { children: [_jsx(Spacing, { bottom: 2 }), _jsxs(Box, { column: true, children: [_jsx(Box, { paddedVertically: 1, children: _jsx(Text, { as: "label", children: t("settingPanel.baseMap") }) }), _jsx(Box, { paddedVertically: 1, children: _jsx(Text, { as: "label", mini: true, children: activeMapName }) }), _jsx(FlexGrid, { gap: 1, elementsNo: 4, children: terria.baseMapsModel.baseMapItems.map((baseMap, i) => (_jsxs(StyledBasemapButton, { isActive: baseMap.item === terria.mainViewer.baseMap, onClick: (event) => selectBaseMap(baseMap.item, event), onMouseEnter: () => mouseEnterBaseMap(baseMap), onMouseLeave: mouseLeaveBaseMap, onFocus: () => mouseEnterBaseMap(baseMap), "data-test-id": `baseMap-${i}`, "data-current-basemap": baseMap.item === terria.mainViewer.baseMap
? true
: undefined, children: [baseMap.item === terria.mainViewer.baseMap ? (_jsx(Box, { position: "absolute", topRight: true, children: _jsx(StyledIcon, { light: true, glyph: GLYPHS.selected, styledWidth: "22px" }) })) : null, _jsx(StyledImage, { fullWidth: true, alt: baseMap.item ? baseMap.item.name : "", src: baseMap.image })] }, baseMap.item?.uniqueId))) }), baseMapStatusMessage && (_jsx(BaseMapStatus, { children: baseMapStatusMessage }))] })] }), _jsxs(_Fragment, { children: [_jsx(Spacing, { bottom: 2 }), _jsxs(Box, { column: true, children: [_jsx(Box, { paddedVertically: 1, children: _jsx(Text, { as: "label", children: t("settingPanel.timeline.title") }) }), _jsx(Checkbox, { textProps: { small: true }, id: "alwaysShowTimeline", isChecked: timelineStack.alwaysShowingTimeline, title: alwaysShowTimelineLabel, onChange: () => {
timelineStack.setAlwaysShowTimeline(!timelineStack.alwaysShowingTimeline);
}, children: _jsx(TextSpan, { children: t("settingPanel.timeline.alwaysShow") }) })] })] }), terria.mainViewer.viewerMode !== ViewerMode.Leaflet && (_jsxs(_Fragment, { children: [_jsx(Spacing, { bottom: 2 }), _jsxs(Box, { column: true, children: [_jsx(Box, { paddedVertically: 1, children: _jsx(Text, { as: "label", children: t("settingPanel.imageOptimisation") }) }), _jsx(Checkbox, { textProps: { small: true }, id: "mapUseNativeResolution", isChecked: useNativeResolution, title: nativeResolutionLabel, onChange: () => toggleUseNativeResolution(), children: _jsx(TextSpan, { children: t("settingPanel.nativeResolutionHeader") }) }), _jsx(Spacing, { bottom: 2 }), _jsx(Box, { paddedVertically: 1, children: _jsx(Text, { as: "label", children: t("settingPanel.mapQuality") }) }), _jsxs(Box, { verticalCenter: true, children: [_jsx(Text, { mini: true, children: t("settingPanel.qualityLabel") }), _jsx(Slider, { min: 1, max: 3, step: 0.1, value: terria.baseMaximumScreenSpaceError, onChange: (val) => onBaseMaximumScreenSpaceErrorChange(val), marks: { 2: "" }, "aria-valuetext": qualityLabels, css: `
margin: 0 10px;
margin-top: 5px;
` }), _jsx(Text, { mini: true, children: t("settingPanel.performanceLabel") })] })] })] }))] }) }));
});
/**
* Return name + CRS if the base map specifies one
*/
function mapDisplayName(baseMap) {
const name = (CatalogMemberMixin.isMixedInto(baseMap) ? baseMap.name : undefined) ?? "";
return name;
}
/**
* Save user's base map preference
*/
function saveBaseMapPreference(terria, baseMap) {
if (baseMap.uniqueId) {
terria.setLocalProperty("basemap", baseMap.uniqueId);
}
}
/**
* Notify user about switching base map to a viewer compatible base map
*/
function notifyBaseMapSwitch(terria, viewer, currentBaseMap, newBaseMap, t) {
const viewerMode = viewer.viewerMode;
terria.notificationState.addNotificationToQueue({
title: t("settingPanel.baseMapSwitched.title"),
message: t("settingPanel.baseMapSwitched.message", {
currentBaseMap: mapDisplayName(currentBaseMap),
newBaseMap: mapDisplayName(newBaseMap),
mapMode: viewer.viewerMode === ViewerMode.Cesium ? "3D" : "2D"
}),
ignore: () =>
// auto-dismiss notification if state changes
viewer.baseMap !== newBaseMap || viewer.viewerMode !== viewerMode,
showAsToast: true,
toastVisibleDuration: 10
});
}
function notifyViewerModeSwitch(newBaseMap, currentViewerMode, newViewerMode, terria, t) {
const viewer = terria.mainViewer;
terria.notificationState.addNotificationToQueue({
title: t("settingPanel.viewerModeSwitched.title"),
message: t("settingPanel.viewerModeSwitched.message", {
newBaseMap: mapDisplayName(newBaseMap),
currentMode: currentViewerMode === ViewerMode.Cesium ? "3D" : "2D",
newMode: newViewerMode === ViewerMode.Cesium ? "3D" : "2D"
}),
ignore: () =>
// auto-dismiss notification if state changes
viewer.viewerMode !== newViewerMode || viewer.baseMap !== newBaseMap,
showAsToast: true,
toastVisibleDuration: 15
});
}
/**
* Returns a status message to show when the given base map is selected.
*
* Currently this returns a message if the base map requires a specific viewer
* mode.
*/
function getBaseMapStatusMessage(baseMap, t) {
const viewerMode = baseMap.preferredViewerMode && isViewerMode(baseMap.preferredViewerMode)
? baseMap.preferredViewerMode.slice(0, 2).toLocaleUpperCase()
: undefined;
if (viewerMode) {
return t("settingPanel.baseMapStatus.requiresViewerMode", { viewerMode });
}
}
export const SETTING_PANEL_NAME = "MenuBarMapSettingsButton";
export default SettingPanel;
const FlexGrid = styled(Box).attrs({ flexWrap: true }) `
gap: ${(props) => props.gap * 5}px;
> * {
flex: ${(props) => `1 0 ${getCalcWidth(props.elementsNo, props.gap)}`};
max-width: ${(props) => getCalcWidth(props.elementsNo, props.gap)};
}
`;
const getCalcWidth = (elementsNo, gap) => `calc(${100 / elementsNo}% - ${gap * 5}px)`;
const SettingsButton = styled(Button) `
background-color: ${(props) => props.theme.overlay};
border: 1px solid
${(props) => (props.isActive ? "rgba(255, 255, 255, 0.5)" : "transparent")};
`;
const StyledBasemapButton = styled(RawButton) `
border-radius: 4px;
position: relative;
border: 2px solid
${(props) => props.isActive ? props.theme.turquoiseBlue : "rgba(255, 255, 255, 0.5)"};
`;
const StyledImage = styled(Box).attrs({
as: "img"
}) `
border-radius: inherit;
`;
const BaseMapStatus = styled(Text) `
font-size: 11px;
padding: ${(p) => p.theme.padding} 0;
`;
//# sourceMappingURL=SettingPanel.js.map