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