diff --git a/src/assets/cloud.png b/src/assets/cloud.png new file mode 100644 index 0000000..b6b3fcc Binary files /dev/null and b/src/assets/cloud.png differ diff --git a/src/assets/moisture.png b/src/assets/moisture.png new file mode 100644 index 0000000..1388300 Binary files /dev/null and b/src/assets/moisture.png differ diff --git a/src/components/common/WavePoint/index.jsx b/src/components/common/WavePoint/index.jsx index cfa3e45..6742841 100644 --- a/src/components/common/WavePoint/index.jsx +++ b/src/components/common/WavePoint/index.jsx @@ -9,6 +9,7 @@ function WavePoint({ deviationR = 5000, eachInterval = 1500, maxR = 3600 * 100, + color = "WHITE", }) { const data = { stationLon, // 经度 @@ -60,7 +61,7 @@ function WavePoint({ transparent: true, color: new Cesium.CallbackProperty(function () { var alp = 1 - r1 / data.maxR; - return Cesium.Color.WHITE.withAlpha(alp); + return Cesium.Color[color].withAlpha(alp); }, false), }) } diff --git a/src/components/domain/Five/ChartPanel.jsx b/src/components/domain/Five/ChartPanel.jsx deleted file mode 100644 index e98be8e..0000000 --- a/src/components/domain/Five/ChartPanel.jsx +++ /dev/null @@ -1,121 +0,0 @@ -import ReactECharts from "echarts-for-react"; - -const years = []; - -for (let year = 2011; year <= 2020; year++) { - years.push(year); -} - -const observedData = [ - -0.90765893, 0.14846958, 1.6577014, -0.32029018, -1.2422978, 0.44113398, - -0.45218363, 1.897564, -0.6926195, -0.529819, -]; - -const predictedData = [ - -0.20760797, 0.206235, 0.85673275, 0.13969683, -0.39370838, 0.23775028, - -0.34866039, 0.93110625, 0.19143088, 0.5760311, -]; - -function ChartPanel() { - const option = { - title: { - // text: "Stacked Line", - }, - tooltip: { - trigger: "axis", - }, - legend: { - data: ["观测结果", "预测结果"], - textStyle: { color: "#04fbfd", cursor: "point" }, - }, - animationDuration: years.length * 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", - }, - }, - }, - yAxis: { - type: "value", - min: -2.0, - max: 2.0, - 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: observedData, - color: "black", - symbol: "none", - itemStyle: { - color: "black", - }, - }, - { - name: "预测结果", - type: "line", - // stack: "Total", - data: predictedData, - color: "red", - symbol: "none", - itemStyle: { - color: "red", - }, - }, - ], - }; - - return ( -
- -
- ); -} - -export default ChartPanel; diff --git a/src/components/domain/Five/HeatmapImageLayer.jsx b/src/components/domain/Five/HeatmapImageLayer.jsx new file mode 100644 index 0000000..705056a --- /dev/null +++ b/src/components/domain/Five/HeatmapImageLayer.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"; +const name = "phitrellis:4_5_heatmap_htmp"; + +function HeatmapImageLayer() { + const tempProvider = useMemo( + () => + new WebMapServiceImageryProvider({ + url: url, + layers: name, + parameters: { + service: "WMS", + format: "image/png", + transparent: true, + }, + }), + [name, url] + ); + return ( + + ); +} + +export default HeatmapImageLayer; diff --git a/src/components/domain/Five/Legend.jsx b/src/components/domain/Five/Legend.jsx new file mode 100644 index 0000000..1ed4a9d --- /dev/null +++ b/src/components/domain/Five/Legend.jsx @@ -0,0 +1,48 @@ +const colorBar = [ + "#7272FF", + "#8585FF", + "#fff", + "#FFAFAF", + "#FF9C9C", + "#FF8787", + "#FF7474", + "#FF6060", + "#FF4B4B", + "#FE3838", + "#FF2323", +]; + +function Legend() { + return ( +
+
+
+ {colorBar.map((color, index) => { + return ( +
+ ); + })} +
+
+ {[-0.02, -0.01, 0.01, 0.02, 0.03, 0.05, 0.1, 0.15, 0.2, 0.25, ""].map( + (item, index) => { + return ( +
+ {item} +
+ ); + } + )} +
+
+ ); +} + +export default Legend; diff --git a/src/components/domain/Five/index.jsx b/src/components/domain/Five/index.jsx index 16ddca8..f59677c 100644 --- a/src/components/domain/Five/index.jsx +++ b/src/components/domain/Five/index.jsx @@ -1,12 +1,12 @@ +import { useState } from "react"; import MapLayout from "@/components/map/Layout"; import CustomToolbar from "@/components/common/CustomToolbar"; import CustomClock from "@/components/common/CustomClock"; import TextInfoPanel from "@/components/common/TextInfoPanel"; import CustomFlyTo from "./CustomFlyTo"; import FormPanel from "./FormPanel"; -import ChartPanel from "./ChartPanel"; -// import RectangleLayer from "./RectangleLayer"; -import { useState } from "react"; +import HeatmapImageLayer from "./HeatmapImageLayer"; +import Legend from "./Legend"; export default function DomainFive() { const [show, setShow] = useState(false); @@ -27,9 +27,10 @@ export default function DomainFive() {
-
{show && }
+
- {/* */} + {show && } + ); } diff --git a/src/components/domain/Four/FormPanel.jsx b/src/components/domain/Four/FormPanel.jsx index ee5a8fe..a4f5f33 100644 --- a/src/components/domain/Four/FormPanel.jsx +++ b/src/components/domain/Four/FormPanel.jsx @@ -1,4 +1,4 @@ -import { Button, Form, Input, Select } from "antd"; +import { Button, Form, Select } from "antd"; const layout = { labelCol: { span: 6 }, @@ -38,6 +38,7 @@ export default function FormPanel({ setShow }) { ], }} onFinish={(values) => { + // 根据values生成charts setShow(true); }} > diff --git a/src/components/domain/Four/RectangleLayer.jsx b/src/components/domain/Four/RectangleLayer.jsx index deb8e62..2fc119d 100644 --- a/src/components/domain/Four/RectangleLayer.jsx +++ b/src/components/domain/Four/RectangleLayer.jsx @@ -1,4 +1,4 @@ -import { Cartesian3, Rectangle, Color, Math as CesiumMath } from "cesium"; +import { Rectangle, Color, Math as CesiumMath } from "cesium"; import { Entity, RectangleGraphics } from "resium"; const points = [ [13, -70, 30, -67], diff --git a/src/components/domain/One/Barotorpic.jsx b/src/components/domain/One/Barotorpic.jsx index 24833eb..e492bb9 100644 --- a/src/components/domain/One/Barotorpic.jsx +++ b/src/components/domain/One/Barotorpic.jsx @@ -1,11 +1,5 @@ import { useCallback, useState } from "react"; -import { - Entity, - LabelGraphics, - EllipseGraphics, - useCesium, - CylinderGraphics, -} from "resium"; +import { Entity, LabelGraphics, EllipseGraphics, useCesium } from "resium"; import { Color, Cartesian3, LabelStyle } from "cesium"; import { useInterval } from "ahooks"; @@ -31,7 +25,6 @@ function Barotropic() { state.data); + + const { shouldAnimate } = viewer.clock; + + // useInterval(() => { + // console.log(currentTime, startTime, stopTime); + // }, 100); useEffect(() => { - if (showPanelHighlight) { + if (shouldAnimate) { setIsHighlight(true); - setTimeout(() => { setIsHighlight(false); }, 3000); } - }, [showPanelHighlight]); - console.log("showPanelHighlight :>> ", showPanelHighlight); + }, [shouldAnimate]); const option = { title: { diff --git a/src/components/domain/One/LabradorImageLayer.jsx b/src/components/domain/One/LabradorImageLayer.jsx index e947c29..79a67ac 100644 --- a/src/components/domain/One/LabradorImageLayer.jsx +++ b/src/components/domain/One/LabradorImageLayer.jsx @@ -1,6 +1,6 @@ import { useEffect, useMemo, useState } from "react"; import { ImageryLayer, useCesium } from "resium"; -import { Cesium3DTile, WebMapServiceImageryProvider } from "cesium"; +import { WebMapServiceImageryProvider } from "cesium"; import { useInterval } from "ahooks"; import { useDispatch, useSelector } from "react-redux"; @@ -21,7 +21,7 @@ function LabradorImageLayer() { useEffect(() => { const { labrador } = imageLayer; if (!!labrador) { - setDelay(1000); + setDelay((60 / nameList.length) * 1000); setIndex(0); } }, [imageLayer]); @@ -40,29 +40,22 @@ function LabradorImageLayer() { }), [name, url] ); + return ( ); }); useInterval(() => { setIndex((index) => index + 1); - if (index === 0) { - dispatch.data.update({ showPanelHighlight: true }); - } if (index >= nameList.length) { - setTimeout(() => { - setIndex(0); - setDelay(1000); - }, (60 - nameList.length) * 1000); - setDelay(undefined); - dispatch.data.update({ showPanelHighlight: false }); + setIndex(0); + setDelay((60 / nameList.length) * 1000); } }, delay); diff --git a/src/components/domain/One/TibetImageLayer.jsx b/src/components/domain/One/TibetImageLayer.jsx index 3ce6da1..80d1319 100644 --- a/src/components/domain/One/TibetImageLayer.jsx +++ b/src/components/domain/One/TibetImageLayer.jsx @@ -17,13 +17,10 @@ function TibetImageLayer() { const [delay, setDelay] = useState(undefined); const [index, setIndex] = useState(0); - //viewer.clock?.shouldAnimate - console.log("viewer.clock?.shouldAnimate :>> ", viewer.clock?.shouldAnimate); - useEffect(() => { const { labrador } = imageLayer; if (!!labrador) { - setDelay(1000); + setDelay((60 / nameList.length) * 1000); setIndex(0); } }, [imageLayer]); @@ -55,11 +52,8 @@ function TibetImageLayer() { setIndex((index) => index + 1); if (index >= nameList.length) { - setTimeout(() => { - setIndex(0); - setDelay(1000); - }, (60 - nameList.length) * 1000); - setDelay(undefined); + setIndex(0); + setDelay((60 / nameList.length) * 1000); } }, delay); diff --git a/src/components/domain/One/index.jsx b/src/components/domain/One/index.jsx index d68bc2c..f4e426c 100644 --- a/src/components/domain/One/index.jsx +++ b/src/components/domain/One/index.jsx @@ -41,7 +41,7 @@ export default function DomainOne() { - {/* */} + ); } diff --git a/src/components/domain/Three/LandImageLayer.jsx b/src/components/domain/Three/LandImageLayer.jsx index 40fb66a..2cfaa40 100644 --- a/src/components/domain/Three/LandImageLayer.jsx +++ b/src/components/domain/Three/LandImageLayer.jsx @@ -1,13 +1,11 @@ -import { useEffect, useMemo, useState } from "react"; -import { ImageryLayer, useCesium } from "resium"; +import { useMemo } from "react"; +import { ImageryLayer } from "resium"; import { WebMapServiceImageryProvider } from "cesium"; const url = "http://analysis.tpdc.ac.cn/gs/geoserver/phitrellis/wms"; const name = "phitrellis:4_3_t2m_era_1024"; function LandImageLayer() { - const { viewer } = useCesium(); - const tempProvider = useMemo( () => new WebMapServiceImageryProvider({ diff --git a/src/components/domain/Three/Legend.jsx b/src/components/domain/Three/Legend.jsx new file mode 100644 index 0000000..3e28a7b --- /dev/null +++ b/src/components/domain/Three/Legend.jsx @@ -0,0 +1,52 @@ +import styles from "./index.module.less"; + +const colorBar = [ + "#7F20F0", + "#2A09FA", + "#1C33F1", + "#3B6CE3", + "#2388F8", + "#0EA5FE", + "#23C0FB", + "#85CAF1", + "#B8E1F3", + "#FEF995", + "#FEDD30", + "#FEB709", + "#FF8B00", + "#FF5D00", + "#FF1001", + "#E00000", + "#D62A2A", + "#FFB1B1", +]; + +function Legend() { + return ( +
+
+
+ {colorBar.map((color, index) => { + return ( +
+ ); + })} +
+
+ {[-0.8, -0.4, 0, 0.4, 0.8, ""].map((item, index) => { + return ( +
+ {item} +
+ ); + })} +
+
+ ); +} + +export default Legend; diff --git a/src/components/domain/Three/OceanImageLayer.jsx b/src/components/domain/Three/OceanImageLayer.jsx index 3d8eaa4..cea4b47 100644 --- a/src/components/domain/Three/OceanImageLayer.jsx +++ b/src/components/domain/Three/OceanImageLayer.jsx @@ -1,8 +1,6 @@ -import { useEffect, useMemo, useState } from "react"; -import { ImageryLayer, useCesium } from "resium"; +import { useMemo } from "react"; +import { ImageryLayer } from "resium"; import { WebMapServiceImageryProvider } from "cesium"; -import { useInterval } from "ahooks"; -import { useSelector } from "react-redux"; const url = "http://analysis.tpdc.ac.cn/gs/geoserver/phitrellis/wms"; const name = "phitrellis:4_3_hadisst_1822"; diff --git a/src/components/domain/Three/index.jsx b/src/components/domain/Three/index.jsx index d35c643..90e974c 100644 --- a/src/components/domain/Three/index.jsx +++ b/src/components/domain/Three/index.jsx @@ -8,6 +8,7 @@ import OceanImageLayer from "./OceanImageLayer"; import WavePoint from "@/components/common/WavePoint"; import { CameraFlyTo } from "resium"; import { Cartesian3 } from "cesium"; +import Legend from "./Legend"; export default function DomainOne() { return ( @@ -26,37 +27,38 @@ export default function DomainOne() {
- {/* */} - {/* */} +
+ - +
- +
+ {/* */}
); } diff --git a/src/components/domain/Three/index.module.less b/src/components/domain/Three/index.module.less new file mode 100644 index 0000000..c9cc1bf --- /dev/null +++ b/src/components/domain/Three/index.module.less @@ -0,0 +1,53 @@ +.legend :global { + position: absolute; + bottom: 40px; + width: 50%; + left: 25%; + z-index: 1000; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + background-color: #1f485690; + 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-radius: 8px; + border: 1px black solid; + + &:not(:nth-child(1)) { + border-left: none; + } + } + } + + .legend-text { + display: flex; + justify-content: space-evenly; + width: 100%; + height: 20px; + margin-left: 8px; + + .legend-text-item { + flex: 1; + text-align: right; + font-weight: 600; + color: white; + -webkit-text-stroke: #04fbfd 1px; + } + } +} diff --git a/src/components/domain/Two/Circles.jsx b/src/components/domain/Two/Circles.jsx index 4fe553f..8a00819 100644 --- a/src/components/domain/Two/Circles.jsx +++ b/src/components/domain/Two/Circles.jsx @@ -1,6 +1,5 @@ import { Fragment, useState } from "react"; import { Entity, EllipsoidGraphics, useCesium, PolylineGraphics } from "resium"; -import { useParams } from "react-router-dom"; import { useInterval, useThrottleFn } from "ahooks"; import { useDispatch } from "react-redux"; diff --git a/src/components/domain/Two/Cloud.jsx b/src/components/domain/Two/Cloud.jsx new file mode 100644 index 0000000..e46c1d1 --- /dev/null +++ b/src/components/domain/Two/Cloud.jsx @@ -0,0 +1,92 @@ +import { useCesium } from "resium"; + +function Cloud() { + const { viewer } = useCesium(); + const scene = viewer.scene; + const position = Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706, 50); + + Cesium.Math.setRandomNumberSeed(2.5); + function getRandomNumberInRange(minValue, maxValue) { + return minValue + Cesium.Math.nextRandomNumber() * (maxValue - minValue); + } + + const clouds = new Cesium.CloudCollection(); + + // manually position clouds in the mountains + function createBackLayerClouds() { + clouds.add({ + position: Cesium.Cartesian3.fromDegrees(-122.6908, 45.496, 300), + scale: new Cesium.Cartesian2(1500, 250), + maximumSize: new Cesium.Cartesian3(50, 15, 13), + slice: 0.3, + }); + + clouds.add({ + position: Cesium.Cartesian3.fromDegrees(-122.72, 45.5, 335), + scale: new Cesium.Cartesian2(1500, 300), + maximumSize: new Cesium.Cartesian3(50, 12, 15), + slice: 0.36, + }); + } + let long, lat, height, scaleX, scaleY, aspectRatio, cloudHeight, depth, slice; + + // randomly generate clouds in a certain area + function createRandomClouds( + numClouds, + startLong, + stopLong, + startLat, + stopLat, + minHeight, + maxHeight + ) { + const rangeLong = stopLong - startLong; + const rangeLat = stopLat - startLat; + for (let i = 0; i < numClouds; i++) { + long = startLong + getRandomNumberInRange(0, rangeLong); + lat = startLat + getRandomNumberInRange(0, rangeLat); + height = getRandomNumberInRange(minHeight, maxHeight); + scaleX = getRandomNumberInRange(150, 350); + scaleY = scaleX / 2.0 - getRandomNumberInRange(0, scaleX / 4.0); + slice = getRandomNumberInRange(0.3, 0.7); + depth = getRandomNumberInRange(5, 20); + aspectRatio = getRandomNumberInRange(1.5, 2.1); + cloudHeight = getRandomNumberInRange(5, 20); + clouds.add({ + position: Cesium.Cartesian3.fromDegrees(long, lat, height), + scale: new Cesium.Cartesian2(scaleX, scaleY), + maximumSize: new Cesium.Cartesian3( + aspectRatio * cloudHeight, + cloudHeight, + depth + ), + slice: slice, + }); + } + } + // manually position clouds in front + const scratch = new Cesium.Cartesian3(); + function createFrontLayerClouds() { + clouds.add({ + position: Cesium.Cartesian3.fromDegrees(-122.666, 45.5126, 97), + scale: new Cesium.Cartesian2(400, 150), + maximumSize: new Cesium.Cartesian3(25, 12, 15), + slice: 0.36, + }); + + clouds.add({ + position: Cesium.Cartesian3.fromDegrees(-122.6665, 45.5262, 76), + scale: new Cesium.Cartesian2(450, 200), + maximumSize: new Cesium.Cartesian3(25, 14, 12), + slice: 0.3, + }); + } + + createBackLayerClouds(); + createRandomClouds(8, -122.685, -122.67, 45.51, 45.525, 50, 250); + createFrontLayerClouds(); + + scene.primitives.add(clouds); +} + +export default Cloud; diff --git a/src/components/domain/Two/EntityLegend.jsx b/src/components/domain/Two/EntityLegend.jsx index 59e7aac..9690d90 100644 --- a/src/components/domain/Two/EntityLegend.jsx +++ b/src/components/domain/Two/EntityLegend.jsx @@ -1,59 +1,48 @@ -import cyclone from "@/assets/cyclone.png"; -import anticyclone from "@/assets/anticyclone.png"; import antiHadleyCell from "@/assets/anti-Hadleycell.png"; import low from "@/assets/Low.png"; import high from "@/assets/High.png"; +import cloud from "@/assets/cloud.png"; +import moisture from "@/assets/moisture.png"; function EntityLegend() { return ( -
+
- + low
Low
- + moisture
Moisture transport
- + antiHadleyCell
anti-Hadley cell
- + high
High
- + cloud
Rainfall
-
-
Surface wd anomaly
-
- {/*
-
- + {/*
+
-
water wapor path
-
*/} +
Surface wind anomaly
*/} +
); } diff --git a/src/components/domain/Two/IndiaOceanSST.jsx b/src/components/domain/Two/IndiaOceanSST.jsx index 5be8261..b4a01a7 100644 --- a/src/components/domain/Two/IndiaOceanSST.jsx +++ b/src/components/domain/Two/IndiaOceanSST.jsx @@ -1,7 +1,6 @@ -import { useEffect, useMemo, useState } from "react"; -import { ImageryLayer, useCesium } from "resium"; +import { useMemo } from "react"; +import { ImageryLayer } from "resium"; import { WebMapServiceImageryProvider } from "cesium"; -import { useInterval } from "ahooks"; import { useSelector } from "react-redux"; const url = "http://analysis.tpdc.ac.cn/gs/geoserver/phitrellis/wms"; diff --git a/src/components/domain/Two/MoistureTransport.jsx b/src/components/domain/Two/MoistureTransport.jsx new file mode 100644 index 0000000..d3125d5 --- /dev/null +++ b/src/components/domain/Two/MoistureTransport.jsx @@ -0,0 +1,54 @@ +import { useInterval } from "ahooks"; +import { Fragment, useCallback, useState } from "react"; +import { Entity, PolylineGraphics, useCesium } from "resium"; + +function MoistureTransport() { + const { viewer } = useCesium(); + const [show, setShow] = useState(false); + + const showAnimate = useCallback(() => { + const { currentTime, stopTime } = viewer.clock; + const leftTime = Math.floor( + stopTime.secondsOfDay - currentTime.secondsOfDay + ); + + if (leftTime < 10) { + setShow(true); + } else if (show) setShow(false); + }, [show]); + + useInterval(showAnimate, 100); + + return ( + + + + + + + + + ); +} + +export default MoistureTransport; diff --git a/src/components/domain/Two/PlateauPolygon.jsx b/src/components/domain/Two/PlateauPolygon.jsx index 47af5a3..e47886b 100644 --- a/src/components/domain/Two/PlateauPolygon.jsx +++ b/src/components/domain/Two/PlateauPolygon.jsx @@ -1,10 +1,8 @@ import { useCallback, useState } from "react"; -import { useParams } from "react-router-dom"; import { Entity, PolygonGraphics, useCesium } from "resium"; import { useInterval } from "ahooks"; function PlateauPolygon() { - const { type } = useParams(); const { viewer } = useCesium(); const [show, setShow] = useState(false); @@ -17,7 +15,7 @@ function PlateauPolygon() { if (leftTime < 20) { setShow(true); } else if (show) setShow(false); - }, [show, type]); + }, [show]); useInterval(showAnimate, 100); diff --git a/src/components/domain/Two/SurfaceAnomaly.jsx b/src/components/domain/Two/SurfaceAnomaly.jsx new file mode 100644 index 0000000..fefa8f8 --- /dev/null +++ b/src/components/domain/Two/SurfaceAnomaly.jsx @@ -0,0 +1,39 @@ +import { useCallback, useState } from "react"; +import { useInterval } from "ahooks"; +import { Entity, PolylineGraphics, useCesium } from "resium"; + +function SurfaceAnomaly() { + const { viewer } = useCesium(); + const [show, setShow] = useState(false); + + const showAnimate = useCallback(() => { + const { currentTime, stopTime } = viewer.clock; + const leftTime = Math.floor( + stopTime.secondsOfDay - currentTime.secondsOfDay + ); + + if (leftTime < 10) { + setShow(true); + } else if (show) setShow(false); + }, [show]); + + useInterval(showAnimate, 100); + + return ( + + + + ); +} + +export default SurfaceAnomaly; diff --git a/src/components/domain/Two/index.jsx b/src/components/domain/Two/index.jsx index f6fcfa6..721d9d6 100644 --- a/src/components/domain/Two/index.jsx +++ b/src/components/domain/Two/index.jsx @@ -10,6 +10,8 @@ import Legend from "./Legend"; import IndianOceanSST from "./IndiaOceanSST"; import EntityLegend from "./EntityLegend"; import Circles from "./Circles"; +import SurfaceAnomaly from "./SurfaceAnomaly"; +import MoistureTransport from "./MoistureTransport"; export default function DomainTwo() { return ( @@ -28,6 +30,8 @@ export default function DomainTwo() { + {/* */} + ); } diff --git a/src/components/home/Layout/NavBar.jsx b/src/components/home/Layout/NavBar.jsx index f515fa5..de55c47 100644 --- a/src/components/home/Layout/NavBar.jsx +++ b/src/components/home/Layout/NavBar.jsx @@ -1,4 +1,3 @@ -import { Button } from "antd"; import { useCallback } from "react"; import { useNavigate } from "react-router-dom"; import NavBarButton from "./NavBarButton"; diff --git a/src/components/map/Layout/CustomClock.jsx b/src/components/map/Layout/CustomClock.jsx deleted file mode 100644 index bc2865a..0000000 --- a/src/components/map/Layout/CustomClock.jsx +++ /dev/null @@ -1,28 +0,0 @@ -import { Clock } from "resium"; - -// 起始时间 -const start = Cesium.JulianDate.fromDate(new Date()); -// 结束时间 -const stop = Cesium.JulianDate.addSeconds(start, 60, new Cesium.JulianDate()); - -function CustomClock() { - return ( - { - // if (!clock.shouldAnimate) return; - // }} - // onStop={() => { - // if (toolbar.showPanel === undefined) - // dispatch.data.updateToolbar({ showPanel: true }); - // }} - /> - ); -} - -export default CustomClock; diff --git a/src/components/map/Layout/DynamicImageryLayer/LayerAntarcticaToQTP.jsx b/src/components/map/Layout/DynamicImageryLayer/LayerAntarcticaToQTP.jsx deleted file mode 100644 index 1eeceb4..0000000 --- a/src/components/map/Layout/DynamicImageryLayer/LayerAntarcticaToQTP.jsx +++ /dev/null @@ -1,35 +0,0 @@ -import { useEffect, useMemo, useState } from "react"; -import { ImageryLayer, useCesium } from "resium"; -import { WebMapServiceImageryProvider } from "cesium"; -import { useInterval } from "ahooks"; -import { useSelector } from "react-redux"; - -const url = "http://analysis.tpdc.ac.cn/gs/geoserver/phitrellis/wms"; - -function LayerAntarcticaToQTP() { - const { imageLayer } = useSelector((state) => state.data); - - const tempProvider = useMemo( - () => - new WebMapServiceImageryProvider({ - url: url, - layers: "phitrellis:india_ocean_sst", - parameters: { - service: "WMS", - format: "image/png", - transparent: true, - }, - }), - [url] - ); - - return ( - - ); -} - -export default LayerAntarcticaToQTP; diff --git a/src/components/map/Layout/DynamicImageryLayer/LayerLabToQTP.jsx b/src/components/map/Layout/DynamicImageryLayer/LayerLabToQTP.jsx deleted file mode 100644 index 9a73681..0000000 --- a/src/components/map/Layout/DynamicImageryLayer/LayerLabToQTP.jsx +++ /dev/null @@ -1,71 +0,0 @@ -import { useEffect, useMemo, useState } from "react"; -import { ImageryLayer, useCesium } from "resium"; -import { WebMapServiceImageryProvider } from "cesium"; -import { useInterval } from "ahooks"; -import { useSelector } from "react-redux"; - -const url = "http://analysis.tpdc.ac.cn/gs/geoserver/phitrellis/wms"; -const nameList = [ - 1980, 1981, 1982, 1983, 1984, 1985, 1986, 1987, 1988, 1989, 1990, 1991, 1992, - 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, - 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, -].map((item) => "phitrellis:4_1_la_" + item); - -function LayerLabToQTP() { - const { viewer } = useCesium(); - const { imageLayer } = useSelector((state) => state.data); - const [delay, setDelay] = useState(undefined); - const [index, setIndex] = useState(0); - - //viewer.clock?.shouldAnimate - console.log("viewer.clock?.shouldAnimate :>> ", viewer.clock?.shouldAnimate); - - useEffect(() => { - const { labrador } = imageLayer; - if (!!labrador) { - setDelay(1000); - setIndex(0); - } - }, [imageLayer]); - - const layers = nameList.map((name, index) => { - const tempProvider = useMemo( - () => - new WebMapServiceImageryProvider({ - url: url, - layers: name, - parameters: { - service: "WMS", - format: "image/png", - transparent: true, - }, - }), - [name, url] - ); - return ( - - ); - }); - - useInterval(() => { - setIndex((index) => index + 1); - - if (index >= nameList.length) { - setTimeout(() => { - setIndex(0); - setDelay(1000); - }, (60 - nameList.length) * 1000); - setDelay(undefined); - } - }, delay); - - if (!viewer.clock?.shouldAnimate) return; - - return layers[index]; -} - -export default LayerLabToQTP; diff --git a/src/components/map/Layout/DynamicImageryLayer/index.jsx b/src/components/map/Layout/DynamicImageryLayer/index.jsx deleted file mode 100644 index e0404c0..0000000 --- a/src/components/map/Layout/DynamicImageryLayer/index.jsx +++ /dev/null @@ -1,13 +0,0 @@ -import { useParams } from "react-router-dom"; -import LayerLabToQTP from "./LayerLabToQTP"; -import LayerAntarcticaToQTP from "./LayerAntarcticaToQTP"; - -const DynamicImageryLayer = () => { - const { type } = useParams(); - - if (type === "1") return ; - else if (type === "2") return ; - else return <>; -}; - -export default DynamicImageryLayer; diff --git a/src/components/map/Layout/Entities/Barotorpic.jsx b/src/components/map/Layout/Entities/Barotorpic.jsx deleted file mode 100644 index 08314d3..0000000 --- a/src/components/map/Layout/Entities/Barotorpic.jsx +++ /dev/null @@ -1,63 +0,0 @@ -import { useCallback, useState } from "react"; -import { - Entity, - LabelGraphics, - EllipseGraphics, - useCesium, - CylinderGraphics, -} from "resium"; -import { Color, Cartesian3, LabelStyle } from "cesium"; -import { useParams } from "react-router-dom"; -import { useInterval } from "ahooks"; - -function Barotropic() { - const { viewer } = useCesium(); - const { type } = useParams(); - - const [show, setShow] = useState(false); - - const showAnimate = useCallback(() => { - const { currentTime, stopTime } = viewer.clock; - const leftTime = Math.floor( - stopTime.secondsOfDay - currentTime.secondsOfDay - ); - - if (leftTime < 10) { - setShow(true); - } else if (show) setShow(false); - }, [show]); - - useInterval(showAnimate, 100); - - if (type !== "1") return <>; - - return ( - - - - - ); -} - -export default Barotropic; diff --git a/src/components/map/Layout/Entities/Circles.jsx b/src/components/map/Layout/Entities/Circles.jsx deleted file mode 100644 index 2fb2136..0000000 --- a/src/components/map/Layout/Entities/Circles.jsx +++ /dev/null @@ -1,157 +0,0 @@ -import { Fragment, useCallback, useState } from "react"; -import { Entity, EllipsoidGraphics, useCesium, PolylineGraphics } from "resium"; -import { useParams } from "react-router-dom"; -import { useInterval, useThrottleFn } from "ahooks"; -import { useDispatch } from "react-redux"; - -const lowCircle = ( - -); -const highCircle = ( - -); -function Circles() { - const { viewer } = useCesium(); - const { type } = useParams(); - const dispatch = useDispatch(); - - if (type !== "2") return <>; - - const { run: showInfo } = useThrottleFn( - () => { - dispatch.data.updateImageLayer({ indianOcean: true }); - dispatch.data.update({ showSite: true }); - }, - { - wait: 1000, - } - ); - - const { run: closeInfo } = useThrottleFn( - () => { - dispatch.data.updateImageLayer({ indianOcean: false }); - dispatch.data.update({ showSite: false }); - }, - { - wait: 1000, - } - ); - - const Circle = ({ id, lon, lat, isLow, showTime }) => { - const circle = isLow ? lowCircle : highCircle; - const [show, setShow] = useState(false); - - useInterval(() => { - const { startTime, currentTime, shouldAnimate } = viewer.clock; - const _time = Math.floor( - currentTime.secondsOfDay - startTime.secondsOfDay - ); - if (!shouldAnimate) return; - if (_time === 40) { - showInfo(); - } else if (_time === 0) { - closeInfo(); - } - if (_time >= showTime) { - setShow(true); - } else { - if (!show) return; - setShow(false); - } - }, 300); - - if (!show) return <>; - - return ( - - - {circle} - - {show && ( - - - - )} - - {circle} - - - ); - }; - - return ( - - - - - - - - ); -} - -export default Circles; diff --git a/src/components/map/Layout/Entities/Cloud.jsx b/src/components/map/Layout/Entities/Cloud.jsx deleted file mode 100644 index f9e1513..0000000 --- a/src/components/map/Layout/Entities/Cloud.jsx +++ /dev/null @@ -1,146 +0,0 @@ -function Cloud() { - const viewer = new Cesium.Viewer("cesiumContainer"); - const scene = viewer.scene; - const position = Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706, 50); - - function getColor(colorName) { - return Cesium.Color[colorName.toUpperCase()]; - } - // These noise parameters are set to default, but can be changed - // to produce different cloud results. However, the noise is precomputed, - // so this cannot be changed dynamically. - const clouds = scene.primitives.add( - new Cesium.CloudCollection({ - noiseDetail: 16.0, - noiseOffset: Cesium.Cartesian3.ZERO, - }) - ); - - const cloudParameters = { - scaleWithMaximumSize: true, - scaleX: 25, - scaleY: 12, - maximumSizeX: 25, - maximumSizeY: 12, - maximumSizeZ: 15, - renderSlice: true, // if false, renders the entire surface of the ellipsoid - slice: 0.36, - brightness: 1.0, - color: "White", - colors: ["White", "Red", "Green", "Blue", "Yellow", "Gray"], - }; - - const cloud = clouds.add({ - position: position, - scale: new Cesium.Cartesian2( - cloudParameters.scaleX, - cloudParameters.scaleY - ), - maximumSize: new Cesium.Cartesian3( - cloudParameters.maximumSizeX, - cloudParameters.maximumSizeY, - cloudParameters.maximumSizeZ - ), - color: getColor(cloudParameters.color), - slice: cloudParameters.renderSlice ? cloudParameters.slice : -1.0, - brightness: cloudParameters.brightness, - }); - - Cesium.knockout.track(cloudParameters); - const toolbar = document.getElementById("toolbar"); - Cesium.knockout.applyBindings(cloudParameters, toolbar); - - Cesium.knockout - .getObservable(cloudParameters, "scaleWithMaximumSize") - .subscribe(function (newValue) { - if (Boolean(newValue)) { - cloudParameters.scaleX = cloudParameters.maximumSizeX; - cloudParameters.scaleY = cloudParameters.maximumSizeY; - } - }); - - Cesium.knockout - .getObservable(cloudParameters, "scaleX") - .subscribe(function (newValue) { - const value = Number(newValue); - cloud.scale = new Cesium.Cartesian2(value, cloud.scale.y); - }); - - Cesium.knockout - .getObservable(cloudParameters, "scaleY") - .subscribe(function (newValue) { - const value = Number(newValue); - cloud.scale = new Cesium.Cartesian2(cloud.scale.x, value); - }); - - Cesium.knockout - .getObservable(cloudParameters, "maximumSizeX") - .subscribe(function (newValue) { - const value = Number(newValue); - cloud.maximumSize = new Cesium.Cartesian3( - value, - cloud.maximumSize.y, - cloud.maximumSize.z - ); - if (cloudParameters.scaleWithMaximumSize) { - cloud.scale = new Cesium.Cartesian2(value, cloud.scale.y); - } - }); - - Cesium.knockout - .getObservable(cloudParameters, "maximumSizeY") - .subscribe(function (newValue) { - const value = Number(newValue); - cloud.maximumSize = new Cesium.Cartesian3( - cloud.maximumSize.x, - value, - cloud.maximumSize.z - ); - if (cloudParameters.scaleWithMaximumSize) { - cloud.scale = new Cesium.Cartesian2(cloud.scale.x, value); - } - }); - - Cesium.knockout - .getObservable(cloudParameters, "maximumSizeZ") - .subscribe(function (newValue) { - const value = Number(newValue); - cloud.maximumSize = new Cesium.Cartesian3( - cloud.maximumSize.x, - cloud.maximumSize.y, - value - ); - }); - - Cesium.knockout - .getObservable(cloudParameters, "renderSlice") - .subscribe(function (newValue) { - if (Boolean(newValue)) { - cloud.slice = Number(cloudParameters.slice); - } else { - cloud.slice = -1.0; - } - }); - - Cesium.knockout - .getObservable(cloudParameters, "slice") - .subscribe(function (newValue) { - cloud.slice = Number(newValue); - }); - - Cesium.knockout - .getObservable(cloudParameters, "color") - .subscribe(function (newValue) { - cloud.color = getColor(newValue); - }); - - Cesium.knockout - .getObservable(cloudParameters, "brightness") - .subscribe(function (newValue) { - cloud.brightness = Number(newValue); - }); - - viewer.camera.lookAt(position, new Cesium.Cartesian3(30, 30, -10)); -} - -export default Cloud; diff --git a/src/components/map/Layout/Entities/Cyclone.jsx b/src/components/map/Layout/Entities/Cyclone.jsx deleted file mode 100644 index 2372afb..0000000 --- a/src/components/map/Layout/Entities/Cyclone.jsx +++ /dev/null @@ -1,138 +0,0 @@ -import { Fragment, useCallback } from "react"; -import { Entity, ModelGraphics, useCesium } from "resium"; -import { useParams } from "react-router-dom"; -import arrowRound from "@/assets/arrow_round.glb"; - -let totalSeconds = 60; -let numberOfSamples = 120; -let wheelAngle = 0; - -function Cyclone() { - const { viewer } = useCesium(); - const { type } = useParams(); - - if (type !== "1") return <>; - - // 自转 - // const position = new Cesium.SampledPositionProperty(); - - // const velocityVectorProperty = new Cesium.VelocityVectorProperty( - // position, - // false - // ); - // const velocityVector = new Cesium.Cartesian3(); - // const wheelAngleProperty = new Cesium.SampledProperty(Number); - - // for (let index = 0; index < numberOfSamples; index++) { - // const factor = index / numberOfSamples; - // const time = Cesium.JulianDate.addSeconds( - // startTime, - // factor * totalSeconds, - // new Cesium.JulianDate() - // ); - // velocityVectorProperty.getValue(time, velocityVector); - // wheelAngle -= 3; - // wheelAngleProperty.addSample(time, wheelAngle); - // } - // const rotationProperty = new Cesium.CallbackProperty(function (time, result) { - // return Cesium.Quaternion.fromAxisAngle( - // Cesium.Cartesian3.UNIT_Z, - // wheelAngleProperty.getValue(time) || wheelAngleProperty._values[0], - // result - // ); - // }, false); - - const getPassTime = useCallback(() => { - const { startTime, currentTime } = viewer.clock; - const _time = Math.floor(currentTime.secondsOfDay - startTime.secondsOfDay); - return _time; - }, [viewer]); - - return ( - - - - - - - - - - - - - - - - - - - - - ); -} - -export default Cyclone; diff --git a/src/components/map/Layout/Entities/PlateauPolygon.jsx b/src/components/map/Layout/Entities/PlateauPolygon.jsx deleted file mode 100644 index 6551a3c..0000000 --- a/src/components/map/Layout/Entities/PlateauPolygon.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import { useCallback, useState } from "react"; -import { useParams } from "react-router-dom"; -import { Entity, PolygonGraphics, useCesium } from "resium"; -import { useInterval } from "ahooks"; - -function PlateauPolygon() { - const { type } = useParams(); - const { viewer } = useCesium(); - const [show, setShow] = useState(false); - - const showAnimate = useCallback(() => { - const { currentTime, stopTime } = viewer.clock; - const leftTime = Math.floor( - stopTime.secondsOfDay - currentTime.secondsOfDay - ); - - if (leftTime < (type === "1" ? 10 : 20)) { - setShow(true); - } else if (show) setShow(false); - }, [show, type]); - - useInterval(showAnimate, 100); - - if (type !== "1" && type !== "2") return <>; - - return ( - - - - ); -} - -export default PlateauPolygon; diff --git a/src/components/map/Layout/Entities/Udraft.jsx b/src/components/map/Layout/Entities/Udraft.jsx deleted file mode 100644 index 9db553d..0000000 --- a/src/components/map/Layout/Entities/Udraft.jsx +++ /dev/null @@ -1,36 +0,0 @@ -import { Entity, PolylineGraphics, useCesium } from "resium"; -import { Cartesian3 } from "cesium"; -import { useParams } from "react-router-dom"; -import { min } from "lodash-es"; - -function Updraft() { - const { viewer } = useCesium(); - const { type } = useParams(); - - if (type !== "1") return <>; - return ( - - - - ); -} - -export default Updraft; diff --git a/src/components/map/Layout/Entities/Watervapor.jsx b/src/components/map/Layout/Entities/Watervapor.jsx deleted file mode 100644 index bfdc6e9..0000000 --- a/src/components/map/Layout/Entities/Watervapor.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import { - Entity, - PointGraphics, - PolylineGraphics, - useCesium, - PathGraphics, - EllipseGraphics, -} from "resium"; -import { Color, Cartesian3 } from "cesium"; -import { Fragment } from "react"; - -function Watervapor() { - return ( - - - - - - ); -} - -export default Watervapor; diff --git a/src/components/map/Layout/InfoLayout/index.jsx b/src/components/map/Layout/InfoLayout/index.jsx deleted file mode 100644 index 64a2d20..0000000 --- a/src/components/map/Layout/InfoLayout/index.jsx +++ /dev/null @@ -1,69 +0,0 @@ -import { useParams } from "react-router-dom"; -import { useSelector } from "react-redux"; -import ChartInfoPanel from "./ChartInfoPanel"; -import TextInfoPanel from "./TextInfoPanel"; -import styles from "./index.module.less"; - -const textInfoList = [ - { - title: "两极协同—拉布拉多海海温偏暖控制夏季高原年代际增温", - content: - "基于再分析资料JRA55和线性斜压模式LBM的试验结果,在年代际尺度上,拉布拉多海海温偏暖会引起北大西洋至欧洲地区大气环流异常,导致青藏高原夏季出现年代际增温。 在年代际尺度上,已有研究表明受大气水汽含量增加影响的晴空向下长波辐射是促进夏季青藏高原增温的主要影响因子。通过对夏季高原温度和水汽含量的进一步分析,研究发现二者在年代际变化尺度以及空间分布具有很好的一致性,即均在1997/1998年显著增高(图70)。同时,对1980-1997和1998-2017两个时间段的位势扰动场,并结合T-N波通量诊断发现从西北大西洋沿欧亚大陆至高原北部存在Rossby波列(图71),该波列在高原东北部激发的异常反气旋对高原净水汽含量增加起到关键作用。经过线性斜压模式(LBM)试验验证,证实拉布拉多海海表温度的年代际增加是激发该Rossby波列的主要原因。因此,拉布拉多海海温年代际正异常激发的Rossby波列可以在高原东北部形成异常反气旋,该反气旋具有相当正压结构,一方面可以增加高原水汽净含量,通过增强长波辐射促进高原增温,另一方面,通过正压结构直接加热高原东北部近地气温", - }, - { - title: "两极协同—南极涛动有效调节青藏高原降水和加热", - content: - "利用ERA5再分析数据诊断和CESM印度洋海温强迫数值实验,发现5月的南极涛动正位相(AAO)激发原子阿蒙森海的纬向波列异常。该异常造成印度洋海温降低进而激发环流异常造成高原上空降水增加、感热通量降低。这一结果有助于提高青藏高原热源的预测技巧,改进亚洲夏季风的预测。", - }, - { - title: "两极协同—连接南极和北极的热带大西洋经向模的媒介作用", - content: - "利用GISTEMP资料,通过EEMD分解方法提取两极温度多年代际变化序列,发现南北极温度变化的跷跷板现象与大西洋多年代际振荡AMO紧密相关。而AMO与热带大西洋经向模AMM在年代际尺度上显著相关。ERA5再分析资料显示AMM可以通过Rossby波影响西南极的气温与海冰偶极子。因此,AMM可能在联系南北极气候变化的中起到了重要的媒介作用。", - }, - { - title: "三极联动影响青藏高原上层温度", - content: - "利用ERA5再分析数据,通过XGBoost和LightGBM方法对青藏高原对流层温度异常进行预测,结果表明该温度异常主要受地球三极信号影响,机器学习方法的高预测性对于南亚高压和亚洲夏季降水有重要指示意义。", - }, - { - title: "三极联动影响东亚夏季风", - content: - "利用39个CMIP6数值模拟数据训练卷积神经网络,利用NOAA20世纪再分析数据进行迁移学习、ERA5再分析数据进行测试,从而预测东亚夏季风。预测结果的热力图可视化表明尽管热带大洋对东亚夏季风的影响是最强的,然而不论是北极、南极还是青藏高原均显示有显著的前期信号(春季),这也表明地球三极可以协同影响东亚夏季风。关于东亚夏季风的预测一直是个难题。近期,我们利用机器学习识别了东亚夏季风的影响因子。地球三极的协同影响在东亚夏季风的归因图中得到了很好的体现:尽管热带大洋对东亚夏季风的影响是最强的,然而不论是北极、南极还是青藏高原均显示有显著的前期信号(春季),这也表明地球三极可以协同影响东亚夏季风。", - }, -]; - -const chartInfoList = [ - { - type: "1", - name: "sst", - }, - { - type: "3", - name: "tripolar", - }, - { - type: "4", - name: "tptt", - }, -]; - -function InfoLayout() { - const { type } = useParams(); - const { toolbar } = useSelector((state) => state.data); - - if (!toolbar.showPanel) return <>; - - const hasText = textInfoList[type - 1]; - const currentChartName = chartInfoList.find( - (item) => item.type === type - )?.name; - - return ( -
- {!!hasText ? : } - -
- ); -} - -export default InfoLayout; diff --git a/src/components/map/Layout/InfoLayout/index.module.less b/src/components/map/Layout/InfoLayout/index.module.less deleted file mode 100644 index 2727332..0000000 --- a/src/components/map/Layout/InfoLayout/index.module.less +++ /dev/null @@ -1,53 +0,0 @@ -.infoLayout :global { - position: absolute; - top: 0; - width: 100%; - height: 100%; - display: flex; - justify-content: space-between; - align-items: flex-end; - pointer-events: none; - - .textInfoPanel { - width: 450px; - height: 400px; - padding: 12px; - border: 1px solid #04fbfd; - color: #02f9ff !important; - background-color: #1f485690; - margin-bottom: 168px; - pointer-events: auto; - display: flex; - flex-direction: column; - - .title { - font-size: 20px; - font-weight: 600; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - margin-bottom: 12px; - } - - .content { - flex: 1; - text-indent: 32px; - line-height: 1.5; - } - } - - .chartInfoPanel { - width: 450px; - height: 400px; - pointer-events: auto; - padding: 12px; - border: 1px solid #04fbfd; - color: #02f9ff !important; - background-color: #1f485690; - margin-bottom: 168px; - // margin-left: 12px; - - // .echarts-for-react { - // } - } -} diff --git a/src/components/map/Layout/index.module.less b/src/components/map/Layout/index.module.less index 85bcbf6..6500a14 100644 --- a/src/components/map/Layout/index.module.less +++ b/src/components/map/Layout/index.module.less @@ -114,7 +114,7 @@ } &::-webkit-scrollbar-thumb { border-radius: 6px; - background: #04fbfd; + background: #00a2a2; } &::-webkit-scrollbar-track { border-radius: 6px; diff --git a/src/models/data.js b/src/models/data.js index 04f40bb..1771689 100644 --- a/src/models/data.js +++ b/src/models/data.js @@ -1,7 +1,6 @@ export const data = { state: { showSite: false, - showPanelHighlight: false, toolbar: {}, imageLayer: { labrador: undefined,