cesium 绘制台风(附代码)
台风的绘制,首要任务就是台风数据,没有数据,一切白搭,技术难点就台风圈绘制较为复杂,数据格式在文末
let Viewer;
let entityList = [];
let entityListTF = [];
function showTFLJ(viewer, data) {
setIsStop(false);
Viewer = viewer;
let dataMap = {};
let codes = [];
entityList = [];
entityListTF = [];
drawWarningLine(viewer);
data.forEach(function (t) { //将数据按照台风编码 分组
if (dataMap[t.code]) {
dataMap[t.code].push(t);
} else {
dataMap[t.code] = [];
codes.push(t.code);
dataMap[t.code].push(t);
}
})
codes.forEach(function (t) {
let positions_sx = [];//实线
let positions_xx = [];//虚线
let position_sx = [];//实线点
let position_xx = [];//虚线点
let typhoonPoints = dataMap[t].sort(function (a, b) {
return new Date(a.time).getTime() - new Date(b.time).getTime();
});
typhoonPoints.forEach(function (p) {
positions_sx.push(p.longitude * 1);
positions_sx.push(p.latitude * 1);
positions_sx.push(8000);
position_sx.push(p);
})
let programBillboard = new Cesium.Entity({
position: Cesium.Cartesian3.fromDegrees(positions_sx[0] * 1, positions_sx[1] * 1, positions_sx[2] * 1),
label: { //文字标签
text: position_sx[0].code + " " + position_sx[0].name + "【" + position_sx[0].ename + "】",
disableDepthTestDistance: Number.POSITIVE_INFINITY,
font: '400 15px Helvetica', // 15pt monospace
color: new Cesium.Color.fromCssColorString("#FFFFFF"),
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
fillColor: new Cesium.Color.fromCssColorString("#FFFFFF"),
pixelOffset: new Cesium.Cartesian2(100, 0), //偏移量
backgroundColor: new Cesium.Color(0.5, 0.6, 1, 0)
},
})
entityList.push(viewer.entities.add(programBillboard));
//实时数据 实线
entityList.push(viewer.entities.add({
name: "tf_polyline",
polyline: {
positions: Cesium.Cartesian3.fromDegreesArrayHeights(positions_sx),//[113.5,14.4,113.7,14.3,113.9,14.2,114.1,14.1,114.3,14.0,114.5,13.9,114.7,13.9,115.0,13.8,115.2,13.8,115.4,13.7,115.6,13.7,115.8,13.7,116.1,13.6,116.3,13.6,116.5,13.5,116.6,13.4,116.8,13.4,116.9,13.3,117.0,13.3,117.1,13.3,117.2,13.3,117.3,13.3,117.4,13.3,117.6,13.1,117.8,13.0,117.9,13.0,118.1,12.9,118.2,12.8,118.4,12.7]
width: 1,
height: 8000,
material: Cesium.Color.WHEAT.withAlpha(0.5)
},
}));
//预报数据 虚线
let forecasts = [];
if (typhoonPoints[typhoonPoints.length - 1].forecast) {
forecasts = JSON.parse(typhoonPoints[typhoonPoints.length - 1].forecast);//最后一个点,取出预报对象
if (forecasts) forecasts = JSON.parse(typhoonPoints[typhoonPoints.length - 2].forecast);//最后第二个点,取出预报对象
}
for (let i = 0; i < forecasts.length; i++) {
positions_xx = [];
//实线第一个点 作为起点
positions_xx.push(positions_sx[positions_sx.length - 3]);
positions_xx.push(positions_sx[positions_sx.length - 2]);
positions_xx.push(positions_sx[positions_sx.length - 1]);
forecasts[i].points.forEach(function (p) {
positions_xx.push(p.lng * 1);
positions_xx.push(p.lat * 1);
positions_xx.push(8000);
p.sets = forecasts[i].sets;
position_xx.push(p);
})
entityList.push(viewer.entities.add({
name: "tf_polyline",
polyline: {
positions: Cesium.Cartesian3.fromDegreesArrayHeights(positions_xx),
width: 1,
height: 8000,
material: new Cesium.PolylineDashMaterialProperty({
color: Cesium.Color.WHEAT.withAlpha(0.5),
dashLength: 20 //短划线长度
})
},
}));
}
let index=0;
function roamPath(){
if(index==position_sx.length){
viewer.camera.flyTo({
destination: new Cesium.Cartesian3.fromDegrees(position_sx[index-1].longitude*1,position_sx[index-1].latitude*1, 840000),//坐标
});
return;
}
let p = position_sx[index];
const entity = viewer.entities.add({
name: "tf_position",
describe: p.name + "_" + p.time + "_" + p.longitude + "/" + p.latitude + "_"
+ p.speed + " 米/秒," + p.power + "级(" + p.strong + ")" + "_"
+ p.pressure + " 百帕" + "_" + p.move_speed + " 公里/小时," + p.move_dir + "_"
+ p.radius7 + "|" + p.radius10 + "|" + p.radius12 + "_" + p.radius7_quad + "|" + p.radius10_quad + "|" + p.radius12_quad,
position: Cesium.Cartesian3.fromDegrees(p.longitude * 1, p.latitude * 1, 8001),
ellipse: {
semiMinorAxis: new Cesium.CallbackProperty(changeR1, false),//12000.0,
semiMajorAxis: new Cesium.CallbackProperty(changeR1, false),//12000.0,
height: 8001,
material: Cesium.Color.BLUE.withAlpha(0.5)
}
});
entityList.push(entity);
if (p.power * 1 <= 7)//热带低压 TD
entity.ellipse.material = TD;
if (p.power * 1 > 7 && p.power * 1 <= 9)//热带风暴 TS
entity.ellipse.material = TS;
if (p.power * 1 > 9 && p.power * 1 <= 11)//强热带风暴 STS
entity.ellipse.material = STS;
if (p.power * 1 > 11 && p.power * 1 <= 13)//台风 TY
entity.ellipse.material = TY;
if (p.power * 1 > 13 && p.power * 1 <= 15)//强台风 STY
entity.ellipse.material = STY;
if (p.power * 1 > 15)//超强台风 SuperTY
entity.ellipse.material = SuperTY;
if (index == position_sx.length - 1) {// 台风圈
let points = new Array();
if (p.radius7 * 1 > 0) {
points = new Array();
getPoints([p.longitude * 1, p.latitude * 1], JSON.parse(p.radius7_quad).ne * 1, 0);//东北方向
getPoints([p.longitude * 1, p.latitude * 1], JSON.parse(p.radius7_quad).se * 1, 90);
getPoints([p.longitude * 1, p.latitude * 1], JSON.parse(p.radius7_quad).sw * 1, 180);
getPoints([p.longitude * 1, p.latitude * 1], JSON.parse(p.radius7_quad).nw * 1, 270);
if (points.length > 0) {
entityList.push(viewer.entities.add({
name: "tf_polygon",
polygon: {
hierarchy: Cesium.Cartesian3.fromDegreesArray(points),
material: new Cesium.Color(242 / 255, 205 / 255, 65 / 255).withAlpha(0.2),
extrudedHeight: 1000,
outline: true,
outlineColor: new Cesium.Color(242 / 255, 205 / 255, 65 / 255),
outlineWidth: 10
}
}));
}
}
if (p.radius10 * 1 > 0) {
points = new Array();
getPoints([p.longitude * 1, p.latitude * 1], JSON.parse(p.radius10_quad).ne * 1, 0);//东北方向
getPoints([p.longitude * 1, p.latitude * 1], JSON.parse(p.radius10_quad).se * 1, 90);
getPoints([p.longitude * 1, p.latitude * 1], JSON.parse(p.radius10_quad).sw * 1, 180);
getPoints([p.longitude * 1, p.latitude * 1], JSON.parse(p.radius10_quad).nw * 1, 270);
if (points.length > 0) {
entityList.push(viewer.entities.add({
name: "tf_polygon",
polygon: {
hierarchy: Cesium.Cartesian3.fromDegreesArray(points),
material: new Cesium.Color(242 / 255, 205 / 255, 65 / 255).withAlpha(0.2),
extrudedHeight: 1000,
outline: true,
outlineColor: new Cesium.Color(242 / 255, 205 / 255, 65 / 255),
outlineWidth: 20
}
}));
}
}
if (p.radius12 * 1 > 0) {
points = new Array();
getPoints([p.longitude * 1, p.latitude * 1], JSON.parse(p.radius12_quad).ne * 1, 0);//东北方向
getPoints([p.longitude * 1, p.latitude * 1], JSON.parse(p.radius12_quad).se * 1, 90);
getPoints([p.longitude * 1, p.latitude * 1], JSON.parse(p.radius12_quad).sw * 1, 180);
getPoints([p.longitude * 1, p.latitude * 1], JSON.parse(p.radius12_quad).nw * 1, 270);
if (points.length > 0) {
entityList.push(viewer.entities.add({
name: "tf_polygon",
polygon: {
hierarchy: Cesium.Cartesian3.fromDegreesArray(points),
material: new Cesium.Color(242 / 255, 205 / 255, 65 / 255).withAlpha(0.2),
extrudedHeight: 1000,
outline: true,
outlineColor: new Cesium.Color(242 / 255, 205 / 255, 65 / 255),
outlineWidth: 10
}
}));
}
}
function getPoints(center, cradius, startAngle) {
let radius = cradius / 100;
let pointNum = 90;
let endAngle = startAngle + 90;
let sin, cos, x, y, angle;
for (let i = 0; i <= pointNum; i++) {
angle = startAngle + (endAngle - startAngle) * i / pointNum;
sin = Math.sin(angle * Math.PI / 180);
cos = Math.cos(angle * Math.PI / 180);
x = center[0] + radius * sin;
y = center[1] + radius * cos;
points.push(x, y);
}
}
//预报点
position_xx.forEach(function (p) {
const entity = viewer.entities.add({
name: "tf_position",
describe: p.sets + "_" + p.time + "_" + p.lng + "/" + p.lat + "_"
+ p.speed + " 米/秒," + p.power + "级(" + p.strong + ")" + "_"
+ (p.pressure ? (p.pressure + " 百帕") : "") + "_ _ | | _ | | ",
position: Cesium.Cartesian3.fromDegrees(p.lng * 1, p.lat * 1, 8001),
ellipse: {
semiMinorAxis: new Cesium.CallbackProperty(changeR1, false),//12000.0,
semiMajorAxis: new Cesium.CallbackProperty(changeR1, false),//12000.0,
height: 8001,
material: Cesium.Color.BLUE.withAlpha(0.5)
}
});
entityList.push(entity);
if (p.power * 1 <= 7)//热带低压 TD
entity.ellipse.material = TD;
if (p.power * 1 > 7 && p.power * 1 <= 9)//热带风暴 TS
entity.ellipse.material = TS;
if (p.power * 1 > 9 && p.power * 1 <= 11)//强热带风暴 STS
entity.ellipse.material = STS;
if (p.power * 1 > 11 && p.power * 1 <= 13)//台风 TY
entity.ellipse.material = TY;
if (p.power * 1 > 13 && p.power * 1 <= 15)//强台风 STY
entity.ellipse.material = STY;
if (p.power * 1 > 15)//超强台风 SuperTY
entity.ellipse.material = SuperTY;
})
}
const entity1 = viewer.entities.add({
name: "tf_position",
position: Cesium.Cartesian3.fromDegrees(p.longitude * 1, p.latitude * 1, 8000),
ellipse: {
semiMinorAxis: new Cesium.CallbackProperty(changeR2, false),//84000.0,
semiMajorAxis: new Cesium.CallbackProperty(changeR2, false),//84000.0,
height: 8000,
material: Cesium.Color.BLUE
}
});
entityListTF.push(entity1);
entity1.ellipse.material = new Cesium.ImageMaterialProperty({
image: new Cesium.CallbackProperty(drawCanvasImage, false),
transparent: true
});
entityListTF.forEach(function (e,i) {
if(index!=i) e.show = false;
})
index++;
viewer.camera.flyTo({
destination: new Cesium.Cartesian3.fromDegrees(p.longitude*1, p.latitude*1, 2000000),//坐标
duration:1,
complete:function(){
if(!getIsStop()) roamPath();
},
cancel:function () {
debugger
},
orientation : {
heading : Cesium.Math.toRadians(30.0),
pitch : Cesium.Math.toRadians(-90.0),
roll : 0.0
},
easingFunction: Cesium.EasingFunction.LINEAR_NONE
});
}
roamPath();
})
//viewer.zoomTo(viewer.entities);
//112.7383109598977,25.758452600672697,0
// viewer.camera.flyTo({
// destination: new Cesium.Cartesian3.fromDegrees(112.7383109598977, 25.658452600672697, 4200000),//相机飞入点
// });
}
/**
* 画警戒线
*/
function drawWarningLine(viewer) {
let Line24 = JJX.features[0].geometry.coordinates;
let Line48 = JJX.features[1].geometry.coordinates;
let programBillboard24 = new Cesium.Entity({
position: Cesium.Cartesian3.fromDegrees(Line24[0][0] * 1, Line24[0][1] * 1, Line24[0][2] * 1),
label: { //文字标签
text: "24小时警戒线",
disableDepthTestDistance: Number.POSITIVE_INFINITY,
font: '500 15px Helvetica', // 15pt monospace
color: new Cesium.Color.fromCssColorString("#FF0000"),
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
fillColor: new Cesium.Color.fromCssColorString("#FF0000"),
pixelOffset: new Cesium.Cartesian2(0, -20), //偏移量
backgroundColor: new Cesium.Color(0.5, 0.6, 1, 0)
},
});
let programBillboard48 = new Cesium.Entity({
position: Cesium.Cartesian3.fromDegrees(Line48[3][0] * 1, Line48[3][1] * 1, Line48[3][2] * 1),
label: { //文字标签
text: "48小时警戒线",
disableDepthTestDistance: Number.POSITIVE_INFINITY,
font: '500 15px Helvetica', // 15pt monospace
color: new Cesium.Color.fromCssColorString("#0000FF"),
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
fillColor: new Cesium.Color.fromCssColorString("#0000FF"),
pixelOffset: new Cesium.Cartesian2(0, -20), //偏移量
backgroundColor: new Cesium.Color(0.5, 0.6, 1, 0)
},
})
entityList.push(viewer.entities.add(programBillboard24));
entityList.push(viewer.entities.add(programBillboard48));
entityList.push(viewer.entities.add({
polyline: {
positions: Cesium.Cartesian3.fromDegreesArrayHeights(Line24.join().split(",").map(Number)),//[113.5,14.4,113.7,14.3,113.9,14.2,114.1,14.1,114.3,14.0,114.5,13.9,114.7,13.9,115.0,13.8,115.2,13.8,115.4,13.7,115.6,13.7,115.8,13.7,116.1,13.6,116.3,13.6,116.5,13.5,116.6,13.4,116.8,13.4,116.9,13.3,117.0,13.3,117.1,13.3,117.2,13.3,117.3,13.3,117.4,13.3,117.6,13.1,117.8,13.0,117.9,13.0,118.1,12.9,118.2,12.8,118.4,12.7]
width: 2,
height: 8000,
material: Cesium.Color.RED.withAlpha(0.8)
},
}));
entityList.push(viewer.entities.add({
polyline: {
positions: Cesium.Cartesian3.fromDegreesArrayHeights(Line48.join().split(",").map(Number)),
width: 2,
height: 8000,
material: new Cesium.PolylineDashMaterialProperty({
color: Cesium.Color.BLUE.withAlpha(0.8),
dashLength: 20 //短划线长度
})
},
}));
}
function changeR1() {
let height = Viewer.scene.globe.ellipsoid.cartesianToCartographic(Viewer.camera.position).height;
return (height * 12000) / 4200000;
}
function changeR2() {
let height = Viewer.scene.globe.ellipsoid.cartesianToCartographic(Viewer.camera.position).height;
return (height * 32000) / 4200000;
}
let curCanvas = 0;
function drawCanvasImage() {
++curCanvas;
if (curCanvas >= 1 && curCanvas < 5) return TF1;
if (curCanvas >= 5 && curCanvas < 10) return TF2;
if (curCanvas >= 10 && curCanvas < 15) return TF3;
if (curCanvas >= 15 && curCanvas < 20) return TF4;
if (curCanvas >= 20) {
curCanvas = 0;
return TF1;
}
}
let isHide = true;//牌子展示
let isStop = false;//是否停止漫游
function getIsHide() {
return isHide;
}
function setIsHide(o) {
isHide = o;
}
function getIsStop() {
return isStop;
}
function setIsStop(o) {
isStop = o;
}
function clearEntityList() {
setIsStop(true);
setIsHide(false);
for (let i = 0; i < entityList.length; i++) {
Viewer.entities.remove(entityList[i]);
}
for(let i=0;i<entityListTF.length;i++){
Viewer.entities.remove(entityListTF[i])
}
}
//展示台风明细,单击台风路径的点触发
function showTFMX(obj) {
let DOM = document.getElementById("ZY-QX-ICON")
let c = new Cesium.Cartesian2(obj.v.mousePosition.x, obj.v.mousePosition.y);
update(c);
function update(c) {
let x = c.x - (DOM.offsetWidth) / 2;
let y = c.y - (DOM.offsetHeight);
DOM.style.top = (y - 10) + 'px';
DOM.style.left = x + 'px';
DOM.style.display = "block";
}
obj.viewer.scene.postRender.addEventListener(function () {
let p = new Cesium.Cartesian3(obj.v.pickedFeature.id._position._value.x, obj.v.pickedFeature.id._position._value.y, obj.v.pickedFeature.id._position._value.z);
let c = Cesium.SceneTransforms.wgs84ToWindowCoordinates(obj.viewer.scene, p);
if (getIsHide()) {
update(c);
} else {
DOM.style.display = "none";
}
});
}
//鼠标单击事件
function setClickEvent(viewWindows, callBack) {
let handler = new Cesium.ScreenSpaceEventHandler(viewWindows.scene.canvas);
handler.setInputAction(function (click) {
var pick = viewWindows.scene.pick(click.position);
//选中某模型 pick选中的对象
if (pick) {
// console.log(pick.id);
callBack(viewWindows, {
pickedFeature: pick,
mousePosition: click.position
});
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
return handler;
}
后台封装:台风数据格式
public class TyphoonInfo {
@TableId(value="id")
String id;
@ApiModelProperty(value = "台风编码")
@TableField(value = "code")
String code;
@ApiModelProperty(value = "台风名称")
@TableField(value = "name")
String name;
@ApiModelProperty(value = "台风英文名称")
@TableField(value = "ename")
String ename;
@ApiModelProperty(value = "开始时间")
@TableField(value = "begin_time")
Date beginTime;
@ApiModelProperty(value = "结束时间")
@TableField(value = "end_time")
Date endTime;
@ApiModelProperty(value = "年度")
@TableField(value = "year")
String year;
@ApiModelProperty(value = "是否是当前台风")
@TableField(value = "is_current")
String isCurrent;
@ApiModelProperty(value = "台风路径列表")
@TableField(exist = false)
private List<TyphoonPoints> typhoonPoints;
}
public class TyphoonPoints {
@TableId(value = "id")
String id;
@ApiModelProperty(value = "台风编码")
@TableField(value = "code")
String code;
@ApiModelProperty(value = "时间")
@TableField(value = "time")
Date time;
@ApiModelProperty(value = "经度")
@TableField(value = "longitude")
String longitude;
@ApiModelProperty(value = "纬度")
@TableField(value = "latitude")
String latitude;
@ApiModelProperty(value = "级别")
@TableField(value = "strong")
String strong;
@ApiModelProperty(value = "最大风力")
@TableField(value = "power")
String power;
@ApiModelProperty(value = "最大风速")
@TableField(value = "speed")
String speed;
@ApiModelProperty(value = "中心气压")
@TableField(value = "pressure")
String pressure;
@ApiModelProperty(value = "移动速度")
@TableField(value = "move_Speed")
String moveSpeed;
@ApiModelProperty(value = "移动方向")
@TableField(value = "move_Dir")
String moveDir;
@ApiModelProperty(value = "7级风圈最大半径值")
@TableField(value = "radius7")
String radius7;
@ApiModelProperty(value = "10级风圈最大半径值")
@TableField(value = "radius10")
String radius10;
@ApiModelProperty(value = "12级风圈最大半径值")
@TableField(value = "radius12")
String radius12;
@ApiModelProperty(value = "7级风圈范围")
@TableField(value = "radius7_Quad")
String radius7Quad;
@ApiModelProperty(value = "10级风圈范围")
@TableField(value = "radius10_Quad")
String radius10Quad;
@ApiModelProperty(value = "12级风圈范围")
@TableField(value ="radius12_Quad")
String radius12Quad;
}
台风数据的获取,可以去网上抓取,网上很多台风数据,附最终效果图:

添加警戒线,
添加台风路径漫游 核心代码:
viewer.camera.flyTo({
destination: new Cesium.Cartesian3.fromDegrees(p.longitude*1, p.latitude*1, 2000000),//坐标
duration:1,
complete:function(){
if(!getIsStop()) roamPath();
},
cancel:function () {
},
orientation : {
heading : Cesium.Math.toRadians(30.0),
pitch : Cesium.Math.toRadians(-90.0),
roll : 0.0
},
easingFunction: Cesium.EasingFunction.LINEAR_NONE
});

浙公网安备 33010602011771号