This commit is contained in:
Aifeilong 2023-10-12 17:56:48 +08:00
parent 117523a7dd
commit 953a201081
45 changed files with 1120 additions and 551 deletions

View File

@ -1,9 +1,10 @@
import { ClockRange, JulianDate } from "cesium";
import { Clock } from "resium";
//
const start = Cesium.JulianDate.fromDate(new Date());
const start = JulianDate.fromDate(new Date());
//
const stop = Cesium.JulianDate.addSeconds(start, 60, new Cesium.JulianDate());
const stop = JulianDate.addSeconds(start, 30, new JulianDate());
function CustomClock() {
return (
@ -13,7 +14,7 @@ function CustomClock() {
startTime={start.clone()}
stopTime={stop.clone()}
currentTime={start.clone()}
clockRange={Cesium.ClockRange.LOOP_STOP}
clockRange={ClockRange.LOOP_STOP}
// onTick={(clock) => {
// if (!clock.shouldAnimate) return;
// }}

View File

@ -1,6 +1,14 @@
import { Fragment } from "react";
import { Entity, EllipseGraphics } from "resium";
import { Entity, EllipseGraphics, LabelGraphics } from "resium";
import wave from "@/assets/wave.png";
import {
CallbackProperty,
Cartesian2,
Cartesian3,
Color,
ImageMaterialProperty,
LabelStyle,
} from "cesium";
function WavePoint({
stationLon,
@ -10,6 +18,7 @@ function WavePoint({
eachInterval = 1500,
maxR = 3600 * 100,
color = "WHITE",
labelText,
}) {
const data = {
stationLon, //
@ -43,25 +52,21 @@ function WavePoint({
const point1 = (
<Entity
id={`wave-point-1-${stationLon}-${stationLat}`}
position={Cesium.Cartesian3.fromDegrees(
data.stationLon,
data.stationLat,
0
)}
position={Cartesian3.fromDegrees(data.stationLon, data.stationLat, 0)}
show={true}
>
<EllipseGraphics
semiMinorAxis={new Cesium.CallbackProperty(changeOne, false)}
semiMajorAxis={new Cesium.CallbackProperty(changeR2, false)}
semiMinorAxis={new CallbackProperty(changeOne, false)}
semiMajorAxis={new CallbackProperty(changeR2, false)}
height={10}
material={
new Cesium.ImageMaterialProperty({
new ImageMaterialProperty({
image: data.imageUrl,
repeat: Cesium.Cartesian2(1.0, 1.0),
repeat: Cartesian2(1.0, 1.0),
transparent: true,
color: new Cesium.CallbackProperty(function () {
color: new CallbackProperty(function () {
var alp = 1 - r1 / data.maxR;
return Cesium.Color[color].withAlpha(alp);
return Color[color].withAlpha(alp);
}, false),
})
}
@ -89,26 +94,22 @@ function WavePoint({
}
point2 = (
<Entity
position={Cesium.Cartesian3.fromDegrees(
data.stationLon,
data.stationLat,
0
)}
position={Cartesian3.fromDegrees(stationLon, stationLat, 0)}
show={true}
id={`wave-point-2-${stationLon}-${stationLat}`}
>
<EllipseGraphics
semiMinorAxis={new Cesium.CallbackProperty(changeTwo, false)}
semiMajorAxis={new Cesium.CallbackProperty(changeR12, false)}
semiMinorAxis={new CallbackProperty(changeTwo, false)}
semiMajorAxis={new CallbackProperty(changeR12, false)}
height={10}
material={
new Cesium.ImageMaterialProperty({
new ImageMaterialProperty({
image: data.imageUrl,
repeat: Cesium.Cartesian2(1.0, 1.0),
repeat: Cartesian2(1.0, 1.0),
transparent: true,
color: new Cesium.CallbackProperty(function () {
color: new CallbackProperty(function () {
var alp = 1 - r1 / data.maxR;
return Cesium.Color.WHITE.withAlpha(alp);
return Color.WHITE.withAlpha(alp);
//entity entity
}, false),
})
@ -122,6 +123,20 @@ function WavePoint({
<Fragment>
{point1}
{point2}
{labelText && (
<Entity position={Cartesian3.fromDegrees(stationLon, stationLat, 0)}>
<LabelGraphics
text={labelText}
font="24px Helvetica"
fillColor={Color.SKYBLUE}
outlineColor={Color.BLACK}
outlineWidth={2}
style={LabelStyle.FILL_AND_OUTLINE}
eyeOffset={new Cartesian2(0, 200000)}
/>{" "}
</Entity>
)}
</Fragment>
);
}

View File

@ -0,0 +1,80 @@
import { Cartesian2, Cartesian3, Color, LabelStyle } from "cesium";
import { Fragment } from "react";
import { Entity, LabelGraphics } from "resium";
const points = [
{
position: [13, -70, 30, -67],
name: "sic_s",
},
{
position: [93, 29, 99, 35],
name: "sc_tp",
},
{
position: [68, 48, 76, 52],
name: "sc_ea",
},
{
position: [150, 60, 160, 70],
name: "ts_ac",
},
{
position: [130, 20, 140, 28],
name: "ts_wp",
},
{
position: [150, 12, 175, 22],
name: "ts_np",
},
{
position: [-110, 20, -95, 35],
name: "ts_arn",
},
{
position: [10, -12, 45, -2],
name: "ts_af",
},
{ position: [80, 10, 110, 20], name: "ts_io" },
{ position: [-150, -10, -40, 5], name: "ts_ep" },
{ position: [65, -62, 90, -57], name: "ts_aa" },
{
position: [145, -30, 155, -20],
name: "ts_ea",
},
{ position: [165, -45, 180, -30], name: "ts_nz" },
{ position: [-140, -60, -110, -45], name: "ts_sp" },
{ position: [-70, -78, -50, -70], name: "ts_wd" },
];
function Labels() {
return (
<Fragment>
{points.map((point, index) => {
const { position, name } = point;
const [x, y, z, m] = position;
const lon = (x + z) / 2;
const lat = (y + m) / 2;
return (
<Entity
key={`rectangle-label-${index}`}
position={Cartesian3.fromDegrees(lon, lat, 0)}
>
<LabelGraphics
text={name}
font="24px Helvetica"
fillColor={Color.SKYBLUE}
outlineColor={Color.BLACK}
outlineWidth={2}
style={LabelStyle.FILL_AND_OUTLINE}
eyeOffset={new Cartesian2(0, 0)}
/>
</Entity>
);
})}
</Fragment>
);
}
export default Labels;

View File

@ -1,21 +1,21 @@
import { Rectangle, Color, Math as CesiumMath } from "cesium";
import { Entity, RectangleGraphics } from "resium";
const points = [
[13, -70, 30, -67],
[93, 29, 99, 35],
[68, 48, 76, 52],
[150, 60, 160, 70],
[130, 20, 140, 28],
[150, 12, 175, 22],
[-110, 20, -95, 35],
[10, -12, 45, -2],
[80, 10, 110, 20],
[-150, -10, -40, 5],
[65, -62, 90, -57],
[145, -30, 155, -20],
[165, -45, 180, -30],
[-140, -60, -110, -45],
[-70, -78, -50, -70],
[13, -70, 30, -67], //sic_s
[93, 29, 99, 35], //sc_tp
[68, 48, 76, 52], //sc_ea
[150, 60, 160, 70], //ts_ac
[130, 20, 140, 28], //wp
[150, 12, 175, 22], //np
[-110, 20, -95, 35], //arn
[10, -12, 45, -2], // af
[80, 10, 110, 20], // io
[-150, -10, -40, 5], //ep
[65, -62, 90, -57], // aa
[145, -30, 155, -20], // ea
[165, -45, 180, -30], // nz
[-140, -60, -110, -45], // sp
[-70, -78, -50, -70], // wd
];
export default function RectangleLayer() {
return points.map((p, i) => (

View File

@ -8,6 +8,7 @@ import ChartPanel from "./ChartPanel";
import RectangleLayer from "./RectangleLayer";
import { useState } from "react";
import Legend from "./Legend";
import Labels from "./Labels";
export default function DomainFour() {
const [show, setShow] = useState(false);
@ -31,6 +32,7 @@ XGBoos是基于梯度提升决策树方法的分类和回归模型。 LightGBM
<div className="bottom-panel">{show && <ChartPanel />}</div>
</div>
{/* <Legend /> */}
<Labels />
<RectangleLayer />
</MapLayout>
);

View File

@ -1,6 +1,6 @@
import { useCallback, useState } from "react";
import { Entity, LabelGraphics, EllipseGraphics, useCesium } from "resium";
import { Color, Cartesian3, LabelStyle } from "cesium";
import { Entity, EllipseGraphics, useCesium } from "resium";
import { Color, Cartesian3 } from "cesium";
import { useInterval } from "ahooks";
function Barotropic() {
@ -14,7 +14,7 @@ function Barotropic() {
stopTime.secondsOfDay - currentTime.secondsOfDay
);
if (leftTime < 10) {
if (leftTime < 5) {
setShow(true);
} else if (show) setShow(false);
}, [show]);
@ -34,16 +34,6 @@ function Barotropic() {
extrudedHeight={1000000.0}
rotation={0}
/>
<LabelGraphics
position={Cartesian3.fromDegrees(98, 48, 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>
);
}

View File

@ -1,75 +1,64 @@
import { Cartesian3, EasingFunction, Math } from "cesium";
import { useDispatch } from "react-redux";
import { Camera, useCesium } from "resium";
import { useCesium } from "resium";
function CustomFlyTo() {
const { viewer } = useCesium();
const { camera } = viewer;
const dispatch = useDispatch();
function cameraFlyToLine(adjustPitch) {
function cameraFlyToLine() {
const step1 = {
destination: Cartesian3.fromDegrees(-10, 50, 6000000),
duration: 5,
complete: () => {
camera.flyTo(step2);
},
easingFunction: EasingFunction.LINEAR_NONE,
};
const step2 = {
destination: Cartesian3.fromDegrees(35, 49, 10000000),
duration: 8,
complete: () => {
camera.flyTo(barotorpic);
},
easingFunction: EasingFunction.LINEAR_NONE,
};
// barotorpic
const barotorpic = {
destination: Cesium.Cartesian3.fromDegrees(80, 46, 15000000),
duration: 30,
destination: Cartesian3.fromDegrees(75, 48, 15000000),
duration: 7,
complete: () => {},
easingFunction: EasingFunction.LINEAR_NONE,
orientation: {
heading: 6.283,
pitch: -1.569,
roll: 0.45,
},
};
//
const sideViewOptions = {
destination: Cesium.Cartesian3.fromDegrees(-50, 46, 2000000),
destination: Cartesian3.fromDegrees(-50, 46, 2000000),
duration: 5,
orientation: {
heading: Cesium.Math.toRadians(-15.0),
pitch: -Cesium.Math.PI_OVER_FOUR,
heading: Math.toRadians(-15.0),
pitch: -Math.PI_OVER_FOUR,
roll: 0.0,
},
complete: () => {
viewer.clock.shouldAnimate = true;
setTimeout(function () {
camera.flyTo(barotorpic);
}, 1000);
camera.flyTo(step1);
}, 5000);
dispatch.data.updateImageLayer({
labrador: true,
});
},
};
//
const plateauOptions = {
destination: Cesium.Cartesian3.fromDegrees(90, 20, 1600000),
duration: 5,
orientation: {
heading: Cesium.Math.toRadians(-15.0),
pitch: -Cesium.Math.PI_OVER_FOUR,
roll: 0.0,
},
complete: function () {
setTimeout(function () {
camera.flyTo(sideViewOptions);
}, 1000);
},
};
//
const labrador = {
destination: Cesium.Cartesian3.fromDegrees(-55, 45, 1600000),
duration: 10,
orientation: {
heading: Cesium.Math.toRadians(-15.0),
pitch: -Cesium.Math.PI_OVER_FOUR,
roll: 0.0,
},
complete: function () {
setTimeout(function () {
camera.flyTo(plateauOptions);
}, 1000);
},
};
if (adjustPitch) {
plateauOptions.pitchAdjustHeight = 1000;
sideViewOptions.pitchAdjustHeight = 1000;
}
camera.flyTo(sideViewOptions);
}
cameraFlyToLine();

View File

@ -1,6 +1,14 @@
import { Fragment, useCallback } from "react";
import { Entity, ModelGraphics, useCesium } from "resium";
import { Entity, LabelGraphics, ModelGraphics, useCesium } from "resium";
import arrowRound from "@/assets/arrow_round.glb";
import {
CallbackProperty,
Cartesian2,
Cartesian3,
Color,
ColorBlendMode,
LabelStyle,
} from "cesium";
let totalSeconds = 60;
let numberOfSamples = 120;
@ -44,18 +52,18 @@ function Cyclone() {
return _time;
}, [viewer]);
const anticycloneColor = Cesium.Color.fromCssColorString("#f70000");
const cycloneColor = Cesium.Color.fromCssColorString("#00AC4D");
const anticycloneColor = Color.fromCssColorString("#f70000");
const cycloneColor = Color.fromCssColorString("#00AC4D");
return (
<Fragment>
<Entity
id={"Anticyclone-1"}
position={
new Cesium.CallbackProperty(function (time, result) {
new CallbackProperty(function (time, result) {
const passTime = getPassTime();
if (passTime < 10) return;
return Cesium.Cartesian3.fromDegrees(-62, 69, 1000000);
if (passTime < 5) return;
return Cartesian3.fromDegrees(-62, 69, 1000000);
}, true)
}
>
@ -69,16 +77,26 @@ function Cyclone() {
// }),
// }}
color={anticycloneColor}
colorBlendMode={Cesium.ColorBlendMode.REPLACE}
colorBlendMode={ColorBlendMode.REPLACE}
/>
<LabelGraphics
position={Cartesian3.fromDegrees(98, 48, 0)}
text={"Anticyclone"}
font="24px Helvetica"
fillColor={Color.SKYBLUE}
outlineColor={Color.BLACK}
outlineWidth={2}
style={LabelStyle.FILL_AND_OUTLINE}
eyeOffset={new Cartesian2(0, 200000)}
/>
</Entity>
<Entity
id={"Cyclone-1"}
position={
new Cesium.CallbackProperty(function (time, result) {
new CallbackProperty(function (time, result) {
const passTime = getPassTime();
if (passTime < 20) return;
return Cesium.Cartesian3.fromDegrees(-20, 55, 1000000);
if (passTime < 10) return;
return Cartesian3.fromDegrees(-20, 55, 1000000);
}, true)
}
>
@ -86,16 +104,16 @@ function Cyclone() {
uri={arrowRound}
minimumPixelSize={128}
color={cycloneColor}
colorBlendMode={Cesium.ColorBlendMode.REPLACE}
colorBlendMode={ColorBlendMode.REPLACE}
/>
</Entity>
<Entity
id={"Anticyclone-2"}
position={
new Cesium.CallbackProperty(function (time, result) {
new CallbackProperty(function (time, result) {
const passTime = getPassTime();
if (passTime < 30) return;
return Cesium.Cartesian3.fromDegrees(29, 49, 1000000);
if (passTime < 15) return;
return Cartesian3.fromDegrees(29, 49, 1000000);
}, true)
}
>
@ -103,16 +121,16 @@ function Cyclone() {
uri={arrowRound}
minimumPixelSize={128}
color={anticycloneColor}
colorBlendMode={Cesium.ColorBlendMode.REPLACE}
colorBlendMode={ColorBlendMode.REPLACE}
/>
</Entity>
<Entity
id={"Cyclone-2"}
position={
new Cesium.CallbackProperty(function (time, result) {
new CallbackProperty(function (time, result) {
const passTime = getPassTime();
if (passTime < 40) return;
return Cesium.Cartesian3.fromDegrees(62, 45, 1000000);
if (passTime < 20) return;
return Cartesian3.fromDegrees(62, 45, 1000000);
}, true)
}
>
@ -120,16 +138,16 @@ function Cyclone() {
uri={arrowRound}
minimumPixelSize={128}
color={cycloneColor}
colorBlendMode={Cesium.ColorBlendMode.REPLACE}
colorBlendMode={ColorBlendMode.REPLACE}
/>
</Entity>
<Entity
id={"Anticyclone-3"}
position={
new Cesium.CallbackProperty(function (time, result) {
new CallbackProperty(function (time, result) {
const passTime = getPassTime();
if (passTime < 50) return;
return Cesium.Cartesian3.fromDegrees(98, 48, 1000000);
if (passTime < 25) return;
return Cartesian3.fromDegrees(98, 48, 1000000);
}, true)
}
>
@ -137,16 +155,16 @@ function Cyclone() {
uri={arrowRound}
minimumPixelSize={128}
color={anticycloneColor}
colorBlendMode={Cesium.ColorBlendMode.REPLACE}
colorBlendMode={ColorBlendMode.REPLACE}
/>
</Entity>
<Entity
id={"Anticyclone-4"}
position={
new Cesium.CallbackProperty(function (time, result) {
new CallbackProperty(function (time, result) {
const passTime = getPassTime();
if (passTime < 50) return;
return Cesium.Cartesian3.fromDegrees(98, 48, 0);
if (passTime < 25) return;
return Cartesian3.fromDegrees(98, 48, 0);
}, true)
}
>
@ -154,7 +172,7 @@ function Cyclone() {
uri={arrowRound}
minimumPixelSize={128}
color={anticycloneColor}
colorBlendMode={Cesium.ColorBlendMode.REPLACE}
colorBlendMode={ColorBlendMode.REPLACE}
/>
</Entity>
</Fragment>

View File

@ -0,0 +1,55 @@
import { Fragment, useMemo } from "react";
import { ImageryLayer } from "resium";
import { WebMapServiceImageryProvider } from "cesium";
const url = "http://analysis.tpdc.ac.cn/gs/geoserver/phitrellis/wms";
const name1 = "phitrellis:4_1_sst_JJA_G_dif";
const name2 = "phitrellis:4_1_t2m_JJA_TP_dif ";
function JJAImageLayer() {
const tempProvider1 = useMemo(
() =>
new WebMapServiceImageryProvider({
url: url,
layers: name1,
parameters: {
service: "WMS",
format: "image/png",
transparent: true,
},
}),
[name1, url]
);
const tempProvider2 = useMemo(
() =>
new WebMapServiceImageryProvider({
url: url,
layers: name2,
parameters: {
service: "WMS",
format: "image/png",
transparent: true,
},
}),
[name2, url]
);
return (
<Fragment>
<ImageryLayer
key={`ImageryLayer-sst`}
imageryProvider={tempProvider1}
show={true}
/>
<ImageryLayer
key={`ImageryLayer-t2m`}
imageryProvider={tempProvider2}
show={true}
/>
</Fragment>
);
}
export default JJAImageLayer;

View File

@ -0,0 +1,63 @@
import { Entity, LabelGraphics, useCesium } from "resium";
import { Cartesian2, Cartesian3, Color, LabelStyle } from "cesium";
import { Fragment, useCallback, useState } from "react";
import { useInterval } from "ahooks";
function Labels() {
const { viewer } = useCesium();
const [show, setShow] = useState(false);
const showAnimate = useCallback(() => {
const { currentTime, stopTime } = viewer.clock;
const leftTime = Math.floor(
stopTime.secondsOfDay - currentTime.secondsOfDay
);
if (leftTime < 5) {
setShow(true);
} else if (show) setShow(false);
}, [show]);
useInterval(showAnimate, 100);
return (
<Fragment>
<Entity position={Cartesian3.fromDegrees(90, 27, 0)}>
<LabelGraphics
text={"青藏高原温度异常"}
font="24px Helvetica"
fillColor={Color.SKYBLUE}
outlineColor={Color.BLACK}
outlineWidth={2}
style={LabelStyle.FILL_AND_OUTLINE}
eyeOffset={new Cartesian2(0, 200000)}
/>
</Entity>
<Entity position={Cartesian3.fromDegrees(-55, 45, 0)}>
<LabelGraphics
text={"拉布拉多海温度异常"}
font="24px Helvetica"
fillColor={Color.SKYBLUE}
outlineColor={Color.BLACK}
outlineWidth={2}
style={LabelStyle.FILL_AND_OUTLINE}
eyeOffset={new Cartesian2(0, 200000)}
/>
</Entity>
<Entity show={show} position={Cartesian3.fromDegrees(98, 48, 0)}>
<LabelGraphics
text={"barotropic"}
font="24px Helvetica"
fillColor={Color.SKYBLUE}
outlineColor={Color.BLACK}
outlineWidth={2}
style={LabelStyle.FILL_AND_OUTLINE}
eyeOffset={new Cartesian2(0, 1200000)}
/>
</Entity>
</Fragment>
);
}
export default Labels;

View File

@ -1,67 +0,0 @@
import { useEffect, useMemo, useState } from "react";
import { ImageryLayer, useCesium } from "resium";
import { WebMapServiceImageryProvider } from "cesium";
import { useInterval } from "ahooks";
import { useDispatch, 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 LabradorImageLayer() {
const dispatch = useDispatch();
const { viewer } = useCesium();
const { imageLayer } = useSelector((state) => state.data);
const [delay, setDelay] = useState(undefined);
const [index, setIndex] = useState(0);
useEffect(() => {
const { labrador } = imageLayer;
if (!!labrador) {
setDelay((60 / nameList.length) * 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) {
setIndex(0);
setDelay((60 / nameList.length) * 1000);
}
}, delay);
if (!viewer.clock?.shouldAnimate) return;
return layers[index];
}
export default LabradorImageLayer;

View File

@ -36,7 +36,7 @@ const colorBar = [
function Legend() {
return (
<div className="legend">
<div className="legend-title">拉布拉多海海表温度夏季年代际异常值</div>
<div className="legend-title">夏季温度年代际异常值</div>
<div className="colorbar">
{colorBar.map((color, index) => {
return (

View File

@ -1,6 +1,7 @@
import { useCallback, useState } from "react";
import { Entity, PolygonGraphics, useCesium } from "resium";
import { useInterval } from "ahooks";
import { Cartesian3, Color } from "cesium";
function PlateauPolygon() {
const { viewer } = useCesium();
@ -22,11 +23,11 @@ function PlateauPolygon() {
return (
<Entity id="plateau" show={show}>
<PolygonGraphics
hierarchy={Cesium.Cartesian3.fromDegreesArray([
hierarchy={Cartesian3.fromDegreesArray([
// 85, 30, 91, 30, 91, 35, 85, 35,
80, 31, 84, 29.5, 87.4, 28, 91, 28, 98, 29, 94, 35, 79, 34.4, 80, 31,
])}
material={new Cesium.Color(1, 0, 0, 0.1)}
material={new Color(1, 0, 0, 0.1)}
/>
</Entity>
);

View File

@ -1,14 +1,25 @@
import { useState } from "react";
import { Entity, PointGraphics, useCesium } from "resium";
import { useInterval } from "ahooks";
import {
Cartesian3,
Color,
JulianDate,
LagrangePolynomialApproximation,
PolylineDashMaterialProperty,
SampledPositionProperty,
TimeInterval,
TimeIntervalCollection,
VelocityOrientationProperty,
} from "cesium";
//
const dataLabToQTP = [
{ 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 },
{ longitude: -62, latitude: 69, height: 1000000, time: 5 },
{ longitude: -20, latitude: 55, height: 1000000, time: 10 },
{ longitude: 29, latitude: 49, height: 1000000, time: 15 },
{ longitude: 62, latitude: 45, height: 1000000, time: 20 },
{ longitude: 98, latitude: 48, height: 1000000, time: 25 },
];
function Point() {
@ -18,9 +29,9 @@ function Point() {
const start = viewer.clock.startTime;
const stop = viewer.clock.stopTime;
const pathMaterial = new Cesium.PolylineDashMaterialProperty({
const pathMaterial = new PolylineDashMaterialProperty({
dashLength: 20,
color: new Cesium.Color(4 / 255, 251 / 255, 253 / 255),
color: new Color(4 / 255, 251 / 255, 253 / 255),
});
useInterval(() => {
@ -33,14 +44,10 @@ function Point() {
* {SampledPositionProperty|*}
*/
function createProperty(source) {
let property = new Cesium.SampledPositionProperty();
let property = new SampledPositionProperty();
for (let i = 0; i < source.length; i++) {
let time = Cesium.JulianDate.addSeconds(
start,
source[i].time,
new Cesium.JulianDate()
);
let position = Cesium.Cartesian3.fromDegrees(
let time = JulianDate.addSeconds(start, source[i].time, new JulianDate());
let position = Cartesian3.fromDegrees(
source[i].longitude,
source[i].latitude,
source[i].height
@ -50,7 +57,7 @@ function Point() {
}
property.setInterpolationOptions({
interpolationDegree: 10,
interpolationAlgorithm: Cesium.LagrangePolynomialApproximation,
interpolationAlgorithm: LagrangePolynomialApproximation,
});
return property;
@ -63,14 +70,14 @@ function Point() {
id={"point"}
position={property}
availability={
new Cesium.TimeIntervalCollection([
new Cesium.TimeInterval({
new TimeIntervalCollection([
new TimeInterval({
start: start,
stop: stop,
}),
])
}
orientation={new Cesium.VelocityOrientationProperty(property)}
orientation={new VelocityOrientationProperty(property)}
path={{
resolution: 1,
material: pathMaterial,
@ -82,9 +89,9 @@ function Point() {
>
<PointGraphics
show={true}
color={Cesium.Color.SKYBLUE}
color={Color.SKYBLUE}
pixelSize={10}
outlineColor={Cesium.Color.YELLOW}
outlineColor={Color.YELLOW}
outlineWidth={3}
/>
</Entity>

View File

@ -1,65 +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_tp_" + item);
function TibetImageLayer() {
const { viewer } = useCesium();
const { imageLayer } = useSelector((state) => state.data);
const [delay, setDelay] = useState(undefined);
const [index, setIndex] = useState(0);
useEffect(() => {
const { labrador } = imageLayer;
if (!!labrador) {
setDelay((60 / nameList.length) * 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) {
setIndex(0);
setDelay((60 / nameList.length) * 1000);
}
}, delay);
if (!viewer.clock?.shouldAnimate) return;
return layers[index];
}
export default TibetImageLayer;

View File

@ -1,5 +1,11 @@
import { Entity, PolylineGraphics, useCesium } from "resium";
import { Cartesian3 } from "cesium";
import {
CallbackProperty,
Cartesian3,
Color,
Ellipsoid,
PolylineArrowMaterialProperty,
} from "cesium";
import { min } from "lodash-es";
function Updraft() {
@ -12,19 +18,19 @@ function Updraft() {
>
<PolylineGraphics
positions={
new Cesium.CallbackProperty(function (time, result) {
new CallbackProperty(function (time, result) {
const { currentTime, startTime } = viewer.clock;
const passTime = currentTime.secondsOfDay - startTime.secondsOfDay;
const height = 100000 * passTime;
return Cesium.Cartesian3.fromDegreesArrayHeights(
const height = 200000 * passTime;
return Cartesian3.fromDegreesArrayHeights(
[-62, 69, 0, -62, 69, min([height, 1000000])],
Cesium.Ellipsoid.WGS84,
Ellipsoid.WGS84,
result
);
}, false)
}
width={30}
material={new Cesium.PolylineArrowMaterialProperty(Cesium.Color.RED)}
material={new PolylineArrowMaterialProperty(Color.RED)}
/>
</Entity>
);

View File

@ -1,3 +1,9 @@
import {
Cartesian3,
Color,
PolylineArrowMaterialProperty,
PolylineDashMaterialProperty,
} from "cesium";
import { Fragment } from "react";
import { Entity, PolylineGraphics } from "resium";
@ -6,13 +12,13 @@ function WaterVaporPath() {
<Fragment>
<Entity>
<PolylineGraphics
positions={Cesium.Cartesian3.fromDegreesArrayHeights([
positions={Cartesian3.fromDegreesArrayHeights([
103, 46, 0, 93, 38, 0,
])}
width={10}
material={
new Cesium.PolylineDashMaterialProperty({
color: Cesium.Color.fromCssColorString("#406ec5"),
new PolylineDashMaterialProperty({
color: Color.fromCssColorString("#406ec5"),
dashLength: 20,
})
}
@ -20,13 +26,13 @@ function WaterVaporPath() {
</Entity>
<Entity>
<PolylineGraphics
positions={Cesium.Cartesian3.fromDegreesArrayHeights([
positions={Cartesian3.fromDegreesArrayHeights([
93, 38, 0, 93, 37.9, 0,
])}
width={30}
material={
new Cesium.PolylineArrowMaterialProperty(
Cesium.Color.fromCssColorString("#406ec5")
new PolylineArrowMaterialProperty(
Color.fromCssColorString("#406ec5")
)
}
/>

View File

@ -10,10 +10,10 @@ import Barotropic from "./Barotorpic";
import WavePoints from "./WavePoints";
import Updraft from "./Updraft";
import Legend from "./Legend";
import LabradorImageLayer from "./LabradorImageLayer";
import TibetImageLayer from "./TibetImageLayer";
import JJAImageLayer from "./JJAImageLayer";
import ChartPanel from "./ChartPanel";
import EntityLegend from "./EntityLegend";
import Labels from "./Labels";
// import WaterVaporPath from "./WaterVaporPath";
export default function DomainOne() {
@ -39,9 +39,9 @@ export default function DomainOne() {
{/* <WaterVaporPath /> */}
<Updraft />
<EntityLegend />
<Labels />
<Legend />
<LabradorImageLayer />
<TibetImageLayer />
<JJAImageLayer />
</MapLayout>
);
}

View File

@ -83,7 +83,7 @@ function ChartPanel() {
data: ["南极", "北极", "青藏高原"],
textStyle: { color: "#04fbfd", cursor: "point" },
},
animationDuration: years.length * 500,
animationDuration: 10 * 1000,
animationEasing: "cubicInOut",
grid: {
left: "3%",

View File

@ -21,9 +21,9 @@ const colorBar = [
"#FFB1B1",
];
function Legend() {
function Legend({ style }) {
return (
<div className={styles.legend}>
<div className={styles.legend} style={{ ...style }}>
<div className="legend-title"></div>
<div className="colorbar">
{colorBar.map((color, index) => {

View File

@ -0,0 +1,31 @@
import { CameraFlyTo } from "resium";
import { Cartesian3 } from "cesium";
import MapLayout from "@/components/map/Layout";
import CustomToolbar from "@/components/common/CustomToolbar";
import CustomClock from "@/components/common/CustomClock";
import LandImageLayer from "../LandImageLayer";
import WavePoint from "@/components/common/WavePoint";
function ViewerOne({ children }) {
return (
<div className="domain-viewer">
<MapLayout>
<div className="title" style={{ zIndex: 999 }}>
两极协同连接南极和北极的热带大西洋经向模的媒介作用
</div>
<CustomToolbar />
<CustomClock />
<CameraFlyTo
duration={5}
destination={Cartesian3.fromDegrees(80, -85, 10000000)}
/>
<WavePoint stationLon={88} stationLat={-85} labelText={"南极"} />
<LandImageLayer />
{children}
</MapLayout>
</div>
);
}
export default ViewerOne;

View File

@ -0,0 +1,21 @@
import { CameraFlyTo } from "resium";
import { Cartesian3 } from "cesium";
import MapLayout from "@/components/map/Layout";
import WavePoint from "@/components/common/WavePoint";
function ViewerThree({ children }) {
return (
<div className="domain-viewer">
<MapLayout>
<CameraFlyTo
duration={5}
destination={Cartesian3.fromDegrees(100, 33, 10000000)}
/>
<WavePoint stationLon={88} stationLat={33} labelText={"青藏高原"} />
{children}
</MapLayout>
</div>
);
}
export default ViewerThree;

View File

@ -0,0 +1,28 @@
import { CameraFlyTo } from "resium";
import { Cartesian3 } from "cesium";
import MapLayout from "@/components/map/Layout";
import OceanImageLayer from "../OceanImageLayer";
import WavePoint from "@/components/common/WavePoint";
function ViewerTwo({ children }) {
return (
<div className="domain-viewer">
<MapLayout>
<OceanImageLayer />
<CameraFlyTo
duration={5}
destination={Cartesian3.fromDegrees(0, 88, 10000000)}
/>
<WavePoint
color={"WHITE"}
stationLon={0}
stationLat={87}
labelText={"北极"}
/>
{children}
</MapLayout>
</div>
);
}
export default ViewerTwo;

View File

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

View File

@ -0,0 +1,21 @@
.sceneOne :global {
display: flex;
width: 100%;
height: 100%;
background-color: gray;
.domain-viewers {
width: 100%;
height: calc(100% - 250px);
display: flex;
.domain-viewer {
flex: 1;
position: relative;
.title {
pointer-events: none;
}
}
}
}

View File

@ -0,0 +1,15 @@
import { Button } from "antd";
function SceneSwitch({ scene, setScene }) {
const switchHandler = () => {
setScene(scene === 1 ? 2 : 1);
};
return (
<Button className="scene-switch" type="primary" onClick={switchHandler}>
切换到场景{scene === 1 ? "二" : "一"}
</Button>
);
}
export default SceneSwitch;

View File

@ -0,0 +1,110 @@
import ReactECharts from "echarts-for-react";
const years = [];
for (let year = 1950; year <= 2020; year++) {
years.push(year);
}
const SATData = [];
const SSTData = [];
function SceneChartPanel() {
const option = {
title: {
// text: "Stacked Line",
},
tooltip: {
trigger: "axis",
},
legend: {
data: ["SAT_SVD1", "SST_SVD1"],
textStyle: { color: "#04fbfd", cursor: "point" },
},
animationDuration: 10 * 1000,
animationEasing: "cubicInOut",
grid: {
left: "3%",
right: "4%",
bottom: "3%",
containLabel: true,
},
xAxis: {
type: "category",
boundaryGap: false,
data: years,
axisLine: {
onZero: false,
symbol: ["none", "arrow"],
symbolOffser: [0, 10],
lineStyle: {
color: "#04fbfd",
},
},
},
yAxis: {
type: "value",
min: -3.0,
max: 2.0,
interval: 1.0,
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: "SAT_SVD1",
type: "line",
stack: "Total",
data: SATData,
smooth: true,
color: "red",
symbol: "none",
itemStyle: {
color: "red",
},
},
{
name: "SST_SVD1",
type: "line",
// stack: "Total",
data: SSTData,
smooth: true,
color: "blue",
symbol: "none",
itemStyle: {
color: "blue",
},
},
],
};
return (
<div className="scene-chart-panel chart-info-panel">
<ReactECharts
option={option}
lazyUpdate={true}
style={{
height: "100%",
width: "100%",
}}
/>
</div>
);
}
export default SceneChartPanel;

View File

@ -0,0 +1,20 @@
import Legend from "../Legend";
import ViewerOne from "../SceneOne/ViewerOne";
import SceneChartPanel from "./SceneChartPanel";
function SceneTwo() {
return (
<div className="scene_two">
<ViewerOne>
<div className="right-panel one" style={{ height: "unset" }}>
<div className="bottom-panel">
<SceneChartPanel />
</div>
</div>
<Legend style={{ bottom: "40px" }} />
</ViewerOne>
</div>
);
}
export default SceneTwo;

View File

@ -1,64 +1,16 @@
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 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";
import Legend from "./Legend";
import { useState } from "react";
import styles from "./index.module.less";
import SceneOne from "./SceneOne";
import SceneTwo from "./SceneTwo";
import SceneSwitch from "./SceneSwitch";
export default function DomainThree() {
const [scene, setScene] = useState(1);
export default function DomainOne() {
return (
<div
style={{
display: "flex",
height: "100%",
}}
>
<div style={{ flex: 1, position: "relative" }}>
<MapLayout>
<div className="title" style={{ zIndex: 999 }}>
两极协同连接南极和北极的热带大西洋经向模的媒介作用
</div>
<CustomToolbar />
<CustomClock />
<CameraFlyTo
duration={5}
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 />
</MapLayout>
</div>
<div style={{ flex: 1, position: "relative" }}>
<MapLayout>
<OceanImageLayer />
<CameraFlyTo
duration={5}
destination={Cartesian3.fromDegrees(0, 88, 10000000)}
/>
<WavePoint color={"WHITE"} stationLon={0} stationLat={87} />
</MapLayout>
</div>
<div style={{ flex: 1, position: "relative" }}>
<MapLayout>
<CameraFlyTo
duration={5}
destination={Cartesian3.fromDegrees(100, 33, 10000000)}
/>
<div className="right-panel one">
<ChartPanel />
</div>
<WavePoint maxR={3600 * 200} stationLon={88} stationLat={33} />
</MapLayout>
</div>
{/* <Legend /> */}
<div className={styles.domainThree}>
{scene === 1 ? <SceneOne /> : <SceneTwo />}
<SceneSwitch scene={scene} setScene={setScene} />
</div>
);
}

View File

@ -1,6 +1,148 @@
.domainThree :global {
height: 100%;
.scene_two {
display: flex;
height: 100%;
.domain-viewer {
flex: 1;
position: relative;
.title {
pointer-events: none;
}
.chart-info-panel {
width: 100%;
height: 100%;
pointer-events: auto;
padding: 12px;
border: 1px solid #04fbfd;
color: #02f9ff !important;
background-color: #1f485690;
transition: all 0.3s ease-in-out;
}
}
}
.scene-switch {
position: absolute;
left: 8px;
top: 8px;
}
.left-panel {
position: absolute;
left: 12px;
bottom: 12px;
width: 450px;
display: flex;
flex-direction: column;
height: 230px;
.top-panel {
flex: 1;
}
.bottom-panel {
flex: 1;
}
.text-info-panel {
width: 100%;
height: 100%;
padding: 12px;
border: 1px solid #04fbfd;
color: #02f9ff !important;
background-color: #1f485690;
pointer-events: auto;
font-size: 18px;
text-indent: 2em;
line-height: 1.5;
z-index: 999;
}
}
.right-panel {
position: absolute;
right: 12px;
bottom: 12px;
width: 450px;
height: 230px;
display: flex;
flex-direction: column;
gap: 12px;
.top-panel {
flex: 1;
}
.bottom-panel {
flex: 1;
}
.form-panel {
width: 100%;
height: 100%;
pointer-events: auto;
padding: 12px;
border: 1px solid #04fbfd;
color: #02f9ff !important;
background-color: #1f485690;
.ant-form-item-label {
label {
color: white;
}
}
.ant-form-item-control {
.ant-select-multiple {
max-height: 300px;
overflow-y: scroll;
&::-webkit-scrollbar {
width: 6px;
height: 1px;
}
&::-webkit-scrollbar-thumb {
border-radius: 6px;
background: #00a2a2;
}
&::-webkit-scrollbar-track {
border-radius: 6px;
background: none;
}
}
}
}
.chart-info-panel {
width: 100%;
height: 100%;
pointer-events: auto;
padding: 12px;
border: 1px solid #04fbfd;
color: #02f9ff !important;
background-color: #1f485690;
transition: all 0.3s ease-in-out;
}
.highlight {
outline: 4px solid #04fbfd;
box-shadow: 0 0 30px #04e0fd;
}
}
.one {
top: 50%;
}
}
.legend :global {
position: absolute;
bottom: 40px;
bottom: 270px;
width: 50%;
left: 25%;
z-index: 1000;
@ -26,7 +168,6 @@
.colorbar-item {
flex: 1;
height: 100%;
// border-radius: 8px;
border: 1px black solid;
&:not(:nth-child(1)) {

View File

@ -1,27 +1,46 @@
import { Fragment, useState } from "react";
import { Entity, EllipsoidGraphics, useCesium, PolylineGraphics } from "resium";
import {
Entity,
EllipsoidGraphics,
useCesium,
PolylineGraphics,
LabelGraphics,
} from "resium";
import { useInterval, useThrottleFn } from "ahooks";
import { useDispatch } from "react-redux";
import {
Cartesian3,
Color,
Math as CesiumMath,
LabelStyle,
PolylineDashMaterialProperty,
Cartesian2,
} from "cesium";
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)}
radii={new Cartesian3(460000.0, 460000.0, 460000.0)}
innerRadii={new Cartesian3(415000.0, 415000.0, 415000.0)}
minimumCone={CesiumMath.toRadians(89.8)}
maximumCone={CesiumMath.toRadians(90.2)}
material={new 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)}
radii={new Cartesian3(460000.0, 460000.0, 460000.0)}
innerRadii={new Cartesian3(415000.0, 415000.0, 415000.0)}
minimumCone={CesiumMath.toRadians(89.8)}
maximumCone={CesiumMath.toRadians(90.2)}
material={new Color(0.99, 0.23, 0.23, 1)}
/>
);
// const labelText={
// '5','Rossby' , '', '680'
// }
function Circles() {
const { viewer } = useCesium();
const dispatch = useDispatch();
@ -56,7 +75,7 @@ function Circles() {
currentTime.secondsOfDay - startTime.secondsOfDay
);
if (!shouldAnimate) return;
if (_time === 40) {
if (_time === 20) {
showInfo();
} else if (_time === 0) {
closeInfo();
@ -75,14 +94,12 @@ function Circles() {
<Fragment>
<Entity
id={`${id}-up`}
position={Cesium.Cartesian3.fromDegrees(lon, lat, 1000000)}
position={Cartesian3.fromDegrees(lon, lat, 1000000)}
>
{circle}
</Entity>
{show && (
<Entity id={`${id}-connection-line`}>
<PolylineGraphics
positions={Cesium.Cartesian3.fromDegreesArrayHeights([
positions={Cartesian3.fromDegreesArrayHeights([
lon,
lat,
1000000,
@ -92,20 +109,31 @@ function Circles() {
])}
width={2}
material={
new Cesium.PolylineDashMaterialProperty({
color: Cesium.Color.WHITE,
new PolylineDashMaterialProperty({
color: Color.WHITE,
dashLength: 4,
})
}
/>
</Entity>
)}
</Entity>
<Entity
id={`${id}-down`}
position={Cesium.Cartesian3.fromDegrees(lon, lat, 0)}
position={Cartesian3.fromDegrees(lon, lat, 0)}
>
{circle}
</Entity>
<Entity position={Cartesian3.fromDegrees(lon, lat, 0)}>
<LabelGraphics
text={"Rossby波列"}
font="24px Helvetica"
fillColor={Color.SKYBLUE}
outlineColor={Color.BLACK}
outlineWidth={2}
style={LabelStyle.FILL_AND_OUTLINE}
eyeOffset={new Cartesian2(0, 10000000)}
/>
</Entity>
</Fragment>
);
};
@ -115,35 +143,35 @@ function Circles() {
<Circle
isLow={true}
id={`low-circle-1`}
showTime={10}
showTime={5}
lon={-110}
lat={-60}
/>
<Circle
isLow={false}
id={`height-circle-1`}
showTime={20}
showTime={10}
lon={-30}
lat={-55}
/>
<Circle
isLow={true}
id={`low-circle-2`}
showTime={30}
showTime={15}
lon={30}
lat={-40}
/>
<Circle
isLow={false}
id={`height-circle-2`}
showTime={40}
showTime={20}
lon={65}
lat={-35}
/>
<Circle
isLow={true}
id={`low-circle-3`}
showTime={50}
showTime={27}
lon={95}
lat={-30}
/>

View File

@ -1,30 +1,31 @@
import { useCesium } from "resium";
import { Cartesian2, Cartesian3, Math } from "cesium";
import { CloudCollection, useCesium } from "resium";
function Cloud() {
const { viewer } = useCesium();
const scene = viewer.scene;
const position = Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706, 50);
const position = Cartesian3.fromDegrees(-123.0744619, 44.0503706, 50);
Cesium.Math.setRandomNumberSeed(2.5);
Math.setRandomNumberSeed(2.5);
function getRandomNumberInRange(minValue, maxValue) {
return minValue + Cesium.Math.nextRandomNumber() * (maxValue - minValue);
return minValue + Math.nextRandomNumber() * (maxValue - minValue);
}
const clouds = new Cesium.CloudCollection();
const clouds = new 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),
position: new Cartesian3.fromDegrees(-122.6908, 45.496, 300),
scale: new Cartesian2(1500, 250),
maximumSize: new 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),
position: new Cartesian3.fromDegrees(-122.72, 45.5, 335),
scale: new Cartesian2(1500, 300),
maximumSize: new Cartesian3(50, 12, 15),
slice: 0.36,
});
}
@ -53,9 +54,9 @@ function Cloud() {
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(
position: Cartesian3.fromDegrees(long, lat, height),
scale: new Cartesian2(scaleX, scaleY),
maximumSize: new Cartesian3(
aspectRatio * cloudHeight,
cloudHeight,
depth
@ -65,19 +66,19 @@ function Cloud() {
}
}
// manually position clouds in front
const scratch = new Cesium.Cartesian3();
const scratch = new 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),
position: Cartesian3.fromDegrees(-122.666, 45.5126, 97),
scale: new Cartesian2(400, 150),
maximumSize: new 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),
position: Cartesian3.fromDegrees(-122.6665, 45.5262, 76),
scale: new Cartesian2(450, 200),
maximumSize: new Cartesian3(25, 14, 12),
slice: 0.3,
});
}

View File

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

View File

@ -0,0 +1,34 @@
import { Cartesian2, Cartesian3, Color, LabelStyle } from "cesium";
import { Fragment } from "react";
import { Entity, LabelGraphics } from "resium";
function Labels() {
return (
<Fragment>
<Entity position={Cartesian3.fromDegrees(73, -7, 0)}>
<LabelGraphics
text={"印度洋海温异常"}
font="24px Helvetica"
fillColor={Color.SKYBLUE}
outlineColor={Color.BLACK}
outlineWidth={2}
style={LabelStyle.FILL_AND_OUTLINE}
eyeOffset={new Cartesian2(0, 200000)}
/>
</Entity>
<Entity position={Cartesian3.fromDegrees(94, 24, 0)}>
<LabelGraphics
text={"6月青藏高原降水和加热"}
font="24px Helvetica"
fillColor={Color.SKYBLUE}
outlineColor={Color.BLACK}
outlineWidth={2}
style={LabelStyle.FILL_AND_OUTLINE}
eyeOffset={new Cartesian2(0, 0)}
/>
</Entity>
</Fragment>
);
}
export default Labels;

View File

@ -1,4 +1,5 @@
import { useInterval } from "ahooks";
import { Cartesian3, Color, PolylineArrowMaterialProperty } from "cesium";
import { Fragment, useCallback, useState } from "react";
import { Entity, PolylineGraphics, useCesium } from "resium";
@ -23,26 +24,24 @@ function MoistureTransport() {
<Fragment>
<Entity show={show}>
<PolylineGraphics
positions={Cesium.Cartesian3.fromDegreesArrayHeights([
positions={Cartesian3.fromDegreesArrayHeights([
85, 33, 200000, 87, 32, 0,
])}
width={15}
material={
new Cesium.PolylineArrowMaterialProperty(
Cesium.Color.fromCssColorString("#70ad47")
new PolylineArrowMaterialProperty(
Color.fromCssColorString("#70ad47")
)
}
/>
</Entity>
<Entity show={show}>
<PolylineGraphics
positions={Cesium.Cartesian3.fromDegreesArrayHeights([
83, 27, 0, 87, 31, 0,
])}
positions={Cartesian3.fromDegreesArrayHeights([83, 27, 0, 87, 31, 0])}
width={15}
material={
new Cesium.PolylineArrowMaterialProperty(
Cesium.Color.fromCssColorString("#70ad47")
new PolylineArrowMaterialProperty(
Color.fromCssColorString("#70ad47")
)
}
/>

View File

@ -1,6 +1,7 @@
import { useCallback, useState } from "react";
import { Entity, PolygonGraphics, useCesium } from "resium";
import { useInterval } from "ahooks";
import { Cartesian3, Color } from "cesium";
function PlateauPolygon() {
const { viewer } = useCesium();
@ -12,7 +13,7 @@ function PlateauPolygon() {
stopTime.secondsOfDay - currentTime.secondsOfDay
);
if (leftTime < 20) {
if (leftTime < 5) {
setShow(true);
} else if (show) setShow(false);
}, [show]);
@ -22,11 +23,11 @@ function PlateauPolygon() {
return (
<Entity id="plateau" show={show}>
<PolygonGraphics
hierarchy={Cesium.Cartesian3.fromDegreesArray([
hierarchy={Cartesian3.fromDegreesArray([
// 85, 30, 91, 30, 91, 35, 85, 35,
80, 31, 84, 29.5, 87.4, 28, 91, 28, 98, 29, 94, 35, 79, 34.4, 80, 31,
])}
material={new Cesium.Color(1, 0, 0, 0.1)}
material={new Color(1, 0, 0, 0.1)}
/>
</Entity>
);

View File

@ -1,16 +1,26 @@
import { useState } from "react";
import { Entity, PointGraphics, useCesium } from "resium";
import { useInterval } from "ahooks";
import {
Cartesian3,
Color,
JulianDate,
LinearApproximation,
SampledPositionProperty,
TimeInterval,
TimeIntervalCollection,
VelocityOrientationProperty,
} from "cesium";
//
const dataAntarcticaToQTP = [
{ longitude: -110, latitude: -60, height: 0, time: 0 },
{ longitude: -110, latitude: -60, height: 1000000, time: 10 },
{ longitude: -30, latitude: -55, height: 1000000, time: 20 },
{ longitude: 30, latitude: -40, height: 1000000, time: 30 },
{ longitude: 65, latitude: -35, height: 1000000, time: 40 },
{ longitude: 95, latitude: -30, height: 1000000, time: 50 },
{ longitude: 95, latitude: -30, height: 0, time: 60 },
{ longitude: -110, latitude: -60, height: 1000000, time: 5 },
{ longitude: -30, latitude: -55, height: 1000000, time: 10 },
{ longitude: 30, latitude: -40, height: 1000000, time: 15 },
{ longitude: 65, latitude: -35, height: 1000000, time: 20 },
{ longitude: 95, latitude: -30, height: 1000000, time: 27 },
{ longitude: 95, latitude: -30, height: 0, time: 30 },
];
function Point() {
@ -21,7 +31,7 @@ function Point() {
const start = viewer.clock.startTime;
const stop = viewer.clock.stopTime;
const pathMaterial = new Cesium.Color(4 / 255, 251 / 255, 253 / 255);
const pathMaterial = new Color(4 / 255, 251 / 255, 253 / 255);
useInterval(() => {
if (viewer.clock?.shouldAnimate) setDelay(undefined);
@ -33,14 +43,10 @@ function Point() {
* {SampledPositionProperty|*}
*/
function createProperty(source) {
let property = new Cesium.SampledPositionProperty();
let property = new SampledPositionProperty();
for (let i = 0; i < source.length; i++) {
let time = Cesium.JulianDate.addSeconds(
start,
source[i].time,
new Cesium.JulianDate()
);
let position = Cesium.Cartesian3.fromDegrees(
let time = JulianDate.addSeconds(start, source[i].time, new JulianDate());
let position = Cartesian3.fromDegrees(
source[i].longitude,
source[i].latitude,
source[i].height
@ -50,7 +56,7 @@ function Point() {
}
property.setInterpolationOptions({
interpolationDegree: 1,
interpolationAlgorithm: Cesium.LinearApproximation,
interpolationAlgorithm: LinearApproximation,
});
return property;
}
@ -62,14 +68,14 @@ function Point() {
id={"point"}
position={property}
availability={
new Cesium.TimeIntervalCollection([
new Cesium.TimeInterval({
new TimeIntervalCollection([
new TimeInterval({
start,
stop,
}),
])
}
orientation={new Cesium.VelocityOrientationProperty(property)}
orientation={new VelocityOrientationProperty(property)}
path={{
resolution: 1,
material: pathMaterial,
@ -81,8 +87,8 @@ function Point() {
>
<PointGraphics
show={true}
color={Cesium.Color.SKYBLUE}
outlineColor={Cesium.Color.YELLOW}
color={Color.SKYBLUE}
outlineColor={Color.YELLOW}
outlineWidth={3}
/>
</Entity>

View File

@ -1,5 +1,5 @@
import { Entity, EllipseGraphics } from "resium";
import { Cartesian3 } from "cesium";
import { Cartesian3, Color } from "cesium";
const fluxData = [
0.03256254, -0.23182425, -0.040055223, -0.03476936, -0.40450656, -0.18742515,
@ -86,7 +86,7 @@ function HeatFlux({ position, index }) {
`}
>
<EllipseGraphics
material={new Cesium.Color.fromCssColorString(colorBar[dataIndex])}
material={new Color.fromCssColorString(colorBar[dataIndex])}
semiMinorAxis={30000.0}
semiMajorAxis={30000.0}
extrudedHeight={400000}

View File

@ -1,5 +1,5 @@
import { Entity, EllipseGraphics } from "resium";
import { Cartesian3 } from "cesium";
import { Cartesian3, Color } from "cesium";
const rainData = [
0.25309432, 0.296679, 0.39937696, -0.15093477, 0.17799897, 0.1186297,
@ -85,7 +85,7 @@ function Rain({ position, index }) {
`}
>
<EllipseGraphics
material={new Cesium.Color.fromCssColorString(colorBar[dataIndex])}
material={new Color.fromCssColorString(colorBar[dataIndex])}
semiMinorAxis={30000.0}
semiMajorAxis={30000.0}
extrudedHeight={200000}

View File

@ -1,6 +1,7 @@
import { useCallback, useState } from "react";
import { useInterval } from "ahooks";
import { Entity, PolylineGraphics, useCesium } from "resium";
import { Cartesian3, Color, PolylineArrowMaterialProperty } from "cesium";
function SurfaceAnomaly() {
const { viewer } = useCesium();
@ -22,14 +23,10 @@ function SurfaceAnomaly() {
return (
<Entity show={show}>
<PolylineGraphics
positions={Cesium.Cartesian3.fromDegreesArrayHeights([
90, -30, 0, 90, -21, 0,
])}
positions={Cartesian3.fromDegreesArrayHeights([90, -30, 0, 90, -21, 0])}
width={10}
material={
new Cesium.PolylineArrowMaterialProperty(
Cesium.Color.fromCssColorString("#000")
)
new PolylineArrowMaterialProperty(Color.fromCssColorString("#000"))
}
/>
</Entity>

View File

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

View File

@ -0,0 +1,16 @@
import { useCesium } from "resium";
function HeadingPitchRoll() {
const { camera } = useCesium();
camera.moveEnd?.addEventListener(function (event) {
const heading = camera.heading;
const pitch = camera.pitch;
const roll = camera.roll;
console.log("heading,pitch,roll :>> ", heading, pitch, roll);
});
return <></>;
}
export default HeadingPitchRoll;

View File

@ -1,3 +1,9 @@
import {
Cartographic,
Math,
ScreenSpaceEventHandler,
ScreenSpaceEventType,
} from "cesium";
import { useState } from "react";
import { useCesium } from "resium";
@ -7,26 +13,22 @@ function Picker() {
const [lon, setLon] = useState();
const [lat, setLat] = useState();
const handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
const handler = new ScreenSpaceEventHandler(scene.canvas);
handler.setInputAction(function (movement) {
const cartesian = viewer.camera.pickEllipsoid(
movement.endPosition,
scene.globe.ellipsoid
);
if (cartesian) {
const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
const longitudeString = Cesium.Math.toDegrees(
cartographic.longitude
).toFixed(4);
const latitudeString = Cesium.Math.toDegrees(
cartographic.latitude
).toFixed(4);
const cartographic = Cartographic.fromCartesian(cartesian);
const longitudeString = Math.toDegrees(cartographic.longitude).toFixed(4);
const latitudeString = Math.toDegrees(cartographic.latitude).toFixed(4);
setLon(longitudeString);
setLat(latitudeString);
} else {
// entity.label.show = false;
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
}, ScreenSpaceEventType.MOUSE_MOVE);
if (!lon && !lat) return <></>;

View File

@ -1,20 +1,10 @@
import { Entity, PointGraphics, Viewer } from "resium";
import styles from "./index.module.less";
import { useMemo } from "react";
import { BingMapsImageryProvider, BingMapsStyle } from "cesium";
import { Cartesian3, Color } from "cesium";
import Picker from "./Picker";
import HeadingPitchRoll from "./HeadingPitchRoll";
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}`}
@ -30,12 +20,12 @@ function MapLayout({ children, className, ...rest }) {
baseLayerPicker={false}
animation={false}
selectionIndicator={false}
// imageryProvider={imageryProvider}
>
<Entity position={Cesium.Cartesian3.fromDegreesArray([0, 90])}>
<PointGraphics color={Cesium.Color.SKYBLUE} pixelSize={10} />
<Entity position={Cartesian3.fromDegreesArray([0, 90])}>
<PointGraphics color={Color.SKYBLUE} pixelSize={10} />
</Entity>
<Picker />
{/* <HeadingPitchRoll /> */}
{children}
</Viewer>
);

View File

@ -63,7 +63,7 @@
color: #02f9ff !important;
background-color: #1f485690;
pointer-events: auto;
font-size: 16px;
font-size: 18px;
text-indent: 2em;
line-height: 1.5;
z-index: 999;