fix
@ -5,6 +5,7 @@
|
||||
"dependencies": {
|
||||
"@ant-design/aliyun-theme": "^0.0.5",
|
||||
"@ant-design/colors": "^7.0.0",
|
||||
"@ant-design/icons": "^4.7.0",
|
||||
"@craco/craco": "^7.1.0",
|
||||
"@rematch/core": "^2.2.0",
|
||||
"@rematch/loading": "^2.1.2",
|
||||
|
@ -4,6 +4,7 @@ import DomainOne from "@/components/domain/One";
|
||||
import DomainTwo from "@/components/domain/Two";
|
||||
import DomainThree from "@/components/domain/Three";
|
||||
import DomainFour from "@/components/domain/Four";
|
||||
import DomainFive from "./components/domain/Five";
|
||||
import "./App.less";
|
||||
|
||||
function App() {
|
||||
@ -14,6 +15,7 @@ function App() {
|
||||
<Route path="map/2" element={<DomainTwo />}></Route>
|
||||
<Route path="map/3" element={<DomainThree />}></Route>
|
||||
<Route path="map/4" element={<DomainFour />}></Route>
|
||||
<Route path="map/5" element={<DomainFive />}></Route>
|
||||
<Route path="*" element={<Navigate to={"/"} replace={true} />} />
|
||||
</Routes>
|
||||
);
|
||||
|
@ -5,4 +5,5 @@ body,
|
||||
#root {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
font-family: sans-serif !important;
|
||||
}
|
||||
|
BIN
src/assets/High.png
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
src/assets/Low.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
src/assets/anti-Hadleycell.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
BIN
src/assets/anticyclone.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
src/assets/cyclone.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
src/assets/updraft.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
src/assets/waterwapor.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
@ -1,117 +1,130 @@
|
||||
import { useCallback, useState } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
import { useCesium } from "resium";
|
||||
import { Select } from "antd";
|
||||
import { Button, Drawer, List, Select } from "antd";
|
||||
import { HomeOutlined, MenuUnfoldOutlined } from "@ant-design/icons";
|
||||
import { useMouse } from "ahooks";
|
||||
import styles from "./index.module.less";
|
||||
|
||||
function CustomToolbar() {
|
||||
const { viewer } = useCesium();
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
const location = useLocation();
|
||||
|
||||
const { toolbar } = useSelector((state) => state.data);
|
||||
const [value, setValue] = useState("sideview");
|
||||
|
||||
const handleChange = (value) => {
|
||||
setValue(value);
|
||||
const pointEntity = viewer.entities.getById("point");
|
||||
if (!viewer) return;
|
||||
if (value === "overhead") {
|
||||
// 俯视
|
||||
viewer.trackedEntity = pointEntity;
|
||||
const destination = pointEntity.position.getValue(
|
||||
viewer.clock.currentTime
|
||||
);
|
||||
if (!destination) return;
|
||||
const newDestination = new Cesium.Cartesian3();
|
||||
newDestination.x = destination.x;
|
||||
newDestination.y = destination.y + 13000000;
|
||||
newDestination.z = destination.z;
|
||||
viewer.camera.flyTo({
|
||||
destination: newDestination,
|
||||
});
|
||||
} else if (value === "sideview") {
|
||||
// 侧视
|
||||
// viewer.trackedEntity = pointEntity;
|
||||
viewer.trackedEntity = undefined;
|
||||
viewer.camera.flyTo({
|
||||
destination: Cesium.Cartesian3.fromDegrees(130, -10.5, 20000000),
|
||||
});
|
||||
} else {
|
||||
// 跟随mftata
|
||||
viewer.trackedEntity = pointEntity;
|
||||
const destination = pointEntity.position.getValue(
|
||||
viewer.clock.currentTimes
|
||||
);
|
||||
if (!destination) return;
|
||||
const newDestination = Cesium.Cartesian3.clone(destination);
|
||||
newDestination.y = destination.y + 1000000;
|
||||
viewer.camera.flyTo({
|
||||
destination: newDestination,
|
||||
orientation: {
|
||||
heading: Cesium.Math.toRadians(0.0),
|
||||
pitch: -Cesium.Math.PI_OVER_FOUR,
|
||||
roll: 0.0,
|
||||
const [mouseX, setMouseX] = useState(0);
|
||||
const [drawerOpen, setDrawerOpen] = useState(false);
|
||||
|
||||
const mapList = [
|
||||
{
|
||||
label: "首页",
|
||||
value: "/",
|
||||
icon: <HomeOutlined />,
|
||||
onClick: () => {
|
||||
navigate("/home", { replace: true });
|
||||
},
|
||||
duration: 0,
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "两极协同—拉布拉多海海温偏暖控制夏季高原年代际增温",
|
||||
value: "/map/1",
|
||||
icon: <MenuUnfoldOutlined />,
|
||||
onClick: () => {
|
||||
navigate("/map/1", { replace: true });
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "两极协同—南极涛动有效调节青藏高原降水和加热",
|
||||
value: "/map/2",
|
||||
icon: <MenuUnfoldOutlined />,
|
||||
onClick: () => {
|
||||
navigate("/map/2", { replace: true });
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "两极协同—连接南极和北极的热带大西洋经向模的媒介作用",
|
||||
value: "/map/3",
|
||||
icon: <MenuUnfoldOutlined />,
|
||||
onClick: () => {
|
||||
navigate("/map/3", { replace: true });
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "三极联动影响青藏高原上层温度",
|
||||
value: "/map/4",
|
||||
icon: <MenuUnfoldOutlined />,
|
||||
onClick: () => {
|
||||
navigate("/map/4", { replace: true });
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "三级联动影响东亚夏季风",
|
||||
value: "/map/5",
|
||||
icon: <MenuUnfoldOutlined />,
|
||||
onClick: () => {
|
||||
navigate("/map/5", { replace: true });
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
const handleMouseMove = (event) => {
|
||||
setMouseX(event.clientX);
|
||||
};
|
||||
|
||||
const navigateHandler = useCallback(() => {
|
||||
navigate("/home", { replace: true });
|
||||
dispatch.data.resetState();
|
||||
}, [navigate, dispatch]);
|
||||
document.addEventListener("mousemove", handleMouseMove);
|
||||
|
||||
const showPanelHandler = useCallback(
|
||||
(value) => {
|
||||
dispatch.data.updateToolbar({
|
||||
showPanel: value,
|
||||
});
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
return () => {
|
||||
document.removeEventListener("mousemove", handleMouseMove);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const isAtRightEdge = mouseX >= window.innerWidth - 10;
|
||||
const iaClose = mouseX + 320 <= window.innerWidth;
|
||||
|
||||
useEffect(() => {
|
||||
if (isAtRightEdge && !drawerOpen) {
|
||||
setDrawerOpen(true);
|
||||
} else if (iaClose && drawerOpen) {
|
||||
setDrawerOpen(false);
|
||||
}
|
||||
}, [isAtRightEdge, iaClose]);
|
||||
|
||||
const onDrawerClose = useCallback(() => {
|
||||
setDrawerOpen(false);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="toolbar">
|
||||
<div className={"focusBtn"} onClick={navigateHandler}>
|
||||
返回首页
|
||||
</div>
|
||||
{/* <Select
|
||||
// onSelect={handleChange}
|
||||
onChange={handleChange}
|
||||
value={value}
|
||||
getPopupContainer={(node) => node}
|
||||
options={[
|
||||
// {
|
||||
// value: "overhead",
|
||||
// label: "俯视视角",
|
||||
// },
|
||||
{
|
||||
value: "sideview",
|
||||
label: "侧视视角",
|
||||
},
|
||||
{
|
||||
value: "follow",
|
||||
label: "跟随视角",
|
||||
},
|
||||
]}
|
||||
/> */}
|
||||
<Select
|
||||
onChange={showPanelHandler}
|
||||
value={!!toolbar.showPanel}
|
||||
getPopupContainer={(node) => node}
|
||||
options={[
|
||||
{
|
||||
value: true,
|
||||
label: "开启展板",
|
||||
},
|
||||
{
|
||||
value: false,
|
||||
label: "关闭展板",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<div className="toolbar" onMouseLeave={onDrawerClose}>
|
||||
<Drawer
|
||||
placement="right"
|
||||
// open={true}
|
||||
open={drawerOpen}
|
||||
width={350}
|
||||
mask={false}
|
||||
closable={false}
|
||||
className={styles.drawerToolbar}
|
||||
>
|
||||
<List
|
||||
dataSource={mapList}
|
||||
renderItem={(item, index) => {
|
||||
return (
|
||||
<List.Item
|
||||
key={index}
|
||||
onClick={item?.onClick}
|
||||
className={
|
||||
location.pathname === item.value ? "active-item" : ""
|
||||
}
|
||||
>
|
||||
<List.Item.Meta avatar={item.icon} title={item.label} />
|
||||
</List.Item>
|
||||
);
|
||||
}}
|
||||
></List>
|
||||
</Drawer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
54
src/components/common/CustomToolbar/index.module.less
Normal file
@ -0,0 +1,54 @@
|
||||
.drawerToolbar :global {
|
||||
transition: all 0.3s ease;
|
||||
|
||||
.ant-drawer-content-wrapper {
|
||||
.ant-drawer-content {
|
||||
background-color: #1f4856;
|
||||
|
||||
.ant-drawer-body {
|
||||
padding: 0;
|
||||
|
||||
.ant-list {
|
||||
.ant-list-item {
|
||||
.ant-list-item-meta {
|
||||
// justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.ant-list-item-meta-avatar {
|
||||
font-size: 24px;
|
||||
color: aliceblue;
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.ant-list-item-meta-content {
|
||||
.ant-list-item-meta-title {
|
||||
font-size: 1.2rem;
|
||||
color: aliceblue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background-color: #94bebf;
|
||||
|
||||
.ant-list-item-meta-avatar {
|
||||
color: #1f4856;
|
||||
}
|
||||
|
||||
.ant-list-item-meta-content {
|
||||
.ant-list-item-meta-title {
|
||||
color: #1f4856;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.active-item {
|
||||
background-color: cadetblue !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,7 +4,8 @@ import { Scrollbars } from "react-custom-scrollbars-2";
|
||||
import { useInterval } from "ahooks";
|
||||
|
||||
let index = 0;
|
||||
function TextInfoPanel({ title, content }) {
|
||||
|
||||
function TextInfoPanel({ content }) {
|
||||
const showNumberPerTimes = 1;
|
||||
const { toolbar } = useSelector((state) => state.data);
|
||||
|
||||
|
115
src/components/domain/Five/ChartPanel.jsx
Normal file
@ -0,0 +1,115 @@
|
||||
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],
|
||||
},
|
||||
},
|
||||
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],
|
||||
},
|
||||
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 (
|
||||
<div className="chart-info-panel">
|
||||
<ReactECharts
|
||||
option={option}
|
||||
lazyUpdate={true}
|
||||
style={{
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ChartPanel;
|
11
src/components/domain/Five/CustomFlyTo.jsx
Normal file
@ -0,0 +1,11 @@
|
||||
import { Cartesian3 } from "cesium";
|
||||
import { CameraFlyTo } from "resium";
|
||||
|
||||
export default function CustomFlyTo() {
|
||||
return (
|
||||
<CameraFlyTo
|
||||
duration={5}
|
||||
destination={Cartesian3.fromDegrees(93, 30, 16000000)}
|
||||
/>
|
||||
);
|
||||
}
|
133
src/components/domain/Five/FormPanel.jsx
Normal file
@ -0,0 +1,133 @@
|
||||
import { Button, Form, Input, Select } from "antd";
|
||||
|
||||
const layout = {
|
||||
labelCol: { span: 6 },
|
||||
wrapperCol: { span: 16 },
|
||||
};
|
||||
const tailLayout = {
|
||||
wrapperCol: { offset: 6, span: 16 },
|
||||
};
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
export default function FormPanel({ setShow }) {
|
||||
const [form] = Form.useForm();
|
||||
|
||||
return (
|
||||
<div className="form-panel">
|
||||
<Form
|
||||
{...layout}
|
||||
form={form}
|
||||
name="control-hooks"
|
||||
initialValues={{
|
||||
CMIP6: [
|
||||
"ACCESS-CM2",
|
||||
"ACCESS-ESM1-5",
|
||||
"AWI-CM-1-1-MR",
|
||||
"AWI-ESM-1-1-LR",
|
||||
"BCC-CSM2-MR",
|
||||
"BCC-ESM1",
|
||||
"CAMS-CSM1-0",
|
||||
"CanESM5",
|
||||
"CAS-ESM2-0",
|
||||
"CESM2-FV2",
|
||||
"CESM2",
|
||||
"CESM2-WACCM-FV2",
|
||||
"CESM2-WACCM",
|
||||
"CIESM",
|
||||
"CMCC-CM2-SR5",
|
||||
"EC-Earth3",
|
||||
"EC-Earth3-Veg",
|
||||
"EC-Earth3-Veg-LR",
|
||||
"FGOALS-f3-L",
|
||||
"FGOALS-g3",
|
||||
"GFDL-ESM4",
|
||||
"GISS-E2-1-G",
|
||||
"GISS-E2-1-H",
|
||||
"INM-CM4-8",
|
||||
"INM-CM5-0",
|
||||
"IPSL-CM6A-LR",
|
||||
"KACE-1-0-G",
|
||||
"MCM-UA-1-0",
|
||||
"MIROC6",
|
||||
"MPI-ESM-1-2-HAM",
|
||||
"MPI-ESM1-2-HR",
|
||||
"MPI-ESM1-2-LR",
|
||||
"MRI-ESM2-0",
|
||||
"NESM3",
|
||||
"NorCPM1",
|
||||
"NorESM2-LM",
|
||||
"NorESM2-MM",
|
||||
"SAM0-UNICON",
|
||||
"TaiESM1",
|
||||
],
|
||||
}}
|
||||
onFinish={(values) => {
|
||||
console.log("Success:", values);
|
||||
setShow(true);
|
||||
}}
|
||||
>
|
||||
<Form.Item
|
||||
name="CMIP6"
|
||||
label="模式名称"
|
||||
rules={[{ required: true, message: "模式名称是必选项" }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择模式名称"
|
||||
mode="multiple"
|
||||
options={[
|
||||
"ACCESS-CM2",
|
||||
"ACCESS-ESM1-5",
|
||||
"AWI-CM-1-1-MR",
|
||||
"AWI-ESM-1-1-LR",
|
||||
"BCC-CSM2-MR",
|
||||
"BCC-ESM1",
|
||||
"CAMS-CSM1-0",
|
||||
"CanESM5",
|
||||
"CAS-ESM2-0",
|
||||
"CESM2-FV2",
|
||||
"CESM2",
|
||||
"CESM2-WACCM-FV2",
|
||||
"CESM2-WACCM",
|
||||
"CIESM",
|
||||
"CMCC-CM2-SR5",
|
||||
"EC-Earth3",
|
||||
"EC-Earth3-Veg",
|
||||
"EC-Earth3-Veg-LR",
|
||||
"FGOALS-f3-L",
|
||||
"FGOALS-g3",
|
||||
"GFDL-ESM4",
|
||||
"GISS-E2-1-G",
|
||||
"GISS-E2-1-H",
|
||||
"INM-CM4-8",
|
||||
"INM-CM5-0",
|
||||
"IPSL-CM6A-LR",
|
||||
"KACE-1-0-G",
|
||||
"MCM-UA-1-0",
|
||||
"MIROC6",
|
||||
"MPI-ESM-1-2-HAM",
|
||||
"MPI-ESM1-2-HR",
|
||||
"MPI-ESM1-2-LR",
|
||||
"MRI-ESM2-0",
|
||||
"NESM3",
|
||||
"NorCPM1",
|
||||
"NorESM2-LM",
|
||||
"NorESM2-MM",
|
||||
"SAM0-UNICON",
|
||||
"TaiESM1",
|
||||
].map((item) => ({
|
||||
label: item,
|
||||
balue: item,
|
||||
}))}
|
||||
></Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item {...tailLayout}>
|
||||
<Button type="primary" htmlType="submit">
|
||||
提交
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
35
src/components/domain/Five/index.jsx
Normal file
@ -0,0 +1,35 @@
|
||||
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";
|
||||
|
||||
export default function DomainFive() {
|
||||
const [show, setShow] = useState(false);
|
||||
|
||||
return (
|
||||
<MapLayout>
|
||||
<div className="title">三极联动影响东亚夏季风</div>
|
||||
<CustomToolbar />
|
||||
<CustomClock />
|
||||
<CustomFlyTo />
|
||||
<div className="left-panel one">
|
||||
<TextInfoPanel
|
||||
content={`利用39个CMIP6数值模拟数据训练卷积神经网络,利用NOAA20世纪再分析数据进行迁移学习、ERA5再分析数据进行测试,从而预测东亚夏季风。预测结果的热力图可视化表明尽管热带大洋对东亚夏季风的影响是最强的,然而不论是北极、南极还是青藏高原均显示有显著的前期信号(春季),这也表明地球三极可以协同影响东亚夏季风。关于东亚夏季风的预测一直是个难题。近期,我们利用机器学习识别了东亚夏季风的影响因子。
|
||||
地球三极的协同影响在东亚夏季风的归因图中得到了很好的体现:尽管热带大洋对东亚夏季风的影响是最强的,然而不论是北极、南极还是青藏高原均显示有显著的前期信号(春季),这也表明地球三极可以协同影响东亚夏季风。`}
|
||||
/>
|
||||
</div>
|
||||
<div className="right-panel">
|
||||
<div className="top-panel">
|
||||
<FormPanel setShow={setShow} />
|
||||
</div>
|
||||
<div className="bottom-panel">{show && <ChartPanel />}</div>
|
||||
</div>
|
||||
{/* <RectangleLayer /> */}
|
||||
</MapLayout>
|
||||
);
|
||||
}
|
@ -1,14 +1,16 @@
|
||||
import { Button, Form, Input, Select } from "antd";
|
||||
|
||||
const layout = {
|
||||
labelCol: { span: 8 },
|
||||
labelCol: { span: 6 },
|
||||
wrapperCol: { span: 16 },
|
||||
};
|
||||
const tailLayout = {
|
||||
wrapperCol: { offset: 8, span: 16 },
|
||||
wrapperCol: { offset: 6, span: 16 },
|
||||
};
|
||||
|
||||
export default function FormPanel() {
|
||||
const { Option } = Select;
|
||||
|
||||
export default function FormPanel({ setShow }) {
|
||||
const [form] = Form.useForm();
|
||||
|
||||
return (
|
||||
@ -18,21 +20,52 @@ export default function FormPanel() {
|
||||
form={form}
|
||||
name="control-hooks"
|
||||
initialValues={{
|
||||
sic: "sic_s",
|
||||
sic: ["sic_s"],
|
||||
sce: ["sc_ea", "sc_tp"],
|
||||
st: [
|
||||
"ts_1",
|
||||
"ts_2",
|
||||
"ts_3",
|
||||
"ts_4",
|
||||
"ts_5",
|
||||
"ts_6",
|
||||
"ts_7",
|
||||
"ts_8",
|
||||
"ts_9",
|
||||
"ts_10",
|
||||
"ts_11",
|
||||
"ts_12",
|
||||
],
|
||||
}}
|
||||
onFinish={(values) => {
|
||||
console.log("Success:", values);
|
||||
setShow(true);
|
||||
}}
|
||||
>
|
||||
<Form.Item name="sic" label="海冰密集度" rules={[{ required: true }]}>
|
||||
<Form.Item
|
||||
name="sic"
|
||||
label="海冰密集度"
|
||||
rules={[{ required: true, message: "海冰密集度是必选项" }]}
|
||||
>
|
||||
<Select placeholder="请选择海冰密集度区域" mode="multiple">
|
||||
<Option value="sic_s">南极附近海冰</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item name="sce" label="积雪范围" rules={[{ required: true }]}>
|
||||
<Form.Item
|
||||
name="sce"
|
||||
label="积雪范围"
|
||||
rules={[{ required: true, message: "积雪范围是必选项" }]}
|
||||
>
|
||||
<Select placeholder="请选择积雪范围" mode="multiple">
|
||||
<Option value="sc_ea">欧亚大陆积雪异常</Option>
|
||||
<Option value="sc_tp">青藏高原积雪异常</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item name="st" label="地表温度范围" rules={[{ required: true }]}>
|
||||
<Form.Item
|
||||
name="st"
|
||||
label="地表温度范围"
|
||||
rules={[{ required: true, message: "地表温度范围是必选项" }]}
|
||||
>
|
||||
<Select placeholder="请选地表温度范围" mode="multiple">
|
||||
<Option value="ts_1">ts_1</Option>
|
||||
<Option value="ts_2">ts_2</Option>
|
||||
|
42
src/components/domain/Four/Legend.jsx
Normal file
@ -0,0 +1,42 @@
|
||||
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;
|
@ -6,8 +6,12 @@ import CustomFlyTo from "./CustomFlyTo";
|
||||
import FormPanel from "./FormPanel";
|
||||
import ChartPanel from "./ChartPanel";
|
||||
import RectangleLayer from "./RectangleLayer";
|
||||
import { useState } from "react";
|
||||
import Legend from "./Legend";
|
||||
|
||||
export default function DomainFour() {
|
||||
const [show, setShow] = useState(false);
|
||||
|
||||
return (
|
||||
<MapLayout>
|
||||
<div className="title">三极联动影响青藏高原上层温度</div>
|
||||
@ -15,16 +19,18 @@ export default function DomainFour() {
|
||||
<CustomClock />
|
||||
<CustomFlyTo />
|
||||
<div className="left-panel one">
|
||||
<TextInfoPanel content="利用ERA5再分析数据,通过XGBoost和Light" />
|
||||
<TextInfoPanel
|
||||
content={`利用ERA5再分析数据,通过XGBoost和LightGBM方法对青藏高原对流层温度异常进行预测,结果表明该温度异常主要受地球三极信号影响,机器学习方法的高预测性对于南亚高压和亚洲夏季降水有重要指示意义。机器学习的方法描述:
|
||||
XGBoos是基于梯度提升决策树方法的分类和回归模型。 LightGBM也是一种基于树的梯度提升方法,可以解决高维输入变量问题。这两个机器学习模型由许多简单的弱学习器(也称为小回归模型)组成,最终的预测是所有弱学习器的预测的加权和。 此外,作为Boosting树模型,XGBoost和LightGBM对多重共线性不敏感,这减少了特征的同时交互,提高了预测能力。`}
|
||||
/>
|
||||
</div>
|
||||
<div className="right-panel">
|
||||
<div className="top-panel">
|
||||
<FormPanel />
|
||||
</div>
|
||||
<div className="bottom-panel">
|
||||
<ChartPanel />
|
||||
<FormPanel setShow={setShow} />
|
||||
</div>
|
||||
<div className="bottom-panel">{show && <ChartPanel />}</div>
|
||||
</div>
|
||||
<Legend />
|
||||
<RectangleLayer />
|
||||
</MapLayout>
|
||||
);
|
||||
|
@ -32,18 +32,18 @@ function Barotropic() {
|
||||
show={show}
|
||||
name="Barotropic"
|
||||
// description="Barotropic"
|
||||
position={Cartesian3.fromDegrees(88, 60, 0)}
|
||||
position={Cartesian3.fromDegrees(98, 48, 0)}
|
||||
>
|
||||
<EllipseGraphics
|
||||
material={new Color(0.73, 0.94, 0.95, 0.4)}
|
||||
semiMinorAxis={150000.0}
|
||||
semiMajorAxis={150000.0}
|
||||
semiMinorAxis={300000.0}
|
||||
semiMajorAxis={300000.0}
|
||||
extrudedHeight={1000000.0}
|
||||
rotation={0}
|
||||
// outline
|
||||
/>
|
||||
<LabelGraphics
|
||||
position={Cartesian3.fromDegrees(88, 60, 0)}
|
||||
position={Cartesian3.fromDegrees(98, 48, 0)}
|
||||
text={"barotropic"}
|
||||
font="24px Helvetica"
|
||||
fillColor={Color.SKYBLUE}
|
||||
|
@ -115,7 +115,7 @@ function ChartPanel() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="chart-info-panel">
|
||||
<div className="chart-info-panel highlight">
|
||||
<ReactECharts
|
||||
option={option}
|
||||
lazyUpdate={true}
|
||||
|
@ -9,7 +9,7 @@ function CustomFlyTo() {
|
||||
function cameraFlyToLine(adjustPitch) {
|
||||
// barotorpic
|
||||
const barotorpic = {
|
||||
destination: Cesium.Cartesian3.fromDegrees(42, 46, 15000000),
|
||||
destination: Cesium.Cartesian3.fromDegrees(80, 46, 15000000),
|
||||
duration: 30,
|
||||
complete: () => {},
|
||||
};
|
||||
@ -71,7 +71,7 @@ function CustomFlyTo() {
|
||||
plateauOptions.pitchAdjustHeight = 1000;
|
||||
sideViewOptions.pitchAdjustHeight = 1000;
|
||||
}
|
||||
camera.flyTo(labrador);
|
||||
camera.flyTo(sideViewOptions);
|
||||
}
|
||||
cameraFlyToLine();
|
||||
|
||||
|
@ -44,6 +44,9 @@ function Cyclone() {
|
||||
return _time;
|
||||
}, [viewer]);
|
||||
|
||||
const anticycloneColor = Cesium.Color.fromCssColorString("#f70000");
|
||||
const cycloneColor = Cesium.Color.fromCssColorString("#00AC4D");
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Entity
|
||||
@ -52,7 +55,7 @@ function Cyclone() {
|
||||
new Cesium.CallbackProperty(function (time, result) {
|
||||
const passTime = getPassTime();
|
||||
if (passTime < 10) return;
|
||||
return Cesium.Cartesian3.fromDegrees(-55, 58, 1000000);
|
||||
return Cesium.Cartesian3.fromDegrees(-62, 69, 1000000);
|
||||
}, true)
|
||||
}
|
||||
>
|
||||
@ -65,6 +68,8 @@ function Cyclone() {
|
||||
// rotation: rotationProperty,
|
||||
// }),
|
||||
// }}
|
||||
color={anticycloneColor}
|
||||
colorBlendMode={Cesium.ColorBlendMode.REPLACE}
|
||||
/>
|
||||
</Entity>
|
||||
<Entity
|
||||
@ -73,11 +78,16 @@ function Cyclone() {
|
||||
new Cesium.CallbackProperty(function (time, result) {
|
||||
const passTime = getPassTime();
|
||||
if (passTime < 20) return;
|
||||
return Cesium.Cartesian3.fromDegrees(-32, 72.2, 1000000);
|
||||
return Cesium.Cartesian3.fromDegrees(-20, 55, 1000000);
|
||||
}, true)
|
||||
}
|
||||
>
|
||||
<ModelGraphics uri={arrowRound} minimumPixelSize={128} />
|
||||
<ModelGraphics
|
||||
uri={arrowRound}
|
||||
minimumPixelSize={128}
|
||||
color={cycloneColor}
|
||||
colorBlendMode={Cesium.ColorBlendMode.REPLACE}
|
||||
/>
|
||||
</Entity>
|
||||
<Entity
|
||||
id={"Anticyclone-2"}
|
||||
@ -85,11 +95,16 @@ function Cyclone() {
|
||||
new Cesium.CallbackProperty(function (time, result) {
|
||||
const passTime = getPassTime();
|
||||
if (passTime < 30) return;
|
||||
return Cesium.Cartesian3.fromDegrees(20, 77, 1000000);
|
||||
return Cesium.Cartesian3.fromDegrees(29, 49, 1000000);
|
||||
}, true)
|
||||
}
|
||||
>
|
||||
<ModelGraphics uri={arrowRound} minimumPixelSize={128} />
|
||||
<ModelGraphics
|
||||
uri={arrowRound}
|
||||
minimumPixelSize={128}
|
||||
color={anticycloneColor}
|
||||
colorBlendMode={Cesium.ColorBlendMode.REPLACE}
|
||||
/>
|
||||
</Entity>
|
||||
<Entity
|
||||
id={"Cyclone-2"}
|
||||
@ -97,11 +112,16 @@ function Cyclone() {
|
||||
new Cesium.CallbackProperty(function (time, result) {
|
||||
const passTime = getPassTime();
|
||||
if (passTime < 40) return;
|
||||
return Cesium.Cartesian3.fromDegrees(63, 69.9, 1000000);
|
||||
return Cesium.Cartesian3.fromDegrees(62, 45, 1000000);
|
||||
}, true)
|
||||
}
|
||||
>
|
||||
<ModelGraphics uri={arrowRound} minimumPixelSize={128} />
|
||||
<ModelGraphics
|
||||
uri={arrowRound}
|
||||
minimumPixelSize={128}
|
||||
color={cycloneColor}
|
||||
colorBlendMode={Cesium.ColorBlendMode.REPLACE}
|
||||
/>
|
||||
</Entity>
|
||||
<Entity
|
||||
id={"Anticyclone-3"}
|
||||
@ -109,11 +129,16 @@ function Cyclone() {
|
||||
new Cesium.CallbackProperty(function (time, result) {
|
||||
const passTime = getPassTime();
|
||||
if (passTime < 50) return;
|
||||
return Cesium.Cartesian3.fromDegrees(88, 60, 1000000);
|
||||
return Cesium.Cartesian3.fromDegrees(98, 48, 1000000);
|
||||
}, true)
|
||||
}
|
||||
>
|
||||
<ModelGraphics uri={arrowRound} minimumPixelSize={128} />
|
||||
<ModelGraphics
|
||||
uri={arrowRound}
|
||||
minimumPixelSize={128}
|
||||
color={anticycloneColor}
|
||||
colorBlendMode={Cesium.ColorBlendMode.REPLACE}
|
||||
/>
|
||||
</Entity>
|
||||
<Entity
|
||||
id={"Anticyclone-4"}
|
||||
@ -121,11 +146,16 @@ function Cyclone() {
|
||||
new Cesium.CallbackProperty(function (time, result) {
|
||||
const passTime = getPassTime();
|
||||
if (passTime < 50) return;
|
||||
return Cesium.Cartesian3.fromDegrees(88, 60, 0);
|
||||
return Cesium.Cartesian3.fromDegrees(98, 48, 0);
|
||||
}, true)
|
||||
}
|
||||
>
|
||||
<ModelGraphics uri={arrowRound} minimumPixelSize={128} />
|
||||
<ModelGraphics
|
||||
uri={arrowRound}
|
||||
minimumPixelSize={128}
|
||||
color={anticycloneColor}
|
||||
colorBlendMode={Cesium.ColorBlendMode.REPLACE}
|
||||
/>
|
||||
</Entity>
|
||||
</Fragment>
|
||||
);
|
||||
|
59
src/components/domain/One/EntityLegend.jsx
Normal file
@ -0,0 +1,59 @@
|
||||
import cyclone from "@/assets/cyclone.png";
|
||||
import anticyclone from "@/assets/anticyclone.png";
|
||||
import updraft from "@/assets/updraft.png";
|
||||
import waterwapor from "@/assets/waterwapor.png";
|
||||
|
||||
function EntityLegend() {
|
||||
return (
|
||||
<div className="entity-legend">
|
||||
<div className="entity-legend-item">
|
||||
<div style={{ fontWeight: 800, color: "#04fafc" }}>- - - -</div>
|
||||
<div className="entity-legend-item-name">Rossby wave path</div>
|
||||
</div>
|
||||
<div className="entity-legend-item">
|
||||
<div>
|
||||
<img src={anticyclone} width={32} />
|
||||
</div>
|
||||
<div className="entity-legend-item-name">anticyclone</div>
|
||||
</div>
|
||||
<div className="entity-legend-item">
|
||||
<div>
|
||||
<img src={updraft} width={32} height={32} />
|
||||
</div>
|
||||
<div className="entity-legend-item-name">updraft</div>
|
||||
</div>
|
||||
<div className="entity-legend-item">
|
||||
<div
|
||||
style={{ width: 32, height: 32, backgroundColor: "#6080b0" }}
|
||||
></div>
|
||||
<div className="entity-legend-item-name">barotroptic structure</div>
|
||||
</div>
|
||||
<div className="entity-legend-item">
|
||||
<div>
|
||||
<img src={cyclone} width={32} />
|
||||
</div>
|
||||
<div className="entity-legend-item-name">cyclone</div>
|
||||
</div>
|
||||
<div className="entity-legend-item">
|
||||
<div
|
||||
style={{
|
||||
width: 48,
|
||||
height: 32,
|
||||
borderRadius: "999px",
|
||||
backgroundColor: "#8d5555",
|
||||
marginLeft: -16,
|
||||
}}
|
||||
></div>
|
||||
<div className="entity-legend-item-name">warming</div>
|
||||
</div>
|
||||
{/* <div className="entity-legend-item">
|
||||
<div>
|
||||
<img src={waterwapor} width={32} />
|
||||
</div>
|
||||
<div className="entity-legend-item-name">water wapor path</div>
|
||||
</div> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default EntityLegend;
|
@ -18,7 +18,6 @@ function LabradorImageLayer() {
|
||||
const [index, setIndex] = useState(0);
|
||||
|
||||
//viewer.clock?.shouldAnimate
|
||||
console.log("viewer.clock?.shouldAnimate :>> ", viewer.clock?.shouldAnimate);
|
||||
|
||||
useEffect(() => {
|
||||
const { labrador } = imageLayer;
|
||||
|
@ -4,11 +4,11 @@ import { useInterval } from "ahooks";
|
||||
|
||||
// 从拉布拉多海到青藏高原
|
||||
const dataLabToQTP = [
|
||||
{ longitude: -55, latitude: 58, height: 1000000, time: 10 },
|
||||
{ longitude: -32, latitude: 72.2, height: 1000000, time: 20 },
|
||||
{ longitude: 20, latitude: 77, height: 1000000, time: 30 },
|
||||
{ longitude: 63, latitude: 69.9, height: 1000000, time: 40 },
|
||||
{ longitude: 88, latitude: 60, height: 1000000, time: 50 },
|
||||
{ longitude: -62, latitude: 69, height: 1000000, time: 10 },
|
||||
{ longitude: -20, latitude: 55, height: 1000000, time: 20 },
|
||||
{ longitude: 29, latitude: 49, height: 1000000, time: 30 },
|
||||
{ longitude: 62, latitude: 45, height: 1000000, time: 40 },
|
||||
{ longitude: 98, latitude: 48, height: 1000000, time: 50 },
|
||||
];
|
||||
|
||||
function Point() {
|
||||
|
@ -17,7 +17,7 @@ function Updraft() {
|
||||
const passTime = currentTime.secondsOfDay - startTime.secondsOfDay;
|
||||
const height = 100000 * passTime;
|
||||
return Cesium.Cartesian3.fromDegreesArrayHeights(
|
||||
[-55, 58, 0, -55, 58, min([height, 1000000])],
|
||||
[-62, 69, 0, -62, 69, min([height, 1000000])],
|
||||
Cesium.Ellipsoid.WGS84,
|
||||
result
|
||||
);
|
||||
|
38
src/components/domain/One/WaterVaporPath.jsx
Normal file
@ -0,0 +1,38 @@
|
||||
import { Fragment } from "react";
|
||||
import { Entity, PolylineGraphics } from "resium";
|
||||
|
||||
function WaterVaporPath() {
|
||||
return (
|
||||
<Fragment>
|
||||
<Entity>
|
||||
<PolylineGraphics
|
||||
positions={Cesium.Cartesian3.fromDegreesArrayHeights([
|
||||
103, 46, 0, 93, 38, 0,
|
||||
])}
|
||||
width={10}
|
||||
material={
|
||||
new Cesium.PolylineDashMaterialProperty({
|
||||
color: Cesium.Color.fromCssColorString("#406ec5"),
|
||||
dashLength: 20,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Entity>
|
||||
<Entity>
|
||||
<PolylineGraphics
|
||||
positions={Cesium.Cartesian3.fromDegreesArrayHeights([
|
||||
93, 38, 0, 93, 37.9, 0,
|
||||
])}
|
||||
width={30}
|
||||
material={
|
||||
new Cesium.PolylineArrowMaterialProperty(
|
||||
Cesium.Color.fromCssColorString("#406ec5")
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Entity>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
export default WaterVaporPath;
|
@ -4,7 +4,7 @@ import WavePoint from "@/components/common/WavePoint";
|
||||
export default function WavePoints() {
|
||||
return (
|
||||
<Fragment>
|
||||
<WavePoint stationLon={-55} stationLat={58} />
|
||||
<WavePoint stationLon={-62} stationLat={69} />
|
||||
<WavePoint stationLon={88} stationLat={32.5} />
|
||||
</Fragment>
|
||||
);
|
||||
|
@ -13,6 +13,8 @@ import Legend from "./Legend";
|
||||
import LabradorImageLayer from "./LabradorImageLayer";
|
||||
import TibetImageLayer from "./TibetImageLayer";
|
||||
import ChartPanel from "./ChartPanel";
|
||||
import EntityLegend from "./EntityLegend";
|
||||
// import WaterVaporPath from "./WaterVaporPath";
|
||||
|
||||
export default function DomainOne() {
|
||||
return (
|
||||
@ -34,7 +36,9 @@ export default function DomainOne() {
|
||||
<Cyclone />
|
||||
<Barotropic />
|
||||
<WavePoints />
|
||||
{/* <WaterVaporPath /> */}
|
||||
<Updraft />
|
||||
<EntityLegend />
|
||||
<Legend />
|
||||
<LabradorImageLayer />
|
||||
<TibetImageLayer />
|
||||
|
@ -3,29 +3,61 @@ import CustomToolbar from "@/components/common/CustomToolbar";
|
||||
import CustomClock from "@/components/common/CustomClock";
|
||||
import TextInfoPanel from "@/components/common/TextInfoPanel";
|
||||
import CustomFlyTo from "./CustomFlyTo";
|
||||
import WavePoints from "./WavePoints";
|
||||
import ChartPanel from "./ChartPanel";
|
||||
import LandImageLayer from "./LandImageLayer";
|
||||
import OceanImageLayer from "./OceanImageLayer";
|
||||
import WavePoint from "@/components/common/WavePoint";
|
||||
import { CameraFlyTo } from "resium";
|
||||
import { Cartesian3 } from "cesium";
|
||||
|
||||
export default function DomainOne() {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
height: "100%",
|
||||
}}
|
||||
>
|
||||
<div style={{ flex: 1, position: "relative" }}>
|
||||
<MapLayout>
|
||||
<div className="title">
|
||||
<div className="title" style={{ zIndex: 999 }}>
|
||||
两极协同—连接南极和北极的热带大西洋经向模的媒介作用
|
||||
</div>
|
||||
<CustomToolbar />
|
||||
<CustomClock />
|
||||
<CustomFlyTo />
|
||||
<CameraFlyTo
|
||||
duration={5}
|
||||
destination={Cartesian3.fromDegrees(0, -85, 16000000)}
|
||||
/>
|
||||
<div className="left-panel one">
|
||||
<TextInfoPanel content="利用GISTEMP资料,通过EEMD分解方法提取两极温度多年代际变化序列,发现南北极温度变化的跷跷板现象与大西洋多年代际振荡AMO紧密相关。而AMO与热带大西洋经向模AMM在年代际尺度上显著相关。ERA5再分析资料显示AMM可以通过Rossby波影响西南极的气温与海冰偶极子。因此,AMM可能在联系南北极气候变化的中起到了重要的媒介作用。" />
|
||||
</div>
|
||||
<WavePoint stationLon={88} stationLat={-85} />
|
||||
{/* <LandImageLayer /> */}
|
||||
{/* <OceanImageLayer /> */}
|
||||
</MapLayout>
|
||||
</div>
|
||||
<div style={{ flex: 1, position: "relative" }}>
|
||||
<MapLayout>
|
||||
<CameraFlyTo
|
||||
duration={5}
|
||||
destination={Cartesian3.fromDegrees(0, 88, 16000000)}
|
||||
/>
|
||||
<WavePoint stationLon={0} stationLat={87} />
|
||||
</MapLayout>
|
||||
</div>
|
||||
<div style={{ flex: 1, position: "relative" }}>
|
||||
<MapLayout>
|
||||
<CameraFlyTo
|
||||
duration={5}
|
||||
destination={Cartesian3.fromDegrees(100, 33, 16000000)}
|
||||
/>
|
||||
<div className="right-panel one">
|
||||
<ChartPanel />
|
||||
</div>
|
||||
<WavePoints />
|
||||
<LandImageLayer />
|
||||
<OceanImageLayer />
|
||||
<WavePoint stationLon={88} stationLat={-85} />
|
||||
</MapLayout>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
155
src/components/domain/Two/Circles.jsx
Normal file
@ -0,0 +1,155 @@
|
||||
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";
|
||||
|
||||
const lowCircle = (
|
||||
<EllipsoidGraphics
|
||||
radii={new Cesium.Cartesian3(460000.0, 460000.0, 460000.0)}
|
||||
innerRadii={new Cesium.Cartesian3(415000.0, 415000.0, 415000.0)}
|
||||
minimumCone={Cesium.Math.toRadians(89.8)}
|
||||
maximumCone={Cesium.Math.toRadians(90.2)}
|
||||
material={new Cesium.Color(0.29, 0.46, 0.77, 1)}
|
||||
/>
|
||||
);
|
||||
const highCircle = (
|
||||
<EllipsoidGraphics
|
||||
radii={new Cesium.Cartesian3(460000.0, 460000.0, 460000.0)}
|
||||
innerRadii={new Cesium.Cartesian3(415000.0, 415000.0, 415000.0)}
|
||||
minimumCone={Cesium.Math.toRadians(89.8)}
|
||||
maximumCone={Cesium.Math.toRadians(90.2)}
|
||||
material={new Cesium.Color(0.99, 0.23, 0.23, 1)}
|
||||
/>
|
||||
);
|
||||
|
||||
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;
|
||||
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 (
|
||||
<Fragment>
|
||||
<Entity
|
||||
id={`${id}-up`}
|
||||
position={Cesium.Cartesian3.fromDegrees(lon, lat, 1000000)}
|
||||
>
|
||||
{circle}
|
||||
</Entity>
|
||||
{show && (
|
||||
<Entity id={`${id}-connection-line`}>
|
||||
<PolylineGraphics
|
||||
positions={Cesium.Cartesian3.fromDegreesArrayHeights([
|
||||
lon,
|
||||
lat,
|
||||
1000000,
|
||||
lon,
|
||||
lat,
|
||||
0,
|
||||
])}
|
||||
width={2}
|
||||
material={
|
||||
new Cesium.PolylineDashMaterialProperty({
|
||||
color: Cesium.Color.WHITE,
|
||||
dashLength: 4,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Entity>
|
||||
)}
|
||||
<Entity
|
||||
id={`${id}-down`}
|
||||
position={Cesium.Cartesian3.fromDegrees(lon, lat, 0)}
|
||||
>
|
||||
{circle}
|
||||
</Entity>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Circle
|
||||
isLow={true}
|
||||
id={`low-circle-1`}
|
||||
showTime={10}
|
||||
lon={-110}
|
||||
lat={-60}
|
||||
/>
|
||||
<Circle
|
||||
isLow={false}
|
||||
id={`height-circle-1`}
|
||||
showTime={20}
|
||||
lon={-30}
|
||||
lat={-55}
|
||||
/>
|
||||
<Circle
|
||||
isLow={true}
|
||||
id={`low-circle-2`}
|
||||
showTime={30}
|
||||
lon={30}
|
||||
lat={-40}
|
||||
/>
|
||||
<Circle
|
||||
isLow={false}
|
||||
id={`height-circle-2`}
|
||||
showTime={40}
|
||||
lon={65}
|
||||
lat={-35}
|
||||
/>
|
||||
<Circle
|
||||
isLow={true}
|
||||
id={`low-circle-3`}
|
||||
showTime={50}
|
||||
lon={95}
|
||||
lat={-30}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
export default Circles;
|
@ -106,7 +106,7 @@ function CustomFlyTo() {
|
||||
plateauOptions.pitchAdjustHeight = 1000;
|
||||
sideViewOptions.pitchAdjustHeight = 1000;
|
||||
}
|
||||
camera.flyTo(antarcticalOptions);
|
||||
camera.flyTo(sideViewOptions);
|
||||
}
|
||||
cameraFlyToLine();
|
||||
return <></>;
|
||||
|
61
src/components/domain/Two/EntityLegend.jsx
Normal file
@ -0,0 +1,61 @@
|
||||
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";
|
||||
|
||||
function EntityLegend() {
|
||||
return (
|
||||
<div className="entity-legend">
|
||||
<div className="entity-legend-item">
|
||||
<div>
|
||||
<img src={low} width={32} />
|
||||
</div>
|
||||
<div className="entity-legend-item-name">Low</div>
|
||||
</div>
|
||||
<div className="entity-legend-item">
|
||||
<div>
|
||||
<img src={anticyclone} width={32} />
|
||||
</div>
|
||||
<div className="entity-legend-item-name">Moisture transport</div>
|
||||
</div>
|
||||
<div className="entity-legend-item">
|
||||
<div>
|
||||
<img src={antiHadleyCell} width={32} />
|
||||
</div>
|
||||
<div className="entity-legend-item-name">anti-Hadley cell</div>
|
||||
</div>
|
||||
<div className="entity-legend-item">
|
||||
<div>
|
||||
<img src={high} width={32} />
|
||||
</div>
|
||||
<div className="entity-legend-item-name">High</div>
|
||||
</div>
|
||||
<div className="entity-legend-item">
|
||||
<div>
|
||||
<img src={cyclone} width={32} />
|
||||
</div>
|
||||
<div className="entity-legend-item-name">Rainfall</div>
|
||||
</div>
|
||||
<div className="entity-legend-item">
|
||||
<div
|
||||
style={{
|
||||
width: 48,
|
||||
height: 32,
|
||||
borderRadius: "999px",
|
||||
backgroundColor: "#8d5555",
|
||||
}}
|
||||
></div>
|
||||
<div className="entity-legend-item-name">Surface wd anomaly</div>
|
||||
</div>
|
||||
{/* <div className="entity-legend-item">
|
||||
<div>
|
||||
<img src={waterwapor} width={32} />
|
||||
</div>
|
||||
<div className="entity-legend-item-name">water wapor path</div>
|
||||
</div> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default EntityLegend;
|
@ -30,7 +30,7 @@ function Legend() {
|
||||
return (
|
||||
<Fragment>
|
||||
<div className="vertical-legend" style={{ right: 12 }}>
|
||||
<div className="legend-title">青藏高原感热通量</div>
|
||||
<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(
|
||||
@ -64,7 +64,7 @@ function Legend() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="vertical-legend" style={{ right: 154 }}>
|
||||
<div className="legend-title">青藏高原降水量</div>
|
||||
<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(
|
||||
|
@ -82,7 +82,6 @@ function Point() {
|
||||
<PointGraphics
|
||||
show={true}
|
||||
color={Cesium.Color.SKYBLUE}
|
||||
pixelSize={10}
|
||||
outlineColor={Cesium.Color.YELLOW}
|
||||
outlineWidth={3}
|
||||
/>
|
||||
|
@ -8,6 +8,8 @@ import PlateauPolygon from "./PlateauPolygon";
|
||||
import WavePoints from "./WavePoints";
|
||||
import Legend from "./Legend";
|
||||
import IndianOceanSST from "./IndiaOceanSST";
|
||||
import EntityLegend from "./EntityLegend";
|
||||
import Circles from "./Circles";
|
||||
|
||||
export default function DomainTwo() {
|
||||
return (
|
||||
@ -16,12 +18,14 @@ export default function DomainTwo() {
|
||||
<CustomToolbar />
|
||||
<CustomClock />
|
||||
<CustomFlyTo />
|
||||
<Circles />
|
||||
<Point />
|
||||
<PlateauPolygon />
|
||||
<div className="left-panel one">
|
||||
<TextInfoPanel content="利用ERA5再分析数据诊断和CESM印度洋海温强迫数值实验,发现5月的南极涛动正位相(AAO)激发原子阿蒙森海的纬向波列异常。该异常造成印度洋海温降低进而激发环流异常造成高原上空降水增加、感热通量降低。这一结果有助于提高青藏高原热源的预测技巧,改进亚洲夏季风的预测。" />
|
||||
</div>
|
||||
<WavePoints />
|
||||
<EntityLegend />
|
||||
<Legend />
|
||||
<IndianOceanSST />
|
||||
</MapLayout>
|
||||
|
@ -35,23 +35,23 @@ function NavBar() {
|
||||
onClick={() => navigateHandler(3)}
|
||||
/>
|
||||
<NavBarButton
|
||||
text={"三极协同-青藏高原上层"}
|
||||
text={"三级联动-青藏高原上层"}
|
||||
onClick={() => navigateHandler(4)}
|
||||
/>
|
||||
<NavBarButton
|
||||
text={"三极协同-东亚夏季风"}
|
||||
text={"三级联动-东亚夏季风"}
|
||||
onClick={() => navigateHandler(5)}
|
||||
/>
|
||||
<NavBarButton
|
||||
text={"三极协同-东亚夏季风"}
|
||||
text={"三级联动-东亚夏季风"}
|
||||
onClick={() => navigateHandler(5)}
|
||||
/>
|
||||
<NavBarButton
|
||||
text={"三极协同-东亚夏季风"}
|
||||
text={"三级联动-东亚夏季风"}
|
||||
onClick={() => navigateHandler(5)}
|
||||
/>
|
||||
<NavBarButton
|
||||
text={"三极协同-东亚夏季风"}
|
||||
text={"三级联动-东亚夏季风"}
|
||||
onClick={() => navigateHandler(5)}
|
||||
/>
|
||||
</div>
|
||||
|
@ -6,7 +6,7 @@
|
||||
flex-direction: column;
|
||||
|
||||
.title {
|
||||
height: 72px;
|
||||
height: 120px;
|
||||
background: url("../../../assets/title.png") 50% no-repeat;
|
||||
margin-bottom: 8px;
|
||||
color: #04fbfd;
|
||||
|
@ -1,10 +1,13 @@
|
||||
import { Entity, LabelGraphics, useCesium } from "resium";
|
||||
import { useState } from "react";
|
||||
import { useCesium } from "resium";
|
||||
|
||||
let handler;
|
||||
|
||||
function Picker() {
|
||||
const { viewer } = useCesium();
|
||||
const { scene } = viewer;
|
||||
const [lon, setLon] = useState(0);
|
||||
const [lat, setLat] = useState(0);
|
||||
|
||||
// Mouse over the globe to see the cartographic position
|
||||
handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
|
||||
@ -21,6 +24,8 @@ function Picker() {
|
||||
const latitudeString = Cesium.Math.toDegrees(
|
||||
cartographic.latitude
|
||||
).toFixed(2);
|
||||
setLon(longitudeString);
|
||||
setLat(latitudeString);
|
||||
// console.log("longitudeString, :>> ", longitudeString, latitudeString);
|
||||
// entity.position = cartesian;
|
||||
// entity.label.show = true;
|
||||
@ -33,16 +38,14 @@ function Picker() {
|
||||
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
|
||||
|
||||
return (
|
||||
<Entity>
|
||||
<LabelGraphics
|
||||
show={false}
|
||||
showBackground={true}
|
||||
font={"14px monospace"}
|
||||
horizontalOrigin={Cesium.HorizontalOrigin.LEFT}
|
||||
verticalOrigin={Cesium.VerticalOrigin.TOP}
|
||||
pixelOffset={new Cesium.Cartesian2(15, 0)}
|
||||
/>
|
||||
</Entity>
|
||||
<div
|
||||
className="picker"
|
||||
style={{ color: "white", position: "absolute", top: 0, right: 0 }}
|
||||
>
|
||||
<p>
|
||||
longitudeString, latitudeString {lon}, {lat}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,20 @@
|
||||
import { Viewer } from "resium";
|
||||
import { Entity, PointGraphics, Viewer } from "resium";
|
||||
import styles from "./index.module.less";
|
||||
import { useMemo } from "react";
|
||||
import { BingMapsImageryProvider, BingMapsStyle } from "cesium";
|
||||
import Picker from "./Picker";
|
||||
|
||||
function MapLayout({ children, className, ...rest }) {
|
||||
const imageryProvider = useMemo(
|
||||
() =>
|
||||
new BingMapsImageryProvider({
|
||||
url: "https://dev.virtualearth.net",
|
||||
key: "Ahv1KDJwbpJl4V8zklaRXcoueWXoNLo16osJiU4Zk07vc-VNNzJ0gB81TZyPqNFz",
|
||||
mapStyle: BingMapsStyle.AERIAL,
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<Viewer
|
||||
className={`${styles.cesiumContainer} ${className}`}
|
||||
@ -10,13 +23,19 @@ function MapLayout({ children, className, ...rest }) {
|
||||
sceneModePicker={false}
|
||||
navigationHelpButton={false}
|
||||
shouldAnimate={false}
|
||||
// infoBox={false}
|
||||
infoBox={false}
|
||||
timeline={false}
|
||||
fullscreenButton={false}
|
||||
geocoder={false}
|
||||
baseLayerPicker={false}
|
||||
animation={false}
|
||||
selectionIndicator={false}
|
||||
// imageryProvider={imageryProvider}
|
||||
>
|
||||
<Entity position={Cesium.Cartesian3.fromDegreesArray([0, 90])}>
|
||||
<PointGraphics color={Cesium.Color.SKYBLUE} pixelSize={10} />
|
||||
</Entity>
|
||||
<Picker />
|
||||
{children}
|
||||
</Viewer>
|
||||
);
|
||||
|
@ -121,6 +121,7 @@
|
||||
font-size: 16px;
|
||||
text-indent: 2em;
|
||||
line-height: 1.5;
|
||||
z-index: 999;
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,6 +151,12 @@
|
||||
border: 1px solid #04fbfd;
|
||||
color: #02f9ff !important;
|
||||
background-color: #1f485690;
|
||||
|
||||
.ant-form-item-label {
|
||||
label {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chart-info-panel {
|
||||
@ -160,6 +167,12 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,6 +180,32 @@
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
.entity-legend {
|
||||
position: absolute;
|
||||
bottom: 140px;
|
||||
width: 50%;
|
||||
left: 25%;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-gap: 10px;
|
||||
background-color: #1f485690;
|
||||
border: 1px solid #04fbfd;
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
color: white;
|
||||
user-select: none;
|
||||
|
||||
.entity-legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.entity-legend-item-name {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.legend {
|
||||
position: absolute;
|
||||
bottom: 40px;
|
||||
|