This commit is contained in:
Aifeilong 2023-10-13 17:35:36 +08:00
parent abce91fc07
commit 29090b3db8
51 changed files with 1125 additions and 708 deletions

BIN
src/assets/heatflux.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
src/assets/rain.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 B

View File

@ -18,10 +18,6 @@ function CustomClock() {
// onTick={(clock) => {
// if (!clock.shouldAnimate) return;
// }}
// onStop={() => {
// if (toolbar.showPanel === undefined)
// dispatch.data.updateToolbar({ showPanel: true });
// }}
/>
);
}

View File

@ -1,20 +1,20 @@
import { useCallback, useEffect, useState } from "react";
import { useSelector } from "react-redux";
import { Scrollbars } from "react-custom-scrollbars-2";
import { useInterval } from "ahooks";
import { useLocation } from "react-router-dom";
let index = 0;
function TextInfoPanel({ content }) {
const location = useLocation();
const showNumberPerTimes = 1;
const { toolbar } = useSelector((state) => state.data);
const [delay, setDelay] = useState(80);
const [contentText, setContentText] = useState("");
useEffect(() => {
index = 0;
}, [toolbar]);
}, [location]);
const showContent = useCallback(() => {
const isFinished = contentText.length >= content.length;
@ -23,7 +23,10 @@ function TextInfoPanel({ content }) {
index += showNumberPerTimes;
return text + content[index - 1];
});
} else setDelay(undefined);
} else {
index = 0;
setDelay(undefined);
}
}, [contentText]);
useInterval(showContent, delay);

View File

@ -24,6 +24,7 @@ function HeatmapImageLayer() {
key={`ImageryLayer-${name}`}
imageryProvider={tempProvider}
show={true}
alpha={0.6}
/>
);
}

View File

@ -15,7 +15,9 @@ const colorBar = [
function Legend() {
return (
<div className="legend">
<div className="legend-title"></div>
<div className="legend-title">
对东亚夏季风具有显著影响的信号表面温度和表面向上感热通量分布
</div>
<div className="colorbar">
{colorBar.map((color, index) => {
return (

View File

@ -24,11 +24,8 @@ export default function DomainFive() {
/>
</div>
<div className="right-panel">
<div className="top-panel">
<FormPanel setShow={setShow} />
</div>
<div className="bottom-panel" />
</div>
{show && <HeatmapImageLayer />}
<Legend />
</MapLayout>

View File

@ -5,46 +5,49 @@ import { Entity, LabelGraphics } from "resium";
const points = [
{
position: [13, -70, 30, -67],
name: "sic_s",
name: "南极附近海冰异常",
// name: "sic_s",
},
{
position: [93, 29, 99, 35],
name: "sc_tp",
name: "青藏高原上空积雪异常",
// name: "sc_tp",
},
{
position: [68, 48, 76, 52],
name: "sc_ea",
name: "欧亚大陆上空积雪异常",
// name: "sc_ea",
},
{
position: [150, 60, 160, 70],
name: "ts_ac",
name: "表面温度异常ts_ac",
},
{
position: [130, 20, 140, 28],
name: "ts_wp",
name: "表面温度异常ts_wp",
},
{
position: [150, 12, 175, 22],
name: "ts_np",
name: "表面温度异常ts_np",
},
{
position: [-110, 20, -95, 35],
name: "ts_arn",
name: "表面温度异常ts_arn",
},
{
position: [10, -12, 45, -2],
name: "ts_af",
name: "表面温度异常ts_af",
},
{ position: [80, 10, 110, 20], name: "ts_io" },
{ position: [-150, -10, -40, 5], name: "ts_ep" },
{ position: [65, -62, 90, -57], name: "ts_aa" },
{ position: [80, 10, 110, 20], name: "表面温度异常ts_io" },
{ position: [-150, -10, -40, 5], name: "表面温度异常ts_ep" },
{ position: [65, -62, 90, -57], name: "表面温度异常ts_aa" },
{
position: [145, -30, 155, -20],
name: "ts_ea",
name: "表面温度异常ts_ea",
},
{ position: [165, -45, 180, -30], name: "ts_nz" },
{ position: [-140, -60, -110, -45], name: "ts_sp" },
{ position: [-70, -78, -50, -70], name: "ts_wd" },
{ position: [165, -45, 180, -30], name: "表面温度异常ts_nz" },
{ position: [-140, -60, -110, -45], name: "表面温度异常ts_sp" },
{ position: [-70, -78, -50, -70], name: "表面温度异常ts_wd" },
];
function Labels() {

View File

@ -1,42 +0,0 @@
const colorBar = [
"#f6df70",
"#f4c53a",
"#f5aa38",
"#f29438",
"#ec6b38",
"#ea5418",
];
function Legend() {
return (
<div className="legend">
<div className="legend-title">xxx</div>
<div className="colorbar">
{colorBar.map((color, index) => {
return (
<div
key={`colorbar-item-${index}`}
className="colorbar-item"
style={{ backgroundColor: color }}
title={`${(-3.2 + index * 0.2).toFixed(1)}~${(
-3.2 +
(index + 1) * 0.2
).toFixed(1)}`}
/>
);
})}
</div>
<div className="legend-text">
{[0.2, 0.3, 0.4, 0.5, 0.6, ""].map((item, index) => {
return (
<div key={`legend-text-item-${index}`} className="legend-text-item">
{item}
</div>
);
})}
</div>
</div>
);
}
export default Legend;

View File

@ -7,7 +7,6 @@ import FormPanel from "./FormPanel";
import ChartPanel from "./ChartPanel";
import RectangleLayer from "./RectangleLayer";
import { useState } from "react";
import Legend from "./Legend";
import Labels from "./Labels";
export default function DomainFour() {
@ -25,13 +24,12 @@ export default function DomainFour() {
XGBoos是基于梯度提升决策树方法的分类和回归模型 LightGBM也是一种基于树的梯度提升方法可以解决高维输入变量问题这两个机器学习模型由许多简单的弱学习器也称为小回归模型组成最终的预测是所有弱学习器的预测的加权和 此外作为Boosting树模型XGBoost和LightGBM对多重共线性不敏感这减少了特征的同时交互提高了预测能力`}
/>
</div>
<div className="right-panel">
<div className="right-panel" style={{ top: 84 }}>
<div className="top-panel">
<FormPanel setShow={setShow} />
</div>
<div className="bottom-panel">{show && <ChartPanel />}</div>
</div>
{/* <Legend /> */}
<Labels />
<RectangleLayer />
</MapLayout>

View File

@ -35,10 +35,6 @@ function ChartPanel() {
const { shouldAnimate } = viewer.clock;
// useInterval(() => {
// console.log(currentTime, startTime, stopTime);
// }, 100);
useEffect(() => {
if (shouldAnimate) {
setIsHighlight(true);
@ -67,12 +63,6 @@ function ChartPanel() {
bottom: "3%",
containLabel: true,
},
// toolbox: {
// feature: {
// //
// saveAsImage: {},
// },
// },
xAxis: {
type: "category",
boundaryGap: false,
@ -85,6 +75,9 @@ function ChartPanel() {
color: "#04fbfd",
},
},
axisLabel: {
interval: 4,
},
},
yAxis: {
type: "value",

View File

@ -1,11 +1,9 @@
import { Cartesian3, EasingFunction, Math } from "cesium";
import { useDispatch } from "react-redux";
import { useCesium } from "resium";
function CustomFlyTo() {
const { viewer } = useCesium();
const { camera } = viewer;
const dispatch = useDispatch();
function cameraFlyToLine() {
const step1 = {
@ -53,9 +51,6 @@ function CustomFlyTo() {
setTimeout(function () {
camera.flyTo(step1);
}, 5000);
dispatch.data.updateImageLayer({
labrador: true,
});
},
};

View File

@ -5,7 +5,7 @@ import waterwapor from "@/assets/waterwapor.png";
function EntityLegend() {
return (
<div className="entity-legend">
<div className="entity-legend" style={{ bottom: 116 }}>
<div className="entity-legend-item">
<div style={{ fontWeight: 800, color: "#04fafc" }}>- - - -</div>
<div className="entity-legend-item-name">Rossby wave path</div>

View File

@ -6,7 +6,8 @@ import { useInterval } from "ahooks";
function Labels() {
const { viewer } = useCesium();
const [show, setShow] = useState(false);
const [showOne, setShowOne] = useState(false);
const [showTwo, setShowTwo] = useState(false);
const showAnimate = useCallback(() => {
const { currentTime, stopTime } = viewer.clock;
@ -14,18 +15,21 @@ function Labels() {
stopTime.secondsOfDay - currentTime.secondsOfDay
);
if (leftTime < 20) {
setShowOne(true);
} else if (showOne) setShowOne(false);
if (leftTime < 5) {
setShow(true);
} else if (show) setShow(false);
}, [show]);
setShowTwo(true);
} else if (showTwo) setShowTwo(false);
}, [showOne, showTwo, viewer]);
useInterval(showAnimate, 100);
useInterval(showAnimate, 300);
return (
<Fragment>
<Entity position={Cartesian3.fromDegrees(90, 27, 0)}>
<Entity position={Cartesian3.fromDegrees(-60, 65, 0)}>
<LabelGraphics
text={"青藏高原温度异常"}
text={"拉布拉多海海温偏暖"}
font="24px Helvetica"
fillColor={Color.SKYBLUE}
outlineColor={Color.BLACK}
@ -34,9 +38,9 @@ function Labels() {
eyeOffset={new Cartesian2(0, 200000)}
/>
</Entity>
<Entity position={Cartesian3.fromDegrees(-55, 45, 0)}>
<Entity show={showOne} position={Cartesian3.fromDegrees(-20, 55, 0)}>
<LabelGraphics
text={"拉布拉多海温度异常"}
text={"北大西洋至欧洲地区大气环流异常"}
font="24px Helvetica"
fillColor={Color.SKYBLUE}
outlineColor={Color.BLACK}
@ -45,7 +49,7 @@ function Labels() {
eyeOffset={new Cartesian2(0, 200000)}
/>
</Entity>
<Entity show={show} position={Cartesian3.fromDegrees(98, 48, 0)}>
<Entity show={showTwo} position={Cartesian3.fromDegrees(98, 48, 0)}>
<LabelGraphics
text={"barotropic"}
font="24px Helvetica"
@ -56,6 +60,17 @@ function Labels() {
eyeOffset={new Cartesian2(0, 1200000)}
/>
</Entity>
<Entity show={showTwo} position={Cartesian3.fromDegrees(90, 27, 0)}>
<LabelGraphics
text={"青藏高原夏季出现年代际增温"}
font="24px Helvetica"
fillColor={Color.SKYBLUE}
outlineColor={Color.BLACK}
outlineWidth={2}
style={LabelStyle.FILL_AND_OUTLINE}
eyeOffset={new Cartesian2(0, 200000)}
/>
</Entity>
</Fragment>
);
}

View File

@ -1,36 +0,0 @@
import { useCallback, useState } from "react";
import { Entity, PolygonGraphics, useCesium } from "resium";
import { useInterval } from "ahooks";
import { Cartesian3, Color } from "cesium";
function PlateauPolygon() {
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 (
<Entity id="plateau" show={show}>
<PolygonGraphics
hierarchy={Cartesian3.fromDegreesArray([
// 85, 30, 91, 30, 91, 35, 85, 35,
80, 31, 84, 29.5, 87.4, 28, 91, 28, 98, 29, 94, 35, 79, 34.4, 80, 31,
])}
material={new Color(1, 0, 0, 0.1)}
/>
</Entity>
);
}
export default PlateauPolygon;

View File

@ -4,7 +4,6 @@ import CustomClock from "@/components/common/CustomClock";
import TextInfoPanel from "@/components/common/TextInfoPanel";
import CustomFlyTo from "./CustomFlyTo";
import Point from "./Point";
import PlateauPolygon from "./PlateauPolygon";
import Cyclone from "./Cyclone";
import Barotropic from "./Barotorpic";
import WavePoints from "./WavePoints";
@ -26,7 +25,6 @@ export default function DomainOne() {
<CustomClock />
<CustomFlyTo />
<Point />
<PlateauPolygon />
<div className="left-panel one">
<TextInfoPanel content="基于再分析资料JRA55和线性斜压模式LBM的试验结果在年代际尺度上拉布拉多海海温偏暖会引起北大西洋至欧洲地区大气环流异常导致青藏高原夏季出现年代际增温。" />
</div>

View File

@ -6,6 +6,7 @@ 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,
@ -27,6 +28,8 @@ const AntarcticaData = [
-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,
@ -48,6 +51,8 @@ const ArcticData = [
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,
@ -109,6 +114,9 @@ function ChartPanel() {
color: "#04fbfd",
},
},
axisLabel: {
interval: 4,
},
},
yAxis: {
type: "value",

View File

@ -37,9 +37,16 @@ function Legend({ style }) {
})}
</div>
<div className="legend-text">
{[-0.8, -0.4, 0, 0.4, 0.8, ""].map((item, index) => {
{[-0.8, -0.4, 0, 0.4, 0.8].map((item, index) => {
return (
<div key={`legend-text-item-${index}`} className="legend-text-item">
<div
key={`legend-text-item-${index}`}
className="legend-text-item"
style={{
textAlign:
index === 2 ? "center" : index < 2 ? "left" : "right",
}}
>
{item}
</div>
);

View File

@ -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 (
<div className={styles.legend}>
<div className="legend-title">全球温度异常空间分布</div>
<div className="colorbar">
{colorBar.map((color, index) => {
return (
<div
key={`colorbar-item-${index}`}
className="colorbar-item"
style={{ backgroundColor: color }}
/>
);
})}
</div>
<div className="legend-text">
{["", -1, -0.8, -0.6, -0.4, -0.2, 0, 0.2, 0.4, 0.6, 0.8, 1.0, ""].map(
(item, index) => {
return (
<div
key={`legend-text-item-${index}`}
className="legend-text-item"
style={{
textAlign:
index === 6 ? "center" : index < 6 ? "left" : "right",
}}
>
{item}
</div>
);
}
)}
</div>
</div>
);
}
export default Legend;

View File

@ -1,6 +1,6 @@
import TextInfoPanel from "@/components/common/TextInfoPanel";
import ChartPanel from "../ChartPanel";
import Legend from "../Legend";
import Legend from "./Legend";
import ViewerOne from "./ViewerOne";
import ViewerTwo from "./ViewerTwo";
import ViewerThree from "./ViewerThree";
@ -14,12 +14,14 @@ function SceneOne() {
<ViewerTwo />
<ViewerThree />
</div>
<div className="domain-info">
<div className="left-panel">
<TextInfoPanel content="利用GISTEMP资料通过EEMD分解方法提取两极温度多年代际变化序列发现南北极温度变化的跷跷板现象与大西洋多年代际振荡AMO紧密相关。而AMO与热带大西洋经向模AMM在年代际尺度上显著相关。ERA5再分析资料显示AMM可以通过Rossby波影响西南极的气温与海冰偶极子。因此AMM可能在联系南北极气候变化的中起到了重要的媒介作用。" />
</div>
<div className="right-panel">
<ChartPanel />
</div>
</div>
<Legend />
</div>
);

View File

@ -1,12 +1,13 @@
.sceneOne :global {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
background-color: gray;
background-color: darkslategray;
.domain-viewers {
width: 100%;
height: calc(100% - 250px);
flex: 1;
display: flex;
.domain-viewer {
@ -18,4 +19,151 @@
}
}
}
.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%;
.text-info-panel {
width: 100%;
height: 100%;
padding: 12px;
border: 1px solid #04fbfd;
color: #02f9ff !important;
background-color: #1f485690;
pointer-events: auto;
font-size: 18px;
text-indent: 2em;
line-height: 1.5;
z-index: 999;
border-radius: 8px;
}
}
.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: #1f485690;
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;
}
}
}
}
.chart-info-panel {
width: 100%;
height: 100%;
pointer-events: auto;
padding: 12px;
border: 1px solid #04fbfd;
color: #02f9ff !important;
background-color: #1f485690;
transition: all 0.3s ease-in-out;
border-radius: 8px;
}
}
}
}
.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: #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: 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;
}
}
}

View File

@ -0,0 +1,11 @@
import { Cartesian3 } from "cesium";
import { CameraFlyTo } from "resium";
export default function CustomFlyTo() {
return (
<CameraFlyTo
duration={5}
destination={Cartesian3.fromDegrees(-40, -30, 16000000)}
/>
);
}

View File

@ -0,0 +1,32 @@
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_2_c_hgt_Layer1";
function HGTImageLayer() {
const tempProvider = useMemo(
() =>
new WebMapServiceImageryProvider({
url: url,
layers: name,
parameters: {
service: "WMS",
format: "image/png",
transparent: true,
},
}),
[name, url]
);
return (
<ImageryLayer
key={`ImageryLayer-${name}`}
imageryProvider={tempProvider}
show={true}
alpha={0.6}
/>
);
}
export default HGTImageLayer;

View File

@ -0,0 +1,168 @@
import { Fragment } from "react";
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",
];
const colorBar2 = [
"#7F20F0",
"#0e03fd",
"#3B5CE3",
"#2684f6",
"#09b0ff",
"#56c4f4",
"#b0ddf3",
"#FEF687",
"#ffcd1e",
"#ff9700",
"#fe5100",
"#f60100",
"#cd0d0c",
"#FFB1B1",
];
function Legend({ style }) {
return (
<Fragment>
<div className={styles.legend} style={{ bottom: 14 }}>
<div className="legend-title">
南半球冬季南极半岛区域地表气温年代际分量(单位K)
</div>
<div className="colorbar">
{colorBar.map((color, index) => {
return (
<div
key={`colorbar-item-${index}`}
className="colorbar-item"
style={{ backgroundColor: color }}
/>
);
})}
</div>
<div className="legend-text">
{[-0.8, -0.4, 0, 0.4, 0.8].map((item, index) => {
return (
<div
key={`legend-text-item-${index}`}
className="legend-text-item"
style={{
textAlign:
index === 2 ? "center" : index < 2 ? "left" : "right",
}}
>
{item}
</div>
);
})}
</div>
</div>
<div className={styles.legend} style={{ bottom: 116 }}>
<div className="legend-title">
南半球冬季热带大西洋海表温度(单位K)
</div>
<div className="colorbar">
{colorBar.map((color, index) => {
return (
<div
key={`colorbar-item-${index}`}
className="colorbar-item"
style={{ backgroundColor: color }}
/>
);
})}
</div>
<div className="legend-text">
{[-0.2, -0.1, 0, 0.1, 0.2].map((item, index) => {
return (
<div
key={`legend-text-item-${index}`}
className="legend-text-item"
style={{
textAlign:
index === 2 ? "center" : index < 2 ? "left" : "right",
}}
>
{item}
</div>
);
})}
</div>
</div>
<div className={styles.legend} style={{ bottom: 220 }}>
<div className="legend-title">
南半球冬季200hPa涡动位势高度异常(单位gpm)
</div>
<div className="colorbar">
{colorBar2.map((color, index) => {
return (
<div
key={`colorbar-item-${index}`}
className="colorbar-item"
style={{ backgroundColor: color }}
/>
);
})}
</div>
<div className="legend-text">
{[
"",
-15,
"",
"",
-10,
"",
"",
-5,
"",
"",
0,
"",
"",
5,
"",
"",
10,
"",
"",
15,
"",
].map((item, index) => {
return (
<div
key={`legend-text-item-${index}`}
className="legend-text-item"
style={{
textAlign: "center",
// textAlign:
// index === 2 ? "center" : index < 2 ? "left" : "right",
}}
>
{item}
</div>
);
})}
</div>
</div>
</Fragment>
);
}
export default Legend;

View File

@ -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_3_2_a_sst_Layer11";
function SSTImageLayer() {
const tempProvider = useMemo(
() =>
new WebMapServiceImageryProvider({
url: url,
layers: name,
parameters: {
service: "WMS",
format: "image/png",
transparent: true,
},
}),
[name, url]
);
return (
<ImageryLayer
key={`ImageryLayer-${name}`}
imageryProvider={tempProvider}
show={true}
/>
);
}
export default SSTImageLayer;

View File

@ -6,8 +6,37 @@ for (let year = 1950; year <= 2020; year++) {
years.push(year);
}
const SATData = [];
const SSTData = [];
const SATData = [
0.429685116, 0.689356685, 0.873310268, 0.967009068, 1.003803611, 1.036836505,
1.102791905, 1.167440891, 1.17760396, 1.124955773, 1.01085484, 0.859563529,
0.71795398, 0.621988237, 0.597750485, 0.598902822, 0.532728314, 0.342583299,
-0.024368536, -0.493425161, -0.9331339, -1.273939967, -1.476933718,
-1.554893732, -1.475591898, -1.279159307, -1.064661622, -0.922379196,
-0.918932199, -1.11133039, -1.43390727, -1.801198602, -2.015464544,
-2.0192132, -1.855630398, -1.571777582, -1.243658185, -1.025858879,
-0.930935264, -0.869536936, -0.719216347, -0.443715662, -0.130082458,
0.151266798, 0.290564477, 0.236905336, 0.056489315, -0.14797987, -0.240299582,
-0.17908895, 0.034180526, 0.390409917, 0.806362808, 1.218396306, 1.526551723,
1.709755898, 1.74579525, 1.635397196, 1.387297392, 1.11059618, 0.822289705,
0.581417382, 0.404003143, 0.258351594, 0.119950205, 0.01043385, -0.079203822,
-0.124122731, -0.129883319, -0.031686373, 0.16967541,
];
const SSTData = [
1.212813735, 1.429750562, 1.521738529, 1.469550729, 1.316006422, 1.125649571,
0.986746848, 0.915246606, 0.915339172, 0.951494932, 0.970494628, 0.926436901,
0.79472959, 0.602078795, 0.395833611, 0.238499194, 0.111982882, -0.020530188,
-0.205848336, -0.508775234, -0.927411437, -1.398505688, -1.82903564,
-2.119443178, -2.199231863, -2.063818693, -1.770155549, -1.401496291,
-1.074879527, -0.84980756, -0.789860964, -0.888945699, -1.096244812,
-1.2922014, -1.367582083, -1.272272468, -1.035024524, -0.74733454,
-0.570655763, -0.564872324, -0.715999901, -0.92039144, -1.045125961,
-0.965325236, -0.686810315, -0.263357043, 0.127493069, 0.37617141,
0.430310369, 0.350464702, 0.266462713, 0.269338131, 0.410024047, 0.642185688,
0.879462898, 1.070014119, 1.173520446, 1.198034406, 1.178782582, 1.129892349,
1.029724956, 0.902248025, 0.708711326, 0.465828151, 0.227744579, 0.055393901,
0.002014907, 0.076390825, 0.281141222, 0.559546649, 0.895649433,
];
function SceneChartPanel() {
const option = {
@ -18,7 +47,7 @@ function SceneChartPanel() {
trigger: "axis",
},
legend: {
data: ["SAT_SVD1", "SST_SVD1"],
data: ["南极半岛", "热带大西洋"],
textStyle: { color: "#04fbfd", cursor: "point" },
},
animationDuration: 10 * 1000,
@ -67,27 +96,27 @@ function SceneChartPanel() {
dataZoom: { type: "inside", start: 0, end: 100 },
series: [
{
name: "SAT_SVD1",
name: "南极半岛",
type: "line",
stack: "Total",
// stack: "Total",
data: SATData,
smooth: true,
color: "red",
color: "blue",
symbol: "none",
itemStyle: {
color: "red",
color: "blue",
},
},
{
name: "SST_SVD1",
name: "热带大西洋",
type: "line",
// stack: "Total",
data: SSTData,
smooth: true,
color: "blue",
color: "red",
symbol: "none",
itemStyle: {
color: "blue",
color: "red",
},
},
],

View File

@ -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_3_2_b_ts_Layer11";
function TSImageLayer() {
const tempProvider = useMemo(
() =>
new WebMapServiceImageryProvider({
url: url,
layers: name,
parameters: {
service: "WMS",
format: "image/png",
transparent: true,
},
}),
[name, url]
);
return (
<ImageryLayer
key={`ImageryLayer-${name}`}
imageryProvider={tempProvider}
show={true}
/>
);
}
export default TSImageLayer;

View File

@ -1,18 +1,33 @@
import Legend from "../Legend";
import MapLayout from "@/components/map/Layout";
import Legend from "./Legend";
import ViewerOne from "../SceneOne/ViewerOne";
import SceneChartPanel from "./SceneChartPanel";
import CustomFlyTo from "./CustomFlyTo";
import SSTImageLayer from "./SSTImageLayer";
import TSImageLayer from "./TSImageLayer";
import HGTImageLayer from "./HGTImageLayer";
function SceneTwo() {
return (
<div className="scene_two">
<ViewerOne>
<MapLayout>
<div className="title">
两极协同连接南极和北极的热带大西洋经向模的媒介作用
</div>
<div className="right-panel one" style={{ height: "unset" }}>
<div className="bottom-panel">
<SceneChartPanel />
</div>
</div>
<Legend style={{ bottom: "40px" }} />
</ViewerOne>
<CustomFlyTo />
<Legend />
<HGTImageLayer />
<SSTImageLayer />
<TSImageLayer />
</MapLayout>
{/* <ViewerOne>
</ViewerOne> */}
</div>
);
}

View File

@ -0,0 +1,52 @@
.legend :global {
position: absolute;
bottom: 270px;
width: 450px;
left: 12px;
// 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: 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;
}
}
}

View File

@ -22,6 +22,7 @@
color: #02f9ff !important;
background-color: #1f485690;
transition: all 0.3s ease-in-out;
border-radius: 8px;
}
}
}
@ -32,163 +33,7 @@
top: 8px;
}
.left-panel {
position: absolute;
left: 12px;
bottom: 12px;
width: 450px;
display: flex;
flex-direction: column;
height: 230px;
.top-panel {
flex: 1;
}
.bottom-panel {
flex: 1;
}
.text-info-panel {
width: 100%;
height: 100%;
padding: 12px;
border: 1px solid #04fbfd;
color: #02f9ff !important;
background-color: #1f485690;
pointer-events: auto;
font-size: 18px;
text-indent: 2em;
line-height: 1.5;
z-index: 999;
}
}
.right-panel {
position: absolute;
right: 12px;
bottom: 12px;
width: 450px;
height: 230px;
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: #1f485690;
.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;
}
}
}
}
.chart-info-panel {
width: 100%;
height: 100%;
pointer-events: auto;
padding: 12px;
border: 1px solid #04fbfd;
color: #02f9ff !important;
background-color: #1f485690;
transition: all 0.3s ease-in-out;
}
.highlight {
outline: 4px solid #04fbfd;
box-shadow: 0 0 30px #04e0fd;
}
}
.one {
top: 50%;
}
}
.legend :global {
position: absolute;
bottom: 270px;
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: 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;
}
}
}

View File

@ -6,8 +6,7 @@ import {
PolylineGraphics,
LabelGraphics,
} from "resium";
import { useInterval, useThrottleFn } from "ahooks";
import { useDispatch } from "react-redux";
import { useInterval } from "ahooks";
import {
Cartesian3,
Color,
@ -43,27 +42,6 @@ const highCircle = (
function Circles() {
const { viewer } = useCesium();
const dispatch = useDispatch();
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;
@ -75,11 +53,6 @@ function Circles() {
currentTime.secondsOfDay - startTime.secondsOfDay
);
if (!shouldAnimate) return;
if (_time === 20) {
showInfo();
} else if (_time === 0) {
closeInfo();
}
if (_time >= showTime) {
setShow(true);
} else {

View File

@ -1,27 +1,10 @@
import { Cartesian3, EasingFunction, Math } from "cesium";
import { useDispatch } from "react-redux";
import { Cartesian3, EasingFunction } from "cesium";
import { useCesium } from "resium";
function CustomFlyTo() {
const dispatch = useDispatch();
const { viewer, camera } = useCesium();
function cameraFlyToLine() {
// //
// const antarcticalOptions = {
// destination: Cartesian3.fromDegrees(88, -89, 1600000),
// orientation: {
// heading: Math.toRadians(15.0),
// pitch: Math.toRadians(-60),
// roll: 0.0,
// },
// duration: 5,
// complete: function () {
// camera.flyTo(area1Options);
// },
// easingFunction: EasingFunction.LINEAR_NONE,
// };
const area1Options = {
destination: Cartesian3.fromDegrees(-110, -86, 10000000),
duration: 5,
@ -32,7 +15,6 @@ function CustomFlyTo() {
},
complete: function () {
viewer.clock.shouldAnimate = true;
dispatch.data.updateToolbar({ showPanel: true });
setTimeout(() => {
camera.flyTo(area2Options);
}, 5000);
@ -72,65 +54,14 @@ function CustomFlyTo() {
roll: -6,
},
complete: function () {
setTimeout(() => {
camera.flyTo({
duration: 5,
destination: Cartesian3.fromDegrees(90, -12, 14000000),
duration: 7,
destination: Cartesian3.fromDegrees(90, 5, 14000000),
});
}, 5 * 1000);
},
easingFunction: EasingFunction.LINEAR_NONE,
};
const area5Options = {
destination: Cartesian3.fromDegrees(95, -30, 10000000),
duration: 5,
complete: function () {
// camera.flyTo(sideViewOptions);
setTimeout(() => {
camera.flyTo({
duration: 5,
destination: Cartesian3.fromDegrees(90, -12, 14000000),
});
}, 5 * 1000);
},
easingFunction: EasingFunction.LINEAR_NONE,
};
//
// const sideViewOptions = {
// destination: Cartesian3.fromDegrees(80, -60, 16000000),
// // destination: Cesium.Cartesian3.fromDegrees(130, -10.5, 20000000),
// duration: 5,
// orientation: {
// heading: Math.toRadians(-10.0),
// pitch: Math.toRadians(-92),
// roll: 6.0,
// },
// complete: () => {
// viewer.clock.shouldAnimate = true;
// dispatch.data.updateToolbar({ showPanel: true });
// setTimeout(() => {
// camera.flyTo({
// duration: 5,
// destination: Cartesian3.fromDegrees(90, -12, 14000000),
// });
// }, 40 * 1000);
// },
// };
const xx = {
destination: Cartesian3.fromDegrees(65, -55, 10000000),
duration: 5,
orientation: {
heading: 6,
pitch: -1.3,
roll: -6,
},
easingFunction: EasingFunction.LINEAR_NONE,
};
camera.flyTo(area1Options);
}
cameraFlyToLine();

View File

@ -6,7 +6,7 @@ import moisture from "@/assets/moisture.png";
function EntityLegend() {
return (
<div className="entity-legend" style={{ bottom: 40 }}>
<div className="entity-legend" style={{ bottom: 116 }}>
<div className="entity-legend-item">
<div>
<img src={low} alt="low" width={32} />

View File

@ -1,12 +1,26 @@
import { useMemo } from "react";
import { ImageryLayer } from "resium";
import { useCallback, useMemo, useState } from "react";
import { ImageryLayer, useCesium } from "resium";
import { WebMapServiceImageryProvider } from "cesium";
import { useSelector } from "react-redux";
import { useInterval } from "ahooks";
const url = "http://analysis.tpdc.ac.cn/gs/geoserver/phitrellis/wms";
function IndianOceanSST() {
const { imageLayer } = useSelector((state) => state.data);
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 < 5) {
setShow(true);
} else if (show) setShow(false);
}, [show]);
useInterval(showAnimate, 100);
const tempProvider = useMemo(
() =>
@ -24,6 +38,7 @@ function IndianOceanSST() {
return (
<ImageryLayer
show={show}
key={`ImageryLayer-india-ocean`}
imageryProvider={tempProvider}
/>

View File

@ -1,11 +1,44 @@
import { useInterval } from "ahooks";
import { Cartesian2, Cartesian3, Color, LabelStyle } from "cesium";
import { Fragment } from "react";
import { Entity, LabelGraphics } from "resium";
import { Fragment, useCallback, useState } from "react";
import { Entity, LabelGraphics, useCesium } from "resium";
function Labels() {
const { viewer } = useCesium();
const [showIO, setShowIO] = useState(false);
const [showTB, setShowTB] = useState(false);
const showAnimate = useCallback(() => {
const { currentTime, stopTime } = viewer.clock;
const leftTime = Math.floor(
stopTime.secondsOfDay - currentTime.secondsOfDay
);
if (leftTime < 5) {
setShowIO(true);
setShowTB(true);
} else if (showIO) {
setShowIO(false);
setShowTB(false);
}
}, [showIO]);
useInterval(showAnimate, 100);
return (
<Fragment>
<Entity position={Cartesian3.fromDegrees(73, -7, 0)}>
<Entity position={Cartesian3.fromDegrees(-110, -60, 0)}>
<LabelGraphics
text={"5月南极涛动"}
font="24px Helvetica"
fillColor={Color.SKYBLUE}
outlineColor={Color.BLACK}
outlineWidth={2}
style={LabelStyle.FILL_AND_OUTLINE}
eyeOffset={new Cartesian2(0, 200000)}
/>
</Entity>
<Entity show={showIO} position={Cartesian3.fromDegrees(73, -7, 0)}>
<LabelGraphics
text={"印度洋海温异常"}
font="24px Helvetica"
@ -16,7 +49,7 @@ function Labels() {
eyeOffset={new Cartesian2(0, 200000)}
/>
</Entity>
<Entity position={Cartesian3.fromDegrees(94, 24, 0)}>
<Entity show={showTB} position={Cartesian3.fromDegrees(94, 24, 0)}>
<LabelGraphics
text={"6月青藏高原降水和加热"}
font="24px Helvetica"

View File

@ -1,85 +1,42 @@
import { Fragment } from "react";
const colorBar = [
"#ffc53d",
"#ffd666",
"#ffe58f",
"#fff1b8",
"#fffbe6",
"#e6f7ff",
"#bae7ff",
"#91d5ff",
"#69c0ff",
"#40a9ff",
];
const colorBar2 = [
"#73d13d",
"#95de64",
"#b7eb8f",
"#d9f7be",
"#f6ffed",
"#fff1f0",
"#ffccc7",
"#ffa39e",
"#ff7875",
"#ff4d4f",
"#4160ac",
"#4363ae",
"#4867b1",
"#4d6bb3",
"#5370b6",
"#5d79bb",
"#6c85c1",
"#778dc7",
"#8297cd",
"#8fa3d3",
"#9baed9",
"#a6b7de",
"#b0c0e3",
"#bcc9e6",
"#c9d3eb",
"#d4ddf1",
"#fbd1d2",
"#fac4c6",
"#f9b8ba",
"#f8abae",
"#f79fa2",
"#f59496",
"#f4878a",
"#f37a7c",
"#f36e70",
"#f26264",
"#f15759",
"#f04c4f",
"#f14145",
"#ef3637",
"#ee2e30",
"#ee2529",
];
function Legend() {
return (
<Fragment>
<div className="vertical-legend" style={{ right: 12 }}>
<div className="legend-title">站点感热通量异常</div>
<div style={{ flex: 1, width: "100%", display: "flex", gap: 8 }}>
<div className="legend-text">
{[-0.5, -0.4, -0.3, -0.2, -0.1, 0, 0.1, 0.2, 0.3, 0.4, 0.5].map(
(item, index) => {
return (
<div
key={`legend-text-item-${index}`}
className="legend-text-item"
>
{item}
</div>
);
}
)}
</div>
<div className="colorbar">
{colorBar2.map((color, index) => {
return (
<div
key={`colorbar-item-${index}`}
className="colorbar-item"
style={{ backgroundColor: color }}
title={`${(-0.5 + index * 0.1).toFixed(1)}~${(
-0.5 +
(index + 1) * 0.1
).toFixed(1)}`}
/>
);
})}
</div>
</div>
</div>
<div className="vertical-legend" style={{ right: 154 }}>
<div className="legend-title">站点降水异常</div>
<div style={{ flex: 1, width: "100%", display: "flex", gap: 8 }}>
<div className="legend-text">
{[-0.5, -0.4, -0.3, -0.2, -0.1, 0, 0.1, 0.2, 0.3, 0.4, 0.5].map(
(item, index) => {
return (
<div
key={`legend-text-item-${index}`}
className="legend-text-item"
>
{item}
</div>
);
}
)}
</div>
<div className="legend">
<div className="legend-title">印度洋海温异常值</div>
<div className="colorbar">
{colorBar.map((color, index) => {
return (
@ -87,17 +44,24 @@ function Legend() {
key={`colorbar-item-${index}`}
className="colorbar-item"
style={{ backgroundColor: color }}
title={`${(-0.5 + index * 0.1).toFixed(1)}~${(
-0.5 +
(index + 1) * 0.1
title={`${(-3.2 + index * 0.2).toFixed(1)}~${(
-3.2 +
(index + 1) * 0.2
).toFixed(1)}`}
/>
);
})}
</div>
<div className="legend-text">
{[-2.4, -1.6, -0.8, 0, 0.8, 1.6, 2.4, ""].map((item, index) => {
return (
<div key={`legend-text-item-${index}`} className="legend-text-item">
{item}
</div>
);
})}
</div>
</div>
</Fragment>
);
}

View File

@ -13,7 +13,7 @@ function MoistureTransport() {
stopTime.secondsOfDay - currentTime.secondsOfDay
);
if (leftTime < 10) {
if (leftTime < 5) {
setShow(true);
} else if (show) setShow(false);
}, [show]);

View File

@ -1,36 +0,0 @@
import { useCallback, useState } from "react";
import { Entity, PolygonGraphics, useCesium } from "resium";
import { useInterval } from "ahooks";
import { Cartesian3, Color } from "cesium";
function PlateauPolygon() {
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 < 5) {
setShow(true);
} else if (show) setShow(false);
}, [show]);
useInterval(showAnimate, 100);
return (
<Entity id="plateau" show={show}>
<PolygonGraphics
hierarchy={Cartesian3.fromDegreesArray([
// 85, 30, 91, 30, 91, 35, 85, 35,
80, 31, 84, 29.5, 87.4, 28, 91, 28, 98, 29, 94, 35, 79, 34.4, 80, 31,
])}
material={new Color(1, 0, 0, 0.1)}
/>
</Entity>
);
}
export default PlateauPolygon;

View File

@ -0,0 +1,99 @@
import { Entity, EllipseGraphics } from "resium";
import { Cartesian3, Color } from "cesium";
const fluxData = [
0.03256254, -0.23182425, -0.040055223, -0.03476936, -0.40450656, -0.18742515,
0.045675285, -0.1613145, 0.06295018, -0.14455463, -0.30952197, 0.08164308,
0.019849295, 0.08785298, -0.22862722, -0.29176497, -0.1504006, 0.06978327,
0.10036364, -0.1807746, -0.21565104, -0.28437, -0.16798478, -0.214204,
-0.049326606, -0.094635, -0.09530785, -0.3044696, -0.28540367, -0.032144096,
-0.20508154, -0.1721087, -0.12554182, -0.11001358, 0.002132158, 0.055833075,
-0.1926637, -0.313678, 0.45139277, -0.22535999, -0.24989001, -0.024313157,
-0.2859986, 0.019246876, -0.02536043, -0.46465757, -0.32723987, -0.1926801,
-0.28803313, 0.005735828, 0.055759758, 0.10569655, -0.14558661, -0.1924466,
-0.035115488, -0.48584095, -0.15169029, -0.0734382, -0.21065243, -0.1338741,
-0.5154115, -0.19396217, -0.10661117, -0.24887498, -0.36621347, -0.39322242,
-0.14620663, -0.1848758, -0.36631036, -0.07608904, -0.161032, 0.08955761,
-0.46289977, -0.19713691, -0.237024, -0.10819046, -0.10532822, -0.20728564,
-0.04151492, -0.1793385,
];
const colorBar = [
"#73d13d",
"#95de64",
"#b7eb8f",
"#d9f7be",
"#f6ffed",
"#fff1f0",
"#ffccc7",
"#ffa39e",
"#ff7875",
"#ff4d4f",
];
const ranges = [
[-999, -0.4],
[-0.4, -0.3],
[-0.3, -0.2],
[-0.2, -0.1],
[-0.1, 0],
[0, 0.1],
[0.1, 0.2],
[0.2, 0.3],
[0.3, 0.4],
[0.4, 999],
];
let dataIndex = 0;
const height = 201000;
//
function HeatFlux({ position, index }) {
const [lon, lat] = position;
for (let i = 0; i < ranges.length; i++) {
const start = ranges[i][0];
const end = ranges[i][1];
if (fluxData[index] >= start && fluxData[index] <= end) {
dataIndex = i;
break;
}
}
return (
<Entity
id={`site-heat-flux-${index}`}
position={Cartesian3.fromDegrees(lon, lat, 0)}
description={`
<table className="description-table" style='width:100%'>
<tbody>
<tr>
<th style='width:40%; background-color:#4b4b4b; padding: 3' >
感热通量数据
</th>
<td style='background-color:#4b4b4b; padding: 3'>
${fluxData[index]}
</td>
</tr>
<tr>
<th style=' width: 40%; background-color: #323232; padding: 3 ' >
站点坐标
</th>
<td style=' background-color: #323232; padding: 3 ' > ${lon}${lat} </td>
</tr>
</tbody>
</table>
`}
>
<EllipseGraphics
material={new Color.fromCssColorString(colorBar[dataIndex])}
semiMinorAxis={30000.0}
semiMajorAxis={30000.0}
extrudedHeight={400000}
height={height}
/>
</Entity>
);
}
export default HeatFlux;

View File

@ -1,5 +1,5 @@
import { Entity, EllipseGraphics } from "resium";
import { Cartesian3, Color } from "cesium";
import { Entity, PolylineGraphics } from "resium";
import { Cartesian3, Color, PolylineArrowMaterialProperty } from "cesium";
const fluxData = [
0.03256254, -0.23182425, -0.040055223, -0.03476936, -0.40450656, -0.18742515,
@ -18,19 +18,6 @@ const fluxData = [
-0.04151492, -0.1793385,
];
const colorBar = [
"#73d13d",
"#95de64",
"#b7eb8f",
"#d9f7be",
"#f6ffed",
"#fff1f0",
"#ffccc7",
"#ffa39e",
"#ff7875",
"#ff4d4f",
];
const ranges = [
[-999, -0.4],
[-0.4, -0.3],
@ -44,8 +31,8 @@ const ranges = [
[0.4, 999],
];
let dataIndex = 0;
const height = 201000;
const height = 1210000;
let length = height + 510000;
//
function HeatFlux({ position, index }) {
@ -55,7 +42,11 @@ function HeatFlux({ position, index }) {
const start = ranges[i][0];
const end = ranges[i][1];
if (fluxData[index] >= start && fluxData[index] <= end) {
dataIndex = i;
if (i < 5) {
length = height + (i - 5) * 100000;
} else {
length = height + (i - 4) * 100000;
}
break;
}
}
@ -64,33 +55,19 @@ function HeatFlux({ position, index }) {
<Entity
id={`site-heat-flux-${index}`}
position={Cartesian3.fromDegrees(lon, lat, 0)}
description={`
<table className="description-table" style='width:100%'>
<tbody>
<tr>
<th style='width:40%; background-color:#4b4b4b; padding: 3' >
感热通量数据
</th>
<td style='background-color:#4b4b4b; padding: 3'>
${fluxData[index]}
</td>
</tr>
<tr>
<th style=' width: 40%; background-color: #323232; padding: 3 ' >
站点坐标
</th>
<td style=' background-color: #323232; padding: 3 ' > ${lon}${lat} </td>
</tr>
</tbody>
</table>
`}
>
<EllipseGraphics
material={new Color.fromCssColorString(colorBar[dataIndex])}
semiMinorAxis={30000.0}
semiMajorAxis={30000.0}
extrudedHeight={400000}
height={height}
<PolylineGraphics
positions={Cartesian3.fromDegreesArrayHeights([
lon,
lat,
height,
lon,
lat,
length,
])}
material={new PolylineArrowMaterialProperty(Color.RED)}
arcType={"NONE"}
width={10}
/>
</Entity>
);

View File

@ -0,0 +1,97 @@
import { Entity, EllipseGraphics } from "resium";
import { Cartesian3, Color } from "cesium";
const rainData = [
0.25309432, 0.296679, 0.39937696, -0.15093477, 0.17799897, 0.1186297,
0.07794944, 0.086336374, -0.016019614, -0.10862, 0.026069753, -0.005777068,
-0.04960121, -0.13736366, 0.268846, 0.32549506, 0.16180113, 0.34738356,
0.21825098, 0.56309533, 0.15624292, 0.1915884, -0.08074772, -0.021623861,
-0.03270335, 0.011358996, -0.09346678, -0.07257225, 0.19685836, -0.004689489,
0.018297506, 0.089315325, 0.07057168, 0.13153072, 0.036938984, -0.17923261,
0.3985438, 0.171897, -0.113426015, 0.4553916, 0.1476996, 0.158635,
-0.041355144, 0.14970288, 0.19565174, 0.213287, 0.0603011, 0.35724494,
-0.13986075, 0.16652703, -0.016419323, -0.09197042, -0.04890049, 0.22730026,
0.4451923, 0.102407545, 0.28357506, 0.259764, 0.2361199, 0.32204336,
0.39939404, 0.4786999, 0.12142443, -0.1493137, 0.3154582, 0.39657483,
-0.08021964, 0.20489447, 0.26629895, -0.34400544, 0.29385626, 0.15458098,
0.016143948, 0.04906382, -0.043581244, 0.1752981, -0.18351379, 0.071934514,
0.10854135, -0.10280543,
];
const colorBar = [
"#ffc53d",
"#ffd666",
"#ffe58f",
"#fff1b8",
"#fffbe6",
"#e6f7ff",
"#bae7ff",
"#91d5ff",
"#69c0ff",
"#40a9ff",
];
const ranges = [
[-999, -0.4],
[-0.4, -0.3],
[-0.3, -0.2],
[-0.2, -0.1],
[-0.1, 0],
[0, 0.1],
[0.1, 0.2],
[0.2, 0.3],
[0.3, 0.4],
[0.4, 999],
];
let dataIndex = 0;
//
function Rain({ position, index }) {
const [lon, lat] = position;
for (let i = 0; i < ranges.length; i++) {
const start = ranges[i][0];
const end = ranges[i][1];
if (rainData[index] >= start && rainData[index] <= end) {
dataIndex = i;
break;
}
}
return (
<Entity
id={`site-rain-${index}`}
position={Cartesian3.fromDegrees(lon, lat, 2000000)}
description={`
<table className="description-table" style='width:100%'>
<tbody>
<tr>
<th style='width:40%; background-color:#4b4b4b; padding: 3' >
青藏高原降水
</th>
<td style='background-color:#4b4b4b; padding: 3'>
${rainData[index]}
</td>
</tr>
<tr>
<th style=' width: 40%; background-color: #323232; padding: 3 ' >
站点坐标
</th>
<td style=' background-color: #323232; padding: 3 ' > ${lon}${lat} </td>
</tr>
</tbody>
</table>
`}
>
<EllipseGraphics
material={new Color.fromCssColorString(colorBar[dataIndex])}
semiMinorAxis={30000.0}
semiMajorAxis={30000.0}
extrudedHeight={200000}
/>
</Entity>
);
}
export default Rain;

View File

@ -1,5 +1,5 @@
import { Entity, EllipseGraphics } from "resium";
import { Cartesian3, Color } from "cesium";
import { Entity, PolylineGraphics } from "resium";
import { Cartesian3, Color, PolylineArrowMaterialProperty } from "cesium";
const rainData = [
0.25309432, 0.296679, 0.39937696, -0.15093477, 0.17799897, 0.1186297,
@ -18,19 +18,6 @@ const rainData = [
0.10854135, -0.10280543,
];
const colorBar = [
"#ffc53d",
"#ffd666",
"#ffe58f",
"#fff1b8",
"#fffbe6",
"#e6f7ff",
"#bae7ff",
"#91d5ff",
"#69c0ff",
"#40a9ff",
];
const ranges = [
[-999, -0.4],
[-0.4, -0.3],
@ -44,7 +31,8 @@ const ranges = [
[0.4, 999],
];
let dataIndex = 0;
const height = 400000;
let length = height + 510000;
//
function Rain({ position, index }) {
@ -54,7 +42,11 @@ function Rain({ position, index }) {
const start = ranges[i][0];
const end = ranges[i][1];
if (rainData[index] >= start && rainData[index] <= end) {
dataIndex = i;
if (i < 5) {
length = height + (i - 5) * 100000;
} else {
length = height + (i - 4) * 100000;
}
break;
}
}
@ -63,32 +55,19 @@ function Rain({ position, index }) {
<Entity
id={`site-rain-${index}`}
position={Cartesian3.fromDegrees(lon, lat, 2000000)}
description={`
<table className="description-table" style='width:100%'>
<tbody>
<tr>
<th style='width:40%; background-color:#4b4b4b; padding: 3' >
青藏高原降水
</th>
<td style='background-color:#4b4b4b; padding: 3'>
${rainData[index]}
</td>
</tr>
<tr>
<th style=' width: 40%; background-color: #323232; padding: 3 ' >
站点坐标
</th>
<td style=' background-color: #323232; padding: 3 ' > ${lon}${lat} </td>
</tr>
</tbody>
</table>
`}
>
<EllipseGraphics
material={new Color.fromCssColorString(colorBar[dataIndex])}
semiMinorAxis={30000.0}
semiMajorAxis={30000.0}
extrudedHeight={200000}
<PolylineGraphics
positions={Cartesian3.fromDegreesArrayHeights([
lon,
lat,
height,
lon,
lat,
length,
])}
material={new PolylineArrowMaterialProperty(Color.SKYBLUE)}
arcType={"NONE"}
width={10}
/>
</Entity>
);

View File

@ -15,10 +15,6 @@ const positions = [
];
function Sites() {
// const { showSite } = useSelector((state) => state.data);
// if (!showSite) return <></>;
return chunk(positions, 2).map((position, index) => {
return <Site key={`site-${index}`} index={index} position={position} />;
});

View File

@ -0,0 +1,46 @@
import rain from "@/assets/rain.png";
import heatflux from "@/assets/heatflux.png";
import styles from "./index.module.less";
function SiteLegend() {
return (
<div className={styles.siteLegend}>
<div className="site-legend-item">
<div>
<img src={rain} alt="rain-increase" width={32} />
</div>
<div className="site-legend-item-name">降水异常增加</div>
</div>
<div className="site-legend-item">
<div>
<img
src={rain}
alt="rain-decrease"
width={32}
style={{ transform: "rotate(180deg)" }}
/>
</div>
<div className="site-legend-item-name">降水异常降低</div>
</div>
<div className="site-legend-item">
<div>
<img src={heatflux} alt="heatflux-increase" width={32} />
</div>
<div className="site-legend-item-name">感热通量异常增加</div>
</div>
<div className="site-legend-item">
<div>
<img
src={heatflux}
alt="heatflux-decrease"
width={32}
style={{ transform: "rotate(180deg)" }}
/>
</div>
<div className="site-legend-item-name">感热通量异常降低</div>
</div>
</div>
);
}
export default SiteLegend;

View File

@ -4,8 +4,8 @@ import WavePoint from "@/components/common/WavePoint";
export default function WavePoints() {
return (
<Fragment>
<WavePoint stationLon={88} stationLat={-85} />
<WavePoint stationLon={88} stationLat={32.5} />
<WavePoint stationLon={88} stationLat={-85} labelText={"南极"} />
<WavePoint stationLon={88} stationLat={32.5} labelText={"青藏高原"} />
</Fragment>
);
}

View File

@ -4,7 +4,6 @@ import CustomClock from "@/components/common/CustomClock";
import TextInfoPanel from "@/components/common/TextInfoPanel";
import CustomFlyTo from "./CustomFlyTo";
import Point from "./Point";
import PlateauPolygon from "./PlateauPolygon";
import WavePoints from "./WavePoints";
import Legend from "./Legend";
import IndianOceanSST from "./IndiaOceanSST";
@ -13,9 +12,11 @@ import Circles from "./Circles";
import SurfaceAnomaly from "./SurfaceAnomaly";
import MoistureTransport from "./MoistureTransport";
import Sites from "./Site";
import SiteLegend from "./SiteLegend";
import Labels from "./Labels";
export default function DomainTwo() {
console.log("111 :>> ", 111);
return (
<MapLayout>
<div className="title">两极协同南极涛动有效调节青藏高原降水和加热</div>
@ -29,8 +30,8 @@ export default function DomainTwo() {
<Legend />
<MoistureTransport />
<Point />
<PlateauPolygon />
<Sites />
<SiteLegend />
<div className="left-panel one">
<TextInfoPanel content="利用ERA5再分析数据诊断和CESM印度洋海温强迫数值实验发现5月的南极涛动正位相AAO激发原子阿蒙森海的纬向波列异常。该异常造成印度洋海温降低进而激发环流异常造成高原上空降水增加、感热通量降低。这一结果有助于提高青藏高原热源的预测技巧改进亚洲夏季风的预测。" />
</div>

View File

@ -0,0 +1,24 @@
.siteLegend :global {
position: absolute;
width: 200px;
height: 300px;
right: 12px;
bottom: 14px;
display: grid;
background-color: #1f485690;
border: 1px solid #04fbfd;
border-radius: 8px;
padding: 8px;
color: white;
user-select: none;
.site-legend-item {
display: flex;
align-items: center;
gap: 8px;
.site-legend-item-name {
font-weight: 700;
}
}
}

View File

@ -1,12 +1,10 @@
import { useCallback } from "react";
import { useNavigate } from "react-router-dom";
import { useDispatch } from "react-redux";
import { createFromIconfontCN } from "@ant-design/icons";
import NavBarButton from "./NavBarButton";
function NavBar() {
const navigate = useNavigate();
const dispatch = useDispatch();
const MyIcon = createFromIconfontCN({
scriptUrl: "//at.alicdn.com/t/c/font_4281629_4lq551rfhki.js",
@ -15,13 +13,8 @@ function NavBar() {
const navigateHandler = useCallback(
(type) => {
navigate(`/map/${type}`, { replace: true });
if (type !== 1 && type !== 2) {
dispatch.data.update({
toolbar: { showPanel: true },
});
}
},
[navigate, dispatch]
[navigate]
);
return (

View File

@ -1,8 +1,9 @@
import { Entity, PointGraphics, Viewer } from "resium";
import styles from "./index.module.less";
import { Cartesian3, Color } from "cesium";
import Picker from "./Picker";
import HeadingPitchRoll from "./HeadingPitchRoll";
import CustomToolbar from "@/components/common/CustomToolbar";
import styles from "./index.module.less";
function MapLayout({ children, className, ...rest }) {
return (
@ -24,6 +25,7 @@ function MapLayout({ children, className, ...rest }) {
<Entity position={Cartesian3.fromDegreesArray([0, 90])}>
<PointGraphics color={Color.SKYBLUE} pixelSize={10} />
</Entity>
<CustomToolbar />
<Picker />
{/* <HeadingPitchRoll /> */}
{children}

View File

@ -28,16 +28,6 @@
font-weight: 700;
}
.toolbar {
position: absolute;
top: 0;
right: 12px;
display: flex;
gap: 8px;
width: 370px;
height: 100%;
}
.left-panel {
position: absolute;
top: 84px;
@ -67,12 +57,13 @@
text-indent: 2em;
line-height: 1.5;
z-index: 999;
border-radius: 8px;
}
}
.right-panel {
position: absolute;
top: 84px;
// top: 84px;
right: 12px;
bottom: 12px;
width: 450px;
@ -96,6 +87,7 @@
border: 1px solid #04fbfd;
color: #02f9ff !important;
background-color: #1f485690;
border-radius: 8px;
.ant-form-item-label {
label {
@ -133,6 +125,7 @@
color: #02f9ff !important;
background-color: #1f485690;
transition: all 0.3s ease-in-out;
border-radius: 8px;
}
.highlight {
@ -173,7 +166,7 @@
.legend {
position: absolute;
bottom: 40px;
bottom: 12px;
width: 50%;
left: 25%;
// height: 40px;

View File

@ -1,36 +1,9 @@
export const data = {
state: {
showSite: false,
toolbar: {},
imageLayer: {
labrador: undefined,
indianOcean: undefined,
},
},
state: {},
reducers: {
update(state, payload) {
return { ...state, ...payload };
},
updateToolbar(state, payload) {
const { toolbar } = state;
return { ...state, toolbar: { ...toolbar, ...payload } };
},
updateImageLayer(state, payload) {
const { imageLayer } = state;
return { ...state, imageLayer: { ...imageLayer, ...payload } };
},
resetState(state, payload) {
return {
showSite: false,
toolbar: {
showPanel: undefined,
},
imageLayer: {
labrador: undefined,
indianOcean: undefined,
},
};
},
},
effects: (dispatch) => ({}),
};