diff --git a/src/components/common/LabelEntity.jsx b/src/components/common/LabelEntity.jsx new file mode 100644 index 0000000..58c55e3 --- /dev/null +++ b/src/components/common/LabelEntity.jsx @@ -0,0 +1,58 @@ +import { useEffect, useState } from "react"; +import { useSelector } from "react-redux"; +import { Entity, LabelGraphics, useCesium } from "resium"; +import { useInterval, useToggle } from "ahooks"; +import { Cartesian2, Color, LabelStyle } from "cesium"; + +export default function LabelEntity({ showTime, text, position, eyeOffset }) { + const { viewer } = useCesium(); + const { replayVersion } = useSelector((state) => state.data); + const [deley, setDeley] = useState(300); + const [show, setShow] = useState(false); + + useEffect(() => { + setDeley(300); + setShow(false); + }, [replayVersion]); + + const [font, { toggle: fontToggle }] = useToggle( + "24px Helvetica", + "bold 32px Helvetica" + ); + const [fillColor, { toggle: colorToggle }] = useToggle( + Color.fromCssColorString("#04fbfd").withAlpha(0.9), + Color.fromCssColorString("#04fbfd") + ); + + useInterval(() => { + const { startTime, currentTime } = viewer.clock; + const passtime = currentTime.secondsOfDay - startTime.secondsOfDay; + + if (passtime >= showTime) { + setShow(true); + let timer = setInterval(() => { + fontToggle(); + colorToggle(); + }, 500); + + setTimeout(() => { + clearInterval(timer); + }, 3000); + setDeley(undefined); + } + }, deley); + + return ( + + + + ); +} diff --git a/src/components/domain/Three/SceneOne/CustomChartPanel.jsx b/src/components/domain/Three/SceneOne/CustomChartPanel.jsx new file mode 100644 index 0000000..5d68f36 --- /dev/null +++ b/src/components/domain/Three/SceneOne/CustomChartPanel.jsx @@ -0,0 +1,187 @@ +import ChartPanel from "@/components/common/ChartPanel"; + +const years = []; + +for (let year = 1900; year <= 2000; year++) { + years.push(year); +} + +// 北极 +const AntarcticaData = [ + 0.101942611, 0.121527548, 0.139042908, 0.153372484, 0.163348844, 0.16781324, + 0.165740405, 0.156668394, 0.140792524, 0.118786901, 0.091903343, 0.062313072, + 0.032977812, 0.00716395, -0.012074836, -0.022308346, -0.021755523, + -0.00911355, 0.016196573, 0.053416993, 0.100564819, 0.155328769, 0.215351821, + 0.278252395, 0.341745714, 0.40383823, 0.463006888, 0.518309403, 0.569408932, + 0.616333481, 0.65915749, 0.697835067, 0.732192543, 0.761939745, 0.78659761, + 0.805314271, 0.81708585, 0.820876623, 0.815679973, 0.800851314, 0.776409401, + 0.742721875, 0.700509783, 0.651004076, 0.595858743, 0.536937172, 0.476075479, + 0.414725656, 0.353864772, 0.294239451, 0.236327526, 0.18015056, 0.125260938, + 0.070978964, 0.016363941, -0.039809019, -0.09861786, -0.160770418, + -0.226238681, -0.294159838, -0.363174145, -0.431703208, -0.497831469, + -0.559188985, -0.613384987, -0.658281488, -0.692634725, -0.716904761, + -0.73222222, -0.739942379, -0.741611283, -0.73898482, -0.733815531, + -0.727389051, -0.720449369, -0.713466528, -0.706847385, -0.700922099, + -0.695911133, -0.691920829, -0.688978939, -0.687022002, -0.685739611, + -0.684476492, -0.682376879, -0.678508387, -0.672015068, -0.662330127, + -0.649157058, -0.632405431, -0.612100964, -0.588400678, -0.56151658, + -0.531629308, -0.498751783, -0.462720586, -0.423305188, -0.380187818, + -0.332990445, -0.281392986, -0.225240753, +]; + +// 南极 +const ArcticData = [ + 0.280477536, 0.259957371, 0.238217659, 0.215366218, 0.191558571, 0.167050728, + 0.142221385, 0.117514405, 0.093353739, 0.070136018, 0.048165337, 0.027449274, + 0.007551689, -0.012191879, -0.032557296, -0.054358257, -0.078483167, + -0.105781444, -0.137208672, -0.174049223, -0.217858518, -0.26986764, + -0.329886107, -0.395653113, -0.463819615, -0.530529991, -0.591308473, + -0.641252419, -0.675396192, -0.689099366, -0.679883827, -0.649742172, + -0.602149752, -0.540699996, -0.469155442, -0.39235518, -0.317187358, + -0.251641431, -0.203124483, -0.175400473, -0.167133512, -0.174805821, + -0.194600911, -0.222664488, -0.254875954, -0.286753396, -0.31384639, + -0.331733387, -0.335992097, -0.322259345, -0.28761534, -0.234372353, + -0.167389335, -0.091870697, -0.013026713, 0.063931601, 0.133955753, + 0.193415777, 0.241940543, 0.280019233, 0.308168154, 0.326938643, 0.337234614, + 0.34096367, 0.340608802, 0.338547054, 0.336593745, 0.335321866, 0.334127457, + 0.331817533, 0.327341923, 0.320216724, 0.310432332, 0.298228465, 0.284102612, + 0.268784935, 0.253114795, 0.237954629, 0.224151962, 0.212536008, 0.203736355, + 0.198010458, 0.195233614, 0.194960735, 0.196490857, 0.19901247, 0.201666439, + 0.203617401, 0.204129573, 0.202630823, 0.19873752, 0.192336315, 0.183625216, + 0.173000981, 0.16106866, 0.148653001, 0.136669759, 0.12600632, 0.117368896, + 0.111065474, 0.107060448, +]; + +// 青藏高原 +const TibetanData = [ + -0.054774324, -0.072074468, -0.088166217, -0.102150868, -0.113128752, + -0.12021764, -0.122708696, -0.12041877, -0.113696453, -0.103130771, + -0.089455408, -0.073531475, -0.056259257, -0.03854918, -0.021352917, + -0.005665395, 0.007550856, 0.01747264, 0.023473197, 0.025172256, 0.022567986, + 0.01612004, 0.006575074, -0.005110343, -0.017780686, -0.030203073, + -0.041154273, -0.04937311, -0.053563998, -0.052516092, -0.045257867, + -0.031279225, -0.010592879, 0.016344502, 0.048680379, 0.085233754, + 0.124518788, 0.164767706, 0.204092074, 0.240587798, 0.272353213, 0.297512938, + 0.314395917, 0.321976324, 0.320031404, 0.309041322, 0.289958303, 0.26397781, + 0.232447596, 0.196815201, 0.15858192, 0.119195224, 0.079906512, 0.041729979, + 0.005377081, -0.028807517, -0.060676589, -0.09016857, -0.11718843, + -0.141549386, -0.163019079, -0.181382947, -0.196521195, -0.208451503, + -0.21726015, -0.223065102, -0.226003897, -0.226234158, -0.223991288, + -0.219766729, -0.214261928, -0.208224121, -0.202386774, -0.197405298, + -0.193729514, -0.191661726, -0.191394367, -0.192960556, -0.196177765, + -0.20070803, -0.206145455, -0.212083352, -0.218149914, -0.223985256, + -0.229195822, -0.233329665, -0.235927137, -0.236550243, -0.234784989, + -0.230241493, -0.222529731, -0.211284994, -0.196192431, -0.177010405, + -0.153629275, -0.126243447, -0.095383816, -0.061763417, -0.026131148, + 0.010750252, 0.048064799, +]; + +function CustomChartPanel() { + const option = { + title: { + // text: "Stacked Line", + }, + tooltip: { + trigger: "axis", + }, + legend: { + data: ["南极", "北极", "青藏高原"], + textStyle: { color: "#04fbfd", cursor: "point", fontSize: 20 }, + }, + animationDuration: 10 * 1000, + animationEasing: "cubicInOut", + grid: { + left: "3%", + right: "4%", + bottom: "3%", + containLabel: true, + }, + // toolbox: { + // feature: { + // // 下载 + // saveAsImage: {}, + // }, + // }, + xAxis: { + type: "category", + boundaryGap: false, + data: years, + axisLine: { + onZero: false, + symbol: ["none", "arrow"], + symbolOffser: [0, 10], + lineStyle: { + color: "#04fbfd", + }, + }, + axisLabel: { + interval: 4, + }, + }, + yAxis: { + type: "value", + min: -1, + max: 1, + interval: 0.5, + splitNumber: 5, + splitLine: { show: false }, + axisLine: { + onZero: false, + show: true, + symbol: ["none", "arrow"], + symbolOffset: [0, 10], + lineStyle: { + color: "#04fbfd", + }, + }, + axisLabel: { + show: true, + }, + axisTick: { show: true }, + scale: true, + }, + dataZoom: { type: "inside", start: 0, end: 100 }, + series: [ + { + name: "北极", + type: "line", + stack: "Total", + data: AntarcticaData, + smooth: true, + color: "red", + symbol: "none", + itemStyle: { + color: "red", + }, + }, + { + name: "南极", + type: "line", + // stack: "Total", + data: ArcticData, + smooth: true, + color: "blue", + symbol: "none", + itemStyle: { + color: "blue", + }, + }, + { + name: "青藏高原", + type: "line", + // stack: "Total", + data: TibetanData, + smooth: true, + color: "green", + symbol: "none", + itemStyle: { + color: "green", + }, + }, + ], + }; + + return ; +} + +export default CustomChartPanel; diff --git a/src/components/domain/Three/SceneOne/Legend.jsx b/src/components/domain/Three/SceneOne/Legend.jsx new file mode 100644 index 0000000..780323d --- /dev/null +++ b/src/components/domain/Three/SceneOne/Legend.jsx @@ -0,0 +1,55 @@ +import styles from "./index.module.less"; + +const colorBar = [ + "#801FEF", + "#050CFB", + "#3B6EE6", + "#159AFF", + "#36C1F8", + "#AADBF2", + "#FFF479", + "#FFC113", + "#FF7E00", + "#FF7E00", + "#CD0000", + "#FFB1B1", +]; + +function Legend() { + return ( +
+
全球温度异常空间分布
+
+ {colorBar.map((color, index) => { + return ( +
+ ); + })} +
+
+ {["", -1, -0.8, -0.6, -0.4, -0.2, 0, 0.2, 0.4, 0.6, 0.8, 1.0, ""].map( + (item, index) => { + return ( +
+ {item} +
+ ); + } + )} +
+
+ ); +} + +export default Legend; diff --git a/src/components/domain/Three/SceneOne/ViewerImageLayer.jsx b/src/components/domain/Three/SceneOne/ViewerImageLayer.jsx new file mode 100644 index 0000000..9856d9f --- /dev/null +++ b/src/components/domain/Three/SceneOne/ViewerImageLayer.jsx @@ -0,0 +1,31 @@ +import { useMemo } from "react"; +import { ImageryLayer } from "resium"; +import { WebMapServiceImageryProvider } from "cesium"; + +const url = "http://analysis.tpdc.ac.cn/gs/geoserver/phitrellis/wms"; + +function LandImageLayer({ name }) { + const tempProvider = useMemo( + () => + new WebMapServiceImageryProvider({ + url: url, + layers: `phitrellis:${name}`, + parameters: { + service: "WMS", + format: "image/png", + transparent: true, + }, + }), + [name, url] + ); + return ( + + ); +} + +export default LandImageLayer; diff --git a/src/components/domain/Three/SceneOne/ViewerOne.jsx b/src/components/domain/Three/SceneOne/ViewerOne.jsx new file mode 100644 index 0000000..fa17210 --- /dev/null +++ b/src/components/domain/Three/SceneOne/ViewerOne.jsx @@ -0,0 +1,31 @@ +import { CameraFlyTo } from "resium"; +import { Cartesian3 } from "cesium"; +import MapLayout from "@/components/map/Layout"; +import CustomToolbar from "@/components/common/CustomToolbar"; +import CustomClock from "@/components/common/CustomClock"; +import ViewerImageLayer from "./ViewerImageLayer"; +import WavePoint from "@/components/common/WavePoint"; + +function ViewerOne({ children }) { + return ( +
+ +
+ 两极协同—连接南极和北极的热带大西洋经向模的媒介作用 +
+ + + + + + + {children} +
+
+ ); +} + +export default ViewerOne; diff --git a/src/components/domain/Three/SceneOne/ViewerThree.jsx b/src/components/domain/Three/SceneOne/ViewerThree.jsx new file mode 100644 index 0000000..9a23fd8 --- /dev/null +++ b/src/components/domain/Three/SceneOne/ViewerThree.jsx @@ -0,0 +1,23 @@ +import { CameraFlyTo } from "resium"; +import { Cartesian3 } from "cesium"; +import MapLayout from "@/components/map/Layout"; +import WavePoint from "@/components/common/WavePoint"; +import ViewerImageLayer from "./ViewerImageLayer"; + +function ViewerThree({ children }) { + return ( +
+ + + + {children} + + +
+ ); +} + +export default ViewerThree; diff --git a/src/components/domain/Three/SceneOne/ViewerTwo.jsx b/src/components/domain/Three/SceneOne/ViewerTwo.jsx new file mode 100644 index 0000000..222853a --- /dev/null +++ b/src/components/domain/Three/SceneOne/ViewerTwo.jsx @@ -0,0 +1,28 @@ +import { CameraFlyTo } from "resium"; +import { Cartesian3 } from "cesium"; +import MapLayout from "@/components/map/Layout"; +import ViewerImageLayer from "./ViewerImageLayer"; +import WavePoint from "@/components/common/WavePoint"; + +function ViewerTwo({ children }) { + return ( +
+ + + + + {children} + +
+ ); +} + +export default ViewerTwo; diff --git a/src/components/domain/Three/SceneOne/index.jsx b/src/components/domain/Three/SceneOne/index.jsx new file mode 100644 index 0000000..4422b25 --- /dev/null +++ b/src/components/domain/Three/SceneOne/index.jsx @@ -0,0 +1,30 @@ +import TextInfoPanel from "@/components/common/TextInfoPanel"; +import CustomChartPanel from "./CustomChartPanel"; +import Legend from "./Legend"; +import ViewerOne from "./ViewerOne"; +import ViewerTwo from "./ViewerTwo"; +import ViewerThree from "./ViewerThree"; +import styles from "./index.module.less"; + +function SceneOne() { + return ( +
+
+ + + +
+
+
+ +
+
+ +
+
+ +
+ ); +} + +export default SceneOne; diff --git a/src/components/domain/Three/SceneOne/index.module.less b/src/components/domain/Three/SceneOne/index.module.less new file mode 100644 index 0000000..c6c6a53 --- /dev/null +++ b/src/components/domain/Three/SceneOne/index.module.less @@ -0,0 +1,142 @@ +.sceneOne :global { + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + background-color: darkslategray; + + .domain-viewers { + width: 100%; + flex: 1; + display: flex; + + .domain-viewer { + flex: 1; + position: relative; + + .title { + pointer-events: none; + } + } + } + + .domain-info { + flex: 1; + margin: 12px; + display: flex; + justify-content: space-between; + gap: 12px; + + .left-panel { + width: 450px; + display: flex; + flex-direction: column; + height: 100%; + } + + .right-panel { + flex: 1; + min-width: 450px; + display: flex; + flex-direction: column; + gap: 12px; + + .top-panel { + flex: 1; + } + + .bottom-panel { + flex: 1; + } + + .form-panel { + width: 100%; + height: 100%; + pointer-events: auto; + padding: 12px; + border: 1px solid #04fbfd; + color: #02f9ff !important; + background-color: #000000e7; + border-radius: 8px; + + .ant-form-item-label { + label { + color: white; + } + } + + .ant-form-item-control { + .ant-select-multiple { + max-height: 300px; + overflow-y: scroll; + + &::-webkit-scrollbar { + width: 6px; + height: 1px; + } + &::-webkit-scrollbar-thumb { + border-radius: 6px; + background: #00a2a2; + } + &::-webkit-scrollbar-track { + border-radius: 6px; + background: none; + } + } + } + } + } + } +} + +.legend :global { + position: absolute; + bottom: calc(50% + 14px); + width: 50%; + left: 25%; + z-index: 1000; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + background-color: #000000e7; + border: 1px solid #04fbfd; + border-radius: 8px; + padding: 8px; + + .legend-title { + color: #04fbfd; + } + + .colorbar { + width: 100%; + height: 14px; + display: flex; + margin: 8px 0; + + .colorbar-item { + flex: 1; + height: 100%; + border: 1px black solid; + + &:not(:nth-child(1)) { + border-left: none; + } + } + } + + .legend-text { + display: flex; + justify-content: space-evenly; + width: 100%; + height: 20px; + + .legend-text-item { + flex: 1; + text-align: right; + font-weight: 600; + color: white; + -webkit-text-stroke: #04fbfd 1px; + } + } +} diff --git a/src/components/domain/Three/SceneSwitch.jsx b/src/components/domain/Three/SceneSwitch.jsx new file mode 100644 index 0000000..7b29f62 --- /dev/null +++ b/src/components/domain/Three/SceneSwitch.jsx @@ -0,0 +1,15 @@ +import { Button } from "antd"; + +function SceneSwitch({ scene, setScene }) { + const switchHandler = () => { + setScene(scene === 1 ? 2 : 1); + }; + + return ( + + ); +} + +export default SceneSwitch; diff --git a/src/components/domain/Three/SceneTwo/Circles.jsx b/src/components/domain/Three/SceneTwo/Circles.jsx index 047a91e..8455da9 100644 --- a/src/components/domain/Three/SceneTwo/Circles.jsx +++ b/src/components/domain/Three/SceneTwo/Circles.jsx @@ -25,7 +25,7 @@ const cycloneColor = Color.fromCssColorString("#0000ff"); function Circles() { const { viewer } = useCesium(); - const Circle = ({ id, lon, lat, isLow, showTime }) => { + const Circle = ({ id, lon, lat, isLow, showTime, labelText }) => { const [show, setShow] = useState(false); const arrow = !isLow ? arrowRound : arrowRoundReverse; const color = !isLow ? anticycloneColor : cycloneColor; @@ -89,17 +89,17 @@ function Circles() { > {circle} - + {/* - + */} ); }; @@ -107,36 +107,43 @@ function Circles() { return ( + state.data); function cameraFlyToLine() { const step1 = { - destination: Cartesian3.fromDegrees(18, -75, 10000000), + destination: Cartesian3.fromDegrees(-29, -54, 10000000), duration: 3, orientation: { heading: 6.283, @@ -24,8 +27,8 @@ export default function CustomFlyTo() { }; const step2 = { - destination: Cartesian3.fromDegrees(80, -75, 10000000), - duration: 5, + destination: Cartesian3.fromDegrees(18, -75, 10000000), + duration: 4, orientation: { heading: 6.283, pitch: -1.3, @@ -36,9 +39,10 @@ export default function CustomFlyTo() { }, easingFunction: EasingFunction.LINEAR_NONE, }; + const step3 = { - destination: Cartesian3.fromDegrees(130, -55, 10000000), - duration: 5, + destination: Cartesian3.fromDegrees(80, -75, 10000000), + duration: 4, orientation: { heading: 6.283, pitch: -1.3, @@ -50,8 +54,8 @@ export default function CustomFlyTo() { easingFunction: EasingFunction.LINEAR_NONE, }; const step4 = { - destination: Cartesian3.fromDegrees(180, -75, 10000000), - duration: 5, + destination: Cartesian3.fromDegrees(158, -55, 10000000), + duration: 6, orientation: { heading: 6.283, pitch: -1.3, @@ -63,8 +67,21 @@ export default function CustomFlyTo() { easingFunction: EasingFunction.LINEAR_NONE, }; const step5 = { + destination: Cartesian3.fromDegrees(180, -75, 10000000), + duration: 3, + orientation: { + heading: 6.283, + pitch: -1.3, + roll: 0, + }, + complete: () => { + camera.flyTo(step6); + }, + easingFunction: EasingFunction.LINEAR_NONE, + }; + const step6 = { destination: Cartesian3.fromDegrees(-130, -85, 10000000), - duration: 5, + duration: 3, orientation: { heading: 6.283, pitch: -1.3, @@ -77,5 +94,11 @@ export default function CustomFlyTo() { } cameraFlyToLine(); + useEffect(() => { + viewer.clock.shouldAnimate = false; + viewer.clock.currentTime = viewer.clock.startTime.clone(); + cameraFlyToLine(); + }, [replayVersion]); + return <>; } diff --git a/src/components/domain/Three/SceneTwo/Labels.jsx b/src/components/domain/Three/SceneTwo/Labels.jsx new file mode 100644 index 0000000..25853d6 --- /dev/null +++ b/src/components/domain/Three/SceneTwo/Labels.jsx @@ -0,0 +1,46 @@ +import { Fragment } from "react"; +import LabelEntity from "@/components/common/LabelEntity"; +import { Cartesian2, Cartesian3 } from "cesium"; + +export default function Labels() { + return ( + + + + + + + + + ); +} diff --git a/src/components/domain/Three/SceneTwo/Point.jsx b/src/components/domain/Three/SceneTwo/Point.jsx index 3f28670..9ef9297 100644 --- a/src/components/domain/Three/SceneTwo/Point.jsx +++ b/src/components/domain/Three/SceneTwo/Point.jsx @@ -14,11 +14,12 @@ import { } from "cesium"; const data = [ - { longitude: 18, latitude: -50, height: 0, time: 0 }, - { longitude: 18, latitude: -50, height: 1000000, time: 2 }, - { longitude: 80, latitude: -50, height: 1000000, time: 7 }, - { longitude: 130, latitude: -40, height: 1000000, time: 12 }, - { longitude: 166, latitude: -55, height: 1000000, time: 17 }, + { longitude: -29, latitude: -33, height: 0, time: 0 }, + { longitude: -29, latitude: -33, height: 1000000, time: 2 }, + { longitude: 18, latitude: -50, height: 1000000, time: 6 }, + { longitude: 80, latitude: -50, height: 1000000, time: 10 }, + { longitude: 157, latitude: -35, height: 1000000, time: 14 }, + { longitude: 178, latitude: -50, height: 1000000, time: 18 }, { longitude: -130, latitude: -65, height: 1000000, time: 22 }, ]; diff --git a/src/components/domain/Three/SceneTwo/ReplayButton.jsx b/src/components/domain/Three/SceneTwo/ReplayButton.jsx new file mode 100644 index 0000000..7252b69 --- /dev/null +++ b/src/components/domain/Three/SceneTwo/ReplayButton.jsx @@ -0,0 +1,20 @@ +import { useCallback } from "react"; +import { useDispatch } from "react-redux"; +import { Button } from "antd"; +import styles from "./index.module.less"; + +export default function ReplayButton() { + const dispatch = useDispatch(); + + const playHandler = useCallback(() => { + dispatch.data.update({ replayVersion: Date.now() }); + }, []); + + return ( +
+ +
+ ); +} diff --git a/src/components/domain/Three/SceneTwo/index.jsx b/src/components/domain/Three/SceneTwo/index.jsx index c65f0e9..dc3c349 100644 --- a/src/components/domain/Three/SceneTwo/index.jsx +++ b/src/components/domain/Three/SceneTwo/index.jsx @@ -5,6 +5,8 @@ import HGTImageLayer from "./HGTImageLayer"; import Point from "./Point"; import Circles from "./Circles"; import CustomChartPanel from "./CustomChartPanel"; +import Labels from "./Labels"; +import ReplayButton from "./ReplayButton"; function SceneTwo() { return ( @@ -20,9 +22,11 @@ function SceneTwo() {
+ + ); diff --git a/src/components/domain/Three/SceneTwo/index.module.less b/src/components/domain/Three/SceneTwo/index.module.less index f2378ea..06e8236 100644 --- a/src/components/domain/Three/SceneTwo/index.module.less +++ b/src/components/domain/Three/SceneTwo/index.module.less @@ -50,3 +50,9 @@ } } } + +.replayButton :global { + position: absolute; + top: 8px; + left: 140px; +} diff --git a/src/components/domain/Three/index.jsx b/src/components/domain/Three/index.jsx index c8c23f6..62f18e2 100644 --- a/src/components/domain/Three/index.jsx +++ b/src/components/domain/Three/index.jsx @@ -1,10 +1,16 @@ -import styles from "./index.module.less"; +import { useState } from "react"; +import SceneOne from "./SceneOne"; import SceneTwo from "./SceneTwo"; +import SceneSwitch from "./SceneSwitch"; +import styles from "./index.module.less"; export default function DomainThree() { + const [scene, setScene] = useState(1); + return (
- + {scene === 1 ? : } +
); }