Bootstrap

两年前初学Cesium时的记录 刚才偶然找到了 哈哈哈

cesium方法总结

添加点,文字(实体)

viewer.entities.add({
                position: Cesium.Cartesian3.fromDegrees(StationLongitude, StationLatitude, StationHeight),
                id: StationNum, //站点编号,供点击事件使用
                data: {//需要传递给点击事件的数据
                    name: StationName, //站点名称,
                    coordinate: { //站点坐标,
                        longitude: StationLongitude,
                        latitude: StationLatitude,
                        height: StationHeight
                    },
                },
                // 点
                point: {
                    color: Cesium.Color.YELLOW, // 点位颜色
                    pixelSize: 8, // 像素点大小
                    scale: StationZoom, //缩放
                    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, //贴地
                },
                // 文字
                label: {
                    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, //贴地
                    scale: StationZoom, //缩放
                    text: StationName, // 文本。支持显式换行符“ \ n”
                    font: "14pt Source Han Sans CN", // 字体样式,以CSS语法指定字体
                    fillColor: Cesium.Color.BLUE, // 字体颜色
                    backgroundColor: "rgba(0,0,0,0)", //Cesium.Color.AQUA
                    showBackground: false, // 是否显示背景颜色
                    outline: false, // 字体边框
                    outlineColor: Cesium.Color.WHITE, // 字体边框颜色
                    outlineWidth: 10, // 字体边框尺寸
                    scale: 1.0, //缩放
                    style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                    // 相对于坐标的水平位置
                    verticalOrigin: Cesium.VerticalOrigin.CENTER,
                    // 相对于坐标的水平位置
                    horizontalOrigin: Cesium.HorizontalOrigin.BOTTOM,
                    // 该属性指定标签在屏幕空间中距此标签原点的像素偏移量
                    pixelOffset: new Cesium.Cartesian2(0, 15),
                    show: true, //显示
                },

            });

添加div(信息牌)

let div = document.createElement("div");
            div.id = StationNum;
            div.setAttribute('class', 'divlabel-container')
            div.style.position = "absolute";
            div.style.width = "80px";
            div.style.height = "30px";
            // div.style.backgroundImage = "url(data/imgs/gif_bgi.gif)"
            // div.style.backgroundRepeat = "no-repeat"
            // div.style.backgroundSize = "cover"
            let HTMLTable = `
    <div :id="id" class="divlabel-container infobox" v-if="show">
        <div class="animate-maker-border">
            <p><span class="animate-marker__text">${StationTem}℃</span></p>
            
        </div>
    </div>
    `;
            div.innerHTML = HTMLTable;
            viewer.cesiumWidget.container.appendChild(div);
            //定位站点信息框
            let gisPosition = Cesium.Cartesian3.fromDegrees(
                StationLongitude,//div经度
                StationLatitude,//div纬度
                StationHeight + 800//div高度

            );
            const canvasHeight = viewer.scene.canvas.height;
            const windowPosition = new Cesium.Cartesian2();
            Cesium.SceneTransforms.wgs84ToWindowCoordinates(
                viewer.scene,
                gisPosition,
                windowPosition,
            );
            div.style.bottom = canvasHeight - windowPosition.y + "px";
            const elWidth = div.offsetWidth;
            div.style.left = windowPosition.x - elWidth / 2 + "px";
            //信息框跟随地球移动实时更新位置
            viewer.scene.postRender.addEventListener(() => {
                const canvasHeight = viewer.scene.canvas.height;
                const windowPosition = new Cesium.Cartesian2();
                Cesium.SceneTransforms.wgs84ToWindowCoordinates(
                    viewer.scene,
                    gisPosition,
                    windowPosition
                );
                div.style.bottom = canvasHeight - windowPosition.y + "px";
                const elWidth = div.offsetWidth;
                div.style.left = windowPosition.x - elWidth / 2 + "px";

            }, this);

添加路径线czml格式(高亮材质)

// json路径方法的竖线
            // const czml = [
            //     {
            //         id: "document",
            //         name: "StationLine",
            //         version: "1.0",
            //     },
            //     {
            //         id: StationNum + "_line",
            //         name:
            //             "StationLine_",
            //         polyline: {
            //             positions: {
            //                 //位置
            //                 cartographicDegrees: [StationLongitude, StationLatitude, StationHeight + 500, StationLongitude, StationLatitude, StationHeight],
            //             },
            //             material: {//纹理材质
            //                 polylineGlow: {
            //                     color: {
            //                         rgba: [100, 149, 237, 255],
            //                     },
            //                     glowPower: 0.3,//辉光功率
            //                     // taperPower: 0.5,//锥度
            //                 },
            //             },
            //             width: 5,
            //         },
            //     },

            // ];
            // const dataSourcePromise = Cesium.CzmlDataSource.load(czml);
            // viewer.dataSources.add(dataSourcePromise);

线实体,图片作为材质

//线 实体 添加图片作为材质
            // viewer.entities.add({
            //     name:
            //         "Orange line with black outline at height and following the surface",
            //     polyline: {
            //         positions: Cesium.Cartesian3.fromDegreesArrayHeights([
            //             109.1,
            //             34.31,
            //             0,
            //             109.1,
            //             34.31,
            //             1000,
            //         ]),
            //         width: 50,
            //         material: new Cesium.ImageMaterialProperty({
            //             image: 'data/imgs/line2.png',
            //             repeat: new Cesium.Cartesian2(1.0, 1.0),
            //             transparent: true,
            //             color: Cesium.Color.WHITE.withAlpha(1),
            //         })
            //     },
            // });

流动线实体(着色器实现)

 viewer.entities.add({
                polyline: {
                    positions: Cesium.Cartesian3.fromDegreesArrayHeights([
                        StationLongitude,
                        StationLatitude,
                        StationHeight,
                        StationLongitude,
                        StationLatitude,
                        StationHeight + 800
                    ]),
                    width: 3,
                    material: new DynamicFlowMaterialProperty(Cesium.Color.BLUE, 30, 0, 0.8)//颜色,速度,透明度,百分比
                },
            });

需要引入的Js

export  function DynamicFlowMaterialProperty(color, speed,opacity,percent){
	this._definitionChanged = new Cesium.Event();
    this.color = color;
    this.speed = speed;
	this.opacity = opacity;
	this.percent = percent;
	DynamicFlowMaterialProperty.prototype.init(color, speed,opacity,percent);
}

Object.defineProperties(DynamicFlowMaterialProperty.prototype, {
      isConstant: {
        get: function () {
          return false;
        }
      },
      definitionChanged: {
        get: function () {
          return this._definitionChanged;
        }
      }
});

DynamicFlowMaterialProperty.prototype.getType = function (time) {
      return 'DynamicFlow';
}
DynamicFlowMaterialProperty.prototype.getValue = function (time, result) {
    if (!Cesium.defined(result)) {
      result = {};
    }
	//console.log(" this.color1", this.color);
    result.color = this.color;
    result.speed = this.speed;
    result.opacity = this.opacity;
	result.percent =this.percent;
    return result;
}
DynamicFlowMaterialProperty.prototype.equals = function (other) {
	return false;
    //return this === other || (other instanceof DynamicFlowMaterialProperty && Cesium.Property.equals(this._color, other._color));
}
DynamicFlowMaterialProperty.prototype.init = function (color, speed,opacity,percent) {
	var sourceStr=`
            uniform vec4 color;
            uniform float speed;
            uniform float percent;
            uniform float opacity;
            czm_material czm_getMaterial(czm_materialInput materialInput)
                {
                    czm_material material = czm_getDefaultMaterial(materialInput);
                    vec2 st = materialInput.st;
                    float time = fract( czm_frameNumber  *  speed / 1000.0);
                    time *= (1.0 + percent);
                    float alpha = smoothstep(time- percent, time, st.s) * step(-time, -st.s);
                    alpha += opacity;
                    material.diffuse= color.rgb;
                    material.alpha = alpha;
                    return material;
                }
                `
		// console.log("sourceStr",sourceStr);
		// console.log("参数:",color, speed,opacity,percent);
		var uniforms={
			color: color,
			speed: speed,
			opacity: opacity,
			percent: percent,
        };
		Cesium.Material._materialCache.addMaterial("DynamicFlow", {
            fabric: {
					type:"DynamicFlow", 
					uniforms:uniforms, 
					source:sourceStr
			},
            translucent: true
        });
    
}

圆告警扩散


            let r1 = 200;
            let r2 = 200;
            //底座1
            viewer.entities.add({
                id: StationNum + "_bottom",//更改项
                position: Cesium.Cartesian3.fromDegrees(StationLongitude, StationLatitude, StationHeight),//更改项
                // billboard: {
                // image: 'data/imgs/WARN.png',
                // width: 40,
                // height: 40,
                // horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
                // verticalOrigin: Cesium.HorizontalOrigin.Bottom,
                // },
                ellipse: {
                    semiMinorAxis: new Cesium.CallbackProperty(function () {
                        r1 = r1 + 1;
                        if (r1 >= 200) {
                            r1 = 0;
                        }
                        return r1;
                    }, false),
                    semiMajorAxis: new Cesium.CallbackProperty(function () {
                        r2 = r2 + 1;
                        if (r2 >= 200) {
                            r2 = 0;
                        }
                        return r2;
                    }, false),
                    height: StationHeight,
                    material: new Cesium.ImageMaterialProperty({
                        image: 'data/imgs/WARN.png',
                        repeat: new Cesium.Cartesian2(1.0, 1.0),
                        transparent: true,
                        color: Cesium.Color.WHITE.withAlpha(1),
                    })
                }
            });

点击地图获取该点经纬度

    var handler= new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
    handler.setInputAction(function (event) {
     var earthPosition  = viewer.camera.pickEllipsoid(event.position,viewer.scene.globe.ellipsoid);
     var cartographic = Cesium.Cartographic.fromCartesian(earthPosition, viewer.scene.globe.ellipsoid, new Cesium.Cartographic());
     var lat=Cesium.Math.toDegrees(cartographic.latitude);
     var lng=Cesium.Math.toDegrees(cartographic.longitude);
     var height=cartographic.height;
     console.log("[Lng=>"+lng+",Lat=>"+lat+",H=>"+height+"]");
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

添加position的方法

position: Cesium.Cartographic.toCartesian(xxx),//使用迪卡尔坐标
position = Cesium.Cartesian3.fromDegrees(lon,lat,height);//使用wgs84坐标系
// (如果只传入经纬度 没有高度,则要设置贴地 )
// heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,

添加color的方法


Cesium.Color.fromCssColorString('#ff0000')
Cesium.Color.BLACK
new Cesium.Color(red, green, blue, alpha)
new Cesium.Color(0.165, 0.165, 0.165,0.8)
Cesium.Color.WHITECesium.Color.RED.withAlpha(0.5)
Cesium.Color.fromCssColorString("rgba(254, 129, 6, 0.75)")
var color = Cesium.Color.fromRandom({alpha: 0.5  });

将entity转化为primitive

const data = await Cesium.Resource.fetchJson('data/geo/water_.json');
  const geometryInstances = [];
  data.geometries.forEach((item) => {
    const geometryInstance = new Cesium.GeometryInstance({
      geometry: new Cesium.GroundPolylineGeometry({
        positions: Cesium.Cartesian3.fromDegreesArray(
          item.coordinates.flat(Infinity)
        ),
        width: 7.0
      })
    });
    geometryInstances.push(geometryInstance);
  });
  water = viewer.scene.primitives.add(
    new Cesium.GroundPolylinePrimitive({
      geometryInstances: geometryInstances,
      appearance: new Cesium.PolylineMaterialAppearance({
        material: new Cesium.Material({
          fabric: {
            type: 'Color',
            uniforms: {
              color: Cesium.Color.fromCssColorString('#9fccff66')
            }
          }
        })
      })
    })
  );

使用 Primitive 添加

添加点

实例化 primitive点类

//全局实例化Primitive点,删除须用
let pointPrimitives = viewer.scene.primitives.add(
  new Cesium.PointPrimitiveCollection({
    blendOption: Cesium.BlendOption.TRANSLUCENT, //透明混合度,用于大数据量渲染时提高效率
    // debugShowBoundingVolume:true
  })
);

添加点

pointPrimitives.add({
        outlineColor: Cesium.Color.fromCssColorString("#f6a00b"),
        outlineWidth: 2,
        pixelSize: 10,
        pixelSize: 10,
        color: Cesium.Color.fromCssColorString("#f6a00b"),
        translucencyByDistance: new Cesium.NearFarScalar(
          200000,
          1.0,
          400000,
          0
        ), //地球缩小时透明度降低
        position: Cesium.Cartesian3.fromDegrees(item.LONGITUDE, item.LATITUDE, 0),
        disableDepthTestDistance: Number.POSITIVE_INFINITY, //永远禁用深度测试
      });

删除点

//删除2D站点 点
export function DelStationPoint_2D() {
  pointPrimitives.show = false;//不显示
  viewer.scene.primitives.remove(pointPrimitives);//在类中移除点
  pointPrimitives = null;//清空实例化对象
  pointPrimitives = viewer.scene.primitives.add(
    new Cesium.PointPrimitiveCollection({
      blendOption: Cesium.BlendOption.TRANSLUCENT, //透明混合度,用于大数据量渲染时提高效率
    })
  );//重新实例化对象以便于下次使用
}

添加文字

实例化对象

let label_name = viewer.scene.primitives.add(
  new Cesium.LabelCollection({
    zIndex: 10,
  })
);

添加文字

label_name.add({
        //添加站名
        position: new Cesium.Cartesian3.fromDegrees(+item.Lon, +item.Lat, 0),
        text: String(stationName),
        font: font,
        fillColor: textcolor,
        horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
        verticalOrigin: Cesium.VerticalOrigin.CENTER,
        translucencyByDistance: new Cesium.NearFarScalar(
          200000,
          1.0,
          400000,
          0
        ), //地球缩小时透明度降低
        pixelOffset: new Cesium.Cartesian2(0, 15), // 偏移量
        // distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 400000),//显示高度
      });

删除文字

//与上删除方法相同

添加卫星轨迹

var viewer = new Cesium.Viewer("cesiumContainer");
//去掉版权信息
viewer._cesiumWidget._creditContainer.style.display = "none";
 
设定了模拟时间的边界
//var start = Cesium.JulianDate.fromDate(new Date(2015, 2, 25, 16));
//var stop = Cesium.JulianDate.addSeconds(start, 360, new Cesium.JulianDate());
 
var start = new Cesium.JulianDate.fromDate(new Date());
start = Cesium.JulianDate.addHours(start, 8, new Cesium.JulianDate());//东八区时间
// 结束时间
var stop = Cesium.JulianDate.addSeconds(start, 360, new Cesium.JulianDate());
 
//确保查看器处于预期的时间
viewer.clock.startTime = start.clone();
viewer.clock.stopTime = stop.clone();
viewer.clock.currentTime = start.clone();
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP; //循环结束时
//时间变化来控制速度 // 时间速率,数字越大时间过的越快
viewer.clock.multiplier = 10;
//给时间线设置边界
viewer.timeline.zoomTo(start, stop);
 
斜线上
//function computeCirclularFlight(lon, lat, hei) {
//    var property = new Cesium.SampledPositionProperty();
//    //i的增量不能太大,不然差值器无法把图形如愿画出来
//    for (var i = 0; i <= 360; i += 30) {
//       // var radians = Cesium.Math.toRadians(i);
//        var time = Cesium.JulianDate.addSeconds(start, i, new Cesium.JulianDate());
//        //绕赤道飞
//        var position = Cesium.Cartesian3.fromDegrees(lon+i, lat + i, hei);
//        //添加sample
//        property.addSample(time, position);
//    }
//    return property;
//}
 
 
function mySatePosition (){
    this.lon = 0;
    this.lat = 0;
    this.hei = 700000;          //卫星高度
    this.phei = 700000 / 2;     //轨道高度
    this.time = 0;
}
 
//var oneSate1;
 
//function getoneState(arr) {
//    arr.splice(0, arr.length);
//    for (var i = 0; i <= 360; i += 30) {
//        var aaa = new mySatePosition();
//        aaa.lon = 0;
//        aaa.lat = i;
//        aaa.time = i;
//        arr.push(aaa);
//    }
//}
//getoneState(oneSate1);
 
function computeCirclularFlight(source,panduan) {
    var property = new Cesium.SampledPositionProperty();
    if (panduan == 1) {         //卫星位置
        for (var i = 0; i < source.length; i++) {
            var time = Cesium.JulianDate.addSeconds(start, source[i].time, new Cesium.JulianDate);
            var position = Cesium.Cartesian3.fromDegrees(source[i].lon, source[i].lat, source[i].hei);
            // 添加位置,和时间对应
            property.addSample(time, position);
        }
    } else if (panduan == 2) {//轨道位置
        for (var i = 0; i < source.length; i++) {
            var time = Cesium.JulianDate.addSeconds(start, source[i].time, new Cesium.JulianDate);
            var position = Cesium.Cartesian3.fromDegrees(source[i].lon, source[i].lat, source[i].phei);
            // 添加位置,和时间对应
            property.addSample(time, position);
        }
    }
    return property;
}
 
//var arrStates = [];
//arrStates.push(oneSate1);
 
//随机获得位置
var arrStates = [];
function getRandState(brr, count) {
    for (var m = 0; m < count; m++) {
        var arr = [];
        var t1 = Math.floor(Math.random() * 360);
        var t2 = Math.floor(Math.random() * 360);
        for (var i = t1; i <= 360 + t1; i += 30) {
            var aaa = new mySatePosition();
            aaa.lon = t2;
            aaa.lat = i;
            aaa.time = i - t1;
            arr.push(aaa);
        }
        brr.push(arr);
    }
    for (var m = 0; m < count; m++) {
        var arr = [];
        var t1 = Math.floor(Math.random() * 360);
        var t2 = Math.floor(Math.random() * 360);
        for (var i = t1; i <= 360 + t1; i += 30) {
            var aaa = new mySatePosition();
            aaa.lon = i;
            aaa.lat = t2;
            aaa.time = i - t1;
            arr.push(aaa);
        }
        brr.push(arr);
    }
}
getRandState(arrStates, 3);
 
function getStatePath(aaa) {
    var entity_ty1p = computeCirclularFlight(aaa, 2);
    var entity_ty1 = viewer.entities.add({
        availability: new Cesium.TimeIntervalCollection([new Cesium.TimeInterval({
            start: start,
            stop: stop
        })]),
        position: entity_ty1p,   //轨道高度
        orientation: new Cesium.VelocityOrientationProperty(entity_ty1p),
        cylinder: {
            HeightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
            length: 700000,
            topRadius: 0,
            bottomRadius: 700000 / 2,
            material: Cesium.Color.RED.withAlpha(.4),
            outline: !0,
            numberOfVerticalLines: 0,
            outlineColor: Cesium.Color.RED.withAlpha(.8)
        },
    });
 
    entity_ty1.position.setInterpolationOptions({
        interpolationDegree: 5,
        interpolationAlgorithm: Cesium.LagrangePolynomialApproximation
    });
 
    var entity1p = computeCirclularFlight(aaa, 1);
    //创建实体
    var entity1 = viewer.entities.add({
        // 将实体availability设置为与模拟时间相同的时间间隔。
        availability: new Cesium.TimeIntervalCollection([new Cesium.TimeInterval({
            start: start,
            stop: stop
        })]),
        position: entity1p,//计算实体位置属性
        //基于位置移动自动计算方向.
        orientation: new Cesium.VelocityOrientationProperty(entity1p),
        //加载飞机模型
        model: {
            uri: '../data/gltfModel/CesiumAir/Cesium_Air.gltf ',
            minimumPixelSize: 32
        },
        //路径
        path: {
            resolution: 1,
            material: new Cesium.PolylineGlowMaterialProperty({
                glowPower: 0.1,
                color: Cesium.Color.PINK
            }),
            width: 5
        }
    });
 
    //差值器
    entity1.position.setInterpolationOptions({
        interpolationDegree: 5,
        interpolationAlgorithm: Cesium.LagrangePolynomialApproximation
    });
}
 
function startfunc() {
    for (var i = 0; i < arrStates.length; i++) {
        getStatePath(arrStates[i]);
    }
}
startfunc();

添加广告牌

实例化对象

let nameBillboard = viewer.scene.primitives.add(
  new Cesium.BillboardCollection({
    blendOption: Cesium.BlendOption.TRANSLUCENT,
  })
);

添加广告牌

const canvas = document.createElement("canvas");
  canvas.width = 300;
  canvas.height = 70;
  const ctx = canvas.getContext("2d");
  ctx.font = "34px Microsoft YaHei";
  ctx.textAlign = "center";
  ctx.textBaseline = "middle";
  ctx.fillStyle = textcolor;
  ctx.fillText(item.name, 150, 50);
  let image = new Image();
  // 将canvas转换成图片
  let canvasSrc = canvas.toDataURL("image/png");
  // 设置图片地址
  image.src = canvasSrc;
  image.onload = () => {
    nameBillboard.add({
      // id: 'value-' + index,
      image: image,
      position: new Cesium.Cartesian3.fromDegrees(+item.Lon, +item.Lat, 0),
      pixelOffset: new Cesium.Cartesian2(0, 20), // 偏移量
      scale: 0.5,
      horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
      verticalOrigin: Cesium.VerticalOrigin.CENTER,
      disableDepthTestDistance: Number.POSITIVE_INFINITY,
      // translucencyByDistance: new Cesium.NearFarScalar(200000, 1.0, 500000, 0),
      // scaleByDistance: new Cesium.NearFarScalar(80000, 1.1, 300000, 0.8),
    });
  };

删除广告牌

nameBillboard.show = false;
  nameBillboard = null;
  nameBillboard = viewer.scene.primitives.add(new Cesium.BillboardCollection());

设置贴地

如果不存在地形,直接设置高度为 0

如果存在地形,设置高度为地形高度即可贴地。
当高度未知时,有两种贴地方法:
1.添加贴地属性

heightReference: Cesium.HeightReference.CLAMP_TO_GROUND

2.设置位置时操作:

let p = ellipsoid.cartesianToCartographic(Cesium.Cartesian3.fromDegrees(+item.Lon, +item.Lat));//在此处设置经纬度
    let promise = Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, [p]);
    Cesium.when(promise, () => {
      pointPrimitives.add({//正常的添加方法
        outlineColor: outlineColor,
        outlineWidth: outlineWidth,
        pixelSize: pixelSize,
        pixelSize: 10,
        color: color,
        translucencyByDistance: new Cesium.NearFarScalar(
          200000,
          1.0,
          400000,
          0
        ), //地球缩小时透明度降低
        position: Cesium.Cartographic.toCartesian(p),//在此处替换位置
        disableDepthTestDistance: Number.POSITIVE_INFINITY, //永远禁用深度测试
      });
    });

删除primitive和entity

primitive:

let react = scene.primitives.add(new Cesium.RectanglePrimitive({
    //绘制矩形
    rectangle : Cesium.Rectangle.fromDegrees(-100.0, 20.0, -90.0, 30.0),
    material : Cesium.Material.fromType('Dot')  //设置材质
}));

//根据变量销毁
react.destroy()

//销毁所有
scene.primitives.removeall(),//谨慎使用,可能删除不必要的primitive

entity:


//获取实体
var getByIdBox = viewer.entities.getById('Box');
//方法一(针对性删除某一个)
 viewer.entities.remove(redBox); 
 //方法二(通过id删除)
  viewer.entities.remove(getByIdBox);
 //方法三(删除所有实体)
 viewer.entities.removeAll();

监听滚轮事件返回相机高度

// cesium 事件
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);  
handler.setInputAction((wheelment) => {
      let CemeraHeight = viewer.camera.positionCartographic.height;
    console.log(CemeraHeight);
  }, Cesium.ScreenSpaceEventType.WHEEL);

制作风流场

风流场类:

/****
 *风场类
 ****/
export var CanvasWindy = function (json, params) {
  //风场json数据
  this.windData = json;
  //可配置参数
  this.viewer = params.viewer;
  this.canvas = params.canvas;
  this.extent = params.extent || []; //风场绘制时的地图范围,范围不应该大于风场数据的范围,顺序:west/east/south/north,有正负区分,如:[110,120,30,36]
  this.canvasContext = params.canvas.getContext("2d"); //canvas上下文
  this.canvasWidth = params.canvasWidth || 300; //画板宽度
  this.canvasHeight = params.canvasHeight || 180; //画板高度
  this.speedRate = params.speedRate || 100; //风前进速率,意思是将当前风场横向纵向分成100份,再乘以风速就能得到移动位置,无论地图缩放到哪一级别都是一样的速度,可以用该数值控制线流动的快慢,值越大,越慢,
  this.particlesNumber = params.particlesNumber || 20000; //初始粒子总数,根据实际需要进行调节
  this.maxAge = params.maxAge || 120; //每个粒子的最大生存周期
  this.frameTime = 1000 / (params.frameRate || 10); //每秒刷新次数,因为requestAnimationFrame固定每秒60次的渲染,所以如果不想这么快,就把该数值调小一些
  this.color = params.color || "#ffffff"; //线颜色,提供几个示例颜色['#14208e','#3ac32b','#e0761a']
  this.lineWidth = params.lineWidth || 1; //线宽度
  //内置参数
  this.initExtent = []; //风场初始范围
  this.calc_speedRate = [0, 0]; //根据speedRate参数计算经纬度步进长度
  this.windField = null;
  this.particles = [];
  this.animateFrame = null; //requestAnimationFrame事件句柄,用来清除操作
  this.isdistory = false; //是否销毁,进行删除操作
  this._init();
};
CanvasWindy.prototype = {
  constructor: CanvasWindy,
  _init: function () {
    var self = this;
    // 创建风场网格
    this.windField = this.createField();
    this.initExtent = [
      this.windField.west,
      this.windField.east,
      this.windField.south,
      this.windField.north,
    ];
    //如果风场创建时,传入的参数有extent,就根据给定的extent,让随机生成的粒子落在extent范围内
    if (this.extent.length != 0) {
      this.extent = [
        Math.max(this.initExtent[0], this.extent[0]),
        Math.min(this.initExtent[1], this.extent[1]),
        Math.max(this.initExtent[2], this.extent[2]),
        Math.min(this.initExtent[3], this.extent[3]),
      ];
    }
    // console.log(this.extent);
    this._calcStep();
    // 创建风场粒子
    for (var i = 0; i < this.particlesNumber; i++) {
      this.particles.push(this.randomParticle(new CanvasParticle()));
    }
    this.canvasContext.fillStyle = "rgba(0, 0, 0, 0.97)";
    this.canvasContext.globalAlpha = 0.6;
    this.animate();

    var then = Date.now();
    (function frame() {
      if (!self.isdistory) {
        self.animateFrame = requestAnimationFrame(frame);
        var now = Date.now();
        var delta = now - then;
        if (delta > self.frameTime) {
          then = now - (delta % self.frameTime);
          self.animate();
        }
      } else {
        self.removeLines();
      }
    })();
  },
  //计算经纬度步进长度
  _calcStep: function () {
    var isextent = this.extent.length != 0;
    var calcExtent = isextent ? this.extent : this.initExtent;
    var calcSpeed = this.speedRate;
    this.calc_speedRate = [
      (calcExtent[1] - calcExtent[0]) / calcSpeed,
      (calcExtent[3] - calcExtent[2]) / calcSpeed,
    ];
  },
  //根据现有参数重新生成风场
  redraw: function () {
    window.cancelAnimationFrame(this.animateFrame);
    this.particles = [];
    this._init();
  },
  createField: function () {
    var data = this._parseWindJson();
    return new CanvasWindField(data);
  },
  animate: function () {
    var self = this,
      field = self.windField;
    var nextLng = null,
      nextLat = null,
      uv = null;
    self.particles.forEach(function (particle) {
      if (particle.age <= 0) {
        self.randomParticle(particle);
      }
      if (particle.age > 0) {
        var x = particle.x,
          y = particle.y,
          tlng = particle.tlng,
          tlat = particle.tlat;
        var gridpos = self._togrid(tlng, tlat);
        var tx = gridpos[0];
        var ty = gridpos[1];
        if (!self.isInExtent(tlng, tlat)) {
          particle.age = 0;
        } else {
          uv = field.getIn(tx, ty);
          nextLng = tlng + self.calc_speedRate[0] * uv[0];
          nextLat = tlat + self.calc_speedRate[1] * uv[1];
          particle.lng = tlng;
          particle.lat = tlat;
          particle.x = tx;
          particle.y = ty;
          particle.tlng = nextLng;
          particle.tlat = nextLat;
          particle.age--;
        }
      }
    });
    if (self.particles.length <= 0) this.removeLines();
    self._drawLines();
  },
  //粒子是否在地图范围内
  isInExtent: function (lng, lat) {
    var calcExtent = this.initExtent;
    if (
      lng >= calcExtent[0] &&
      lng <= calcExtent[1] &&
      lat >= calcExtent[2] &&
      lat <= calcExtent[3]
    )
      return true;
    return false;
  },
  _resize: function (width, height) {
    this.canvasWidth = width;
    this.canvasHeight = height;
  },
  _parseWindJson: function () {
    var uComponent = null,
      vComponent = null,
      header = null;
    this.windData.forEach(function (record) {
      var type =
        record.header.parameterCategory + "," + record.header.parameterNumber;
      switch (type) {
        case "2,2":
          uComponent = record["data"];
          header = record["header"];
          break;
        case "2,3":
          vComponent = record["data"];
          break;
        default:
          break;
      }
    });
    return {
      header: header,
      uComponent: uComponent,
      vComponent: vComponent,
    };
  },
  removeLines: function () {
    window.cancelAnimationFrame(this.animateFrame);
    this.isdistory = true;
    this.canvas.width = 1;
    document.getElementById("content").removeChild(this.canvas);
  },
  //根据粒子当前所处的位置(棋盘网格位置),计算经纬度,在根据经纬度返回屏幕坐标
  _tomap: function (lng, lat, particle) {
    var ct3 = Cesium.Cartesian3.fromDegrees(lng, lat, 0);
    // 判断当前点是否在地球可见端
    var isVisible = new Cesium.EllipsoidalOccluder(
      Cesium.Ellipsoid.WGS84,
      this.viewer.camera.position
    ).isPointVisible(ct3);
    var pos = Cesium.SceneTransforms.wgs84ToWindowCoordinates(
      this.viewer.scene,
      ct3
    );
    if (!isVisible) {
      particle.age = 0;
    }
    // console.log(pos);
    return pos ? [pos.x, pos.y] : null;
  },
  //根据经纬度,算出棋盘格位置
  _togrid: function (lng, lat) {
    var field = this.windField;
    var x =
      ((lng - this.initExtent[0]) / (this.initExtent[1] - this.initExtent[0])) *
      (field.cols - 1);
    var y =
      ((this.initExtent[3] - lat) / (this.initExtent[3] - this.initExtent[2])) *
      (field.rows - 1);
    return [x, y];
  },
  _drawLines: function () {
    var self = this;
    var particles = this.particles;
    this.canvasContext.lineWidth = self.lineWidth;
    //后绘制的图形和前绘制的图形如果发生遮挡的话,只显示后绘制的图形跟前一个绘制的图形重合的前绘制的图形部分,示例:https://www.w3school.com.cn/tiy/t.asp?f=html5_canvas_globalcompop_all
    this.canvasContext.globalCompositeOperation = "destination-in";
    this.canvasContext.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
    this.canvasContext.globalCompositeOperation = "lighter"; //重叠部分的颜色会被重新计算
    this.canvasContext.globalAlpha = 0.9;
    this.canvasContext.beginPath();
    this.canvasContext.strokeStyle = this.color;
    particles.forEach(function (particle) {
      var movetopos = self._tomap(particle.lng, particle.lat, particle);
      var linetopos = self._tomap(particle.tlng, particle.tlat, particle);
      // console.log(movetopos,linetopos);
      if (movetopos != null && linetopos != null) {
        self.canvasContext.moveTo(movetopos[0], movetopos[1]);
        self.canvasContext.lineTo(linetopos[0], linetopos[1]);
      }
    });
    this.canvasContext.stroke();
  },
  //随机数生成器(小数)
  fRandomByfloat: function (under, over) {
    return under + Math.random() * (over - under);
  },
  //随机数生成器(整数)
  fRandomBy: function (under, over) {
    switch (arguments.length) {
      case 1:
        return parseInt(Math.random() * under + 1);
      case 2:
        return parseInt(Math.random() * (over - under + 1) + under);
      default:
        return 0;
    }
  },
  //根据当前风场extent随机生成粒子
  randomParticle: function (particle) {
    var safe = 30,
      x = -1,
      y = -1,
      lng = null,
      lat = null;
    var hasextent = this.extent.length != 0;
    var calc_extent = hasextent ? this.extent : this.initExtent;
    do {
      try {
        if (hasextent) {
          var pos_x = this.fRandomBy(0, this.canvasWidth);
          var pos_y = this.fRandomBy(0, this.canvasHeight);
          var cartesian = this.viewer.camera.pickEllipsoid(
            new Cesium.Cartesian2(pos_x, pos_y),
            this.viewer.scene.globe.ellipsoid
          );
          var cartographic =
            this.viewer.scene.globe.ellipsoid.cartesianToCartographic(
              cartesian
            );
          if (cartographic) {
            //将弧度转为度的十进制度表示
            lng = Cesium.Math.toDegrees(cartographic.longitude);
            lat = Cesium.Math.toDegrees(cartographic.latitude);
          }
        } else {
          lng = this.fRandomByfloat(calc_extent[0], calc_extent[1]);
          lat = this.fRandomByfloat(calc_extent[2], calc_extent[3]);
        }
      } catch (e) {}
      if (lng) {
        var gridpos = this._togrid(lng, lat);
        x = gridpos[0];
        y = gridpos[1];
      }
    } while (this.windField.getIn(x, y)[2] <= 0 && safe++ < 30);
    var field = this.windField;
    var uv = field.getIn(x, y);
    var nextLng = lng + this.calc_speedRate[0] * uv[0];
    var nextLat = lat + this.calc_speedRate[1] * uv[1];
    particle.lng = lng;
    particle.lat = lat;
    particle.x = x;
    particle.y = y;
    particle.tlng = nextLng;
    particle.tlat = nextLat;
    particle.speed = uv[2];
    particle.age = Math.round(Math.random() * this.maxAge); //每一次生成都不一样
    return particle;
  },
};

/****
 *棋盘类
 *根据风场数据生产风场棋盘网格
 ****/
var CanvasWindField = function (obj) {
  this.west = null;
  this.east = null;
  this.south = null;
  this.north = null;
  this.rows = null;
  this.cols = null;
  this.dx = null;
  this.dy = null;
  this.unit = null;
  this.date = null;

  this.grid = null;
  this._init(obj);
};
CanvasWindField.prototype = {
  constructor: CanvasWindField,
  _init: function (obj) {
    var header = obj.header,
      uComponent = obj["uComponent"],
      vComponent = obj["vComponent"];

    // debugger;
    this.west = +header["lo1"];
    this.east = +header["lo2"];
    this.south = +header["la2"];
    this.north = +header["la1"];
    this.rows = +header["ny"];
    this.cols = +header["nx"];
    this.dx = +header["dx"];
    this.dy = +header["dy"];
    this.unit = header["parameterUnit"];
    this.date = header["refTime"];

    this.grid = [];
    var k = 0,
      rows = null,
      uv = null;
    for (var j = 0; j < this.rows; j++) {
      rows = [];
      for (var i = 0; i < this.cols; i++, k++) {
        uv = this._calcUV(uComponent[k], vComponent[k]);
        rows.push(uv);
      }
      this.grid.push(rows);
    }
  },
  _calcUV: function (u, v) {
    return [+u, +v, Math.sqrt(u * u + v * v)];
  },
  //二分差值算法计算给定节点的速度
  _bilinearInterpolation: function (x, y, g00, g10, g01, g11) {
    var rx = 1 - x;
    var ry = 1 - y;
    var a = rx * ry,
      b = x * ry,
      c = rx * y,
      d = x * y;
    var u = g00[0] * a + g10[0] * b + g01[0] * c + g11[0] * d;
    var v = g00[1] * a + g10[1] * b + g01[1] * c + g11[1] * d;
    return this._calcUV(u, v);
  },
  getIn: function (x, y) {
    if (x < 0 || x >= 359 || y >= 180) {
      return [0, 0, 0];
    }
    var x0 = Math.floor(x),
      y0 = Math.floor(y),
      x1,
      y1;
    if (x0 === x && y0 === y) return this.grid[y][x];

    x1 = x0 + 1;
    y1 = y0 + 1;

    var g00 = this.getIn(x0, y0),
      g10 = this.getIn(x1, y0),
      g01 = this.getIn(x0, y1),
      g11 = this.getIn(x1, y1);
    var result = null;
    try {
      result = this._bilinearInterpolation(x - x0, y - y0, g00, g10, g01, g11);
    } catch (e) {
      console.log(x, y);
    }
    return result;
  },
  isInBound: function (x, y) {
    if (x >= 0 && x < this.cols - 1 && y >= 0 && y < this.rows - 1) return true;
    return false;
  },
};

/****
 *粒子对象
 ****/
var CanvasParticle = function () {
  this.lng = null; //粒子初始经度
  this.lat = null; //粒子初始纬度
  this.x = null; //粒子初始x位置(相对于棋盘网格,比如x方向有360个格,x取值就是0-360,这个是初始化时随机生成的)
  this.y = null; //粒子初始y位置(同上)
  this.tlng = null; //粒子下一步将要移动的经度,这个需要计算得来
  this.tlat = null; //粒子下一步将要移动的y纬度,这个需要计算得来
  this.age = null; //粒子生命周期计时器,每次-1
  this.speed = null; //粒子移动速度,可以根据速度渲染不同颜色
};

风流场类的调用:


//风场显示
const showWindy = function () {
  $('#windycanvas').show();
};
//风场隐藏
const hideWindy = function () {
  $('#windycanvas').hide();
};
//设置地图操作,便于使用风场
const initWindy = (windy) => {
  /**
  *如果处于全球状态就设置为[](只要有一个角获取不到坐标就表示全球状态,实时计算)
  **/
  var globalExtent = [];

  //获取当前三维窗口左上、右上、左下、右下坐标
  var getCesiumExtent = function () {
    var canvaswidth = window.innerWidth,
      canvasheight = window.innerHeight - 50;

    var left_top_pt = new Cesium.Cartesian2(0, 0);
    var left_bottom_pt = new Cesium.Cartesian2(0, canvasheight);
    var right_top_pt = new Cesium.Cartesian2(canvaswidth, 0);
    var right_bottom_pt = new Cesium.Cartesian2(canvaswidth, canvasheight);

    var pick1 = viewer.scene.globe.pick(viewer.camera.getPickRay(left_top_pt), viewer.scene);
    var pick2 = viewer.scene.globe.pick(viewer.camera.getPickRay(left_bottom_pt), viewer.scene);
    var pick3 = viewer.scene.globe.pick(viewer.camera.getPickRay(right_top_pt), viewer.scene);
    var pick4 = viewer.scene.globe.pick(viewer.camera.getPickRay(right_bottom_pt), viewer.scene);
    if (pick1 && pick2 && pick3 && pick4) {
      //将三维坐标转成地理坐标---只需计算左下右上的坐标即可
      var geoPt1 = viewer.scene.globe.ellipsoid.cartesianToCartographic(pick2);
      var geoPt2 = viewer.scene.globe.ellipsoid.cartesianToCartographic(pick3);
      //地理坐标转换为经纬度坐标
      var point1 = [geoPt1.longitude / Math.PI * 180, geoPt1.latitude / Math.PI * 180];
      var point2 = [geoPt2.longitude / Math.PI * 180, geoPt2.latitude / Math.PI * 180];
      // console.log(point1,point2);
      //此时说明extent需要分为东西半球
      if (point1[0] > point2[0]) {
        globalExtent = [point1[0], 180, point1[1], point2[1], -180, point2[0], point1[1], point2[1]];
      } else {
        globalExtent = [point1[0], point2[0], point1[1], point2[1]];
      }
    } else {
      globalExtent = [];
    }
  };
  // 开启监听器--无论对当前地球做的任何操作都会监听到
  let postRender = viewer.scene.postRender.addEventListener(() => {
    getCesiumExtent();
  });
  var refreshTimer = -1;
  var mouse_down = false;
  var mouse_move = false;
  var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
  //鼠标滚动、旋转后是否需要重新生成风场---如果需要,打开以下注释--旋转或者移动到北半球的时候计算会有问题
  handler.setInputAction(function (e) {
    console.log(windy);
    clearTimeout(refreshTimer);
    //每次鼠标滚轮旋转后不显示风场
    // hideWindy();
    setTimeout(function () {
      windy.extent = globalExtent;
      windy.redraw();
      showWindy();
    }, 200);
  }, Cesium.ScreenSpaceEventType.WHEEL);
  //鼠标左键、右键按下
  handler.setInputAction(function (e) {
    mouse_down = true;
  }, Cesium.ScreenSpaceEventType.LEFT_DOWN);
  handler.setInputAction(function (e) {
    mouse_down = true;
  }, Cesium.ScreenSpaceEventType.RIGHT_DOWN);
  //鼠标移动
  handler.setInputAction(function (e) {
    if (mouse_down) {
      //每次鼠标移动后不显示风场
      // hideWindy();
      mouse_move = true;
    }
  }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
  //鼠标左键、右键抬起
  handler.setInputAction(function (e) {
    if (mouse_down && mouse_move) {
      windy.extent = globalExtent;
      windy.redraw();
    }
    showWindy();
    mouse_down = false;
    mouse_move = false;
  }, Cesium.ScreenSpaceEventType.LEFT_UP);
  handler.setInputAction(function (e) {
    if (mouse_down && mouse_move) {
      windy.extent = globalExtent;
      windy.redraw();
    }
    showWindy();
    mouse_down = false;
    mouse_move = false;
  }, Cesium.ScreenSpaceEventType.RIGHT_UP);

}


//调整风场canvas大小
const resizeCanvas = function (windy) {
  if (windycanvas == null) {
    return;
  }
  windycanvas.width = window.innerWidth;
  windycanvas.height = window.innerHeight;
  console.log(windycanvas.width, windycanvas.height);
  if (windy) {
    windy._resize(windycanvas.width, windycanvas.height);
  }
};

//获取风场数据、生成风场
let getwindData = () => {
  // let Data_Promise = Service(`http://139.9.141.68:11111/weather_data/sn/grid/20220818180000/108.5000/109.0000/33.5000/34.0000`)
  let Data_Promise = axios(`/data/jsonData/data.json`)
  Data_Promise.then((res) => {

    console.log('风的格点预报:', res.data);
    //设置canvas
    var windycanvas = document.createElement('canvas');
    windycanvas.setAttribute("id", "windycanvas");
    windycanvas.style.position = 'fixed'
    windycanvas.style["pointer-events"] = "none";
    windycanvas.style["z-index"] = 10;
    windycanvas.style["bottom"] = 0;
    windycanvas.style["top"] = 0;
    windycanvas.style["left"] = 0;
    windycanvas.style["right"] = 0;
    document.getElementById('cesiumContainer').appendChild(windycanvas);

    resizeCanvas();
    window.onresize = resizeCanvas;

    //风场参数设置
    let params = {
      viewer: viewer,
      canvas: windycanvas,
      canvasWidth: window.innerWidth,
      canvasHeight: window.innerHeight,
      speedRate: 5000,
      particlesNumber: 5000,
      maxAge: 120,
      frameRate: 10,
      color: '#3a92ff',
      lineWidth: 3,
    };

    var windy
    setTimeout(() => {
      windy = new CanvasWindy(res.data, params);
      showWindy()
      //初始化风场设置
      initWindy(windy)
      console.log('风场已经执行');
    }, 2000);
  })
}

风流场工具类(如果只需要显示风流场则不需要):

mport $ from 'jquery'
import {CanvasWindy} from "./CanvasWindy";
import {Cartesian2,ScreenSpaceEventType,ScreenSpaceEventHandler} from 'cesium/Cesium'
// export let wa={}
let windyUtil = (function () {
  function windyUtil (zdhCesium,layerId) {
    this.zdhCesium = zdhCesium
    this.globalExtent = []
    this.canvasId = layerId
    this.postRender = null
    this.handler = null
    this.allgrid = null
    this.windData = null
    this.windy = null
    this.windycanvas = null
    this.WindInformation = {
      block_show:false,           //模块化显示
      Wind_speed:0,               //风速
      wind_direction:"无",         //风向
      wind_field:[]               //差值后的向量
    }
    this.windyVisible = true
  }
  windyUtil.prototype.showWindy = function(){
    this.windyVisible = true
    $('#' + this.canvasId).show()
    this.windy.extent = this.globalExtent;
    this.windy.redraw();
  }
  windyUtil.prototype.hideWindy = function(){
    this.windyVisible = false
    $('#' + this.canvasId).hide()
  }
  windyUtil.prototype.getCesiumExtent = function(){
    let canvaswidth = window.innerWidth,
      canvasheight = window.innerHeight*0.5-50;

    let left_top_pt = new Cartesian2(0,0)
    let left_bottom_pt = new Cartesian2(0,canvasheight)
    let right_top_pt = new Cartesian2(canvaswidth,0)
    let right_bottom_pt = new Cartesian2(canvaswidth,canvasheight)

    let pick1= this.zdhCesium.viewer.scene.globe.pick(this.zdhCesium.viewer.camera.getPickRay(left_top_pt), this.zdhCesium.viewer.scene)
    let pick2= this.zdhCesium.viewer.scene.globe.pick(this.zdhCesium.viewer.camera.getPickRay(left_bottom_pt), this.zdhCesium.viewer.scene)
    let pick3= this.zdhCesium.viewer.scene.globe.pick(this.zdhCesium.viewer.camera.getPickRay(right_top_pt), this.zdhCesium.viewer.scene)
    let pick4= this.zdhCesium.viewer.scene.globe.pick(this.zdhCesium.viewer.camera.getPickRay(right_bottom_pt), this.zdhCesium.viewer.scene)
    if(pick1 && pick2 && pick3 && pick4){
      //将三维坐标转成地理坐标---只需计算左下右上的坐标即可
      let geoPt1= this.zdhCesium.viewer.scene.globe.ellipsoid.cartesianToCartographic(pick2)
      let geoPt2= this.zdhCesium.viewer.scene.globe.ellipsoid.cartesianToCartographic(pick3)
      //地理坐标转换为经纬度坐标
      let point1=[geoPt1.longitude / Math.PI * 180,geoPt1.latitude / Math.PI * 180]
      let point2=[geoPt2.longitude / Math.PI * 180,geoPt2.latitude / Math.PI * 180]
      // console.log(point1,point2);
      //此时说明extent需要分为东西半球
      if(point1[0]>point2[0]){
        this.globalExtent = [point1[0],180,point1[1],point2[1],-180,point2[0],point1[1],point2[1]];
      }else{
        this.globalExtent = [point1[0],point2[0],point1[1],point2[1]];
      }
    }else{
      this.globalExtent = []
    }
  }
  windyUtil.prototype.resizeCanvas = function(){
    if(this.windycanvas==null){
      return;
    }
    this.windycanvas.width=window.innerWidth;
    this.windycanvas.height=window.innerHeight;
    // console.log(windycanvas.width,windycanvas.height);
    if(this.windy){
      this.windy._resize(this.windycanvas.width,this.windycanvas.height);
    }
  }
  windyUtil.prototype.createWindLayer = function (windData) {
    if (this.windycanvas != null){
      return
    }
    let _this = this
    // 开启监听器--无论对当前地球做的任何操作都会监听到
    this.postRender = this.zdhCesium.viewer.scene.postRender.addEventListener(() => {
      _this.getCesiumExtent();
    });
    this.addWindHandle()
    if (this.windycanvas == null){
      this.windycanvas = document.createElement('canvas');
      this.windycanvas.setAttribute("id", this.canvasId);
      this.windycanvas.style.position='absolute'
      this.windycanvas.style["pointer-events"]="none";
      this.windycanvas.style["z-index"]=10;
      this.windycanvas.style["top"]=0;
      this.windycanvas.style["right"]=0;
      document.getElementById('cesiumContainer1').appendChild(this.windycanvas);
    }
    this.resizeCanvas();
    window.οnresize=this.resizeCanvas;
    //风场的参数配置,除了canvas/viewer是必传项,其他可以不传,参数含义见windy.js
    let params = {
      viewer:this.zdhCesium.viewer,
      canvas:this.windycanvas,
      canvasWidth:window.innerWidth,
      canvasHeight:window.innerHeight,
      speedRate:5000,
      particlesNumber:5000,
      maxAge:120,
      frameRate:10,
      color:'#ffffff',
      lineWidth:1,
    };
    this.windData = windData
    this.analysisWindyData(this.windData)
    this.windy = new CanvasWindy(this.windData, params);
  }
  windyUtil.prototype.addWindHandle = function () {
    let _this = this
    let refreshTimer = -1;
    let mouse_down = false;
    let mouse_move = false;
    this.handler = new ScreenSpaceEventHandler(this.zdhCesium.viewer.scene.canvas);
    //鼠标滚动、旋转后是否需要重新生成风场---如果需要,打开以下注释--旋转或者移动到北半球的时候计算会有问题
    this.handler.setInputAction(function(e) {
      if(_this.windyVisible === true){
        clearTimeout(refreshTimer);
        $('#' + _this.canvasId).hide()
        // _this.hideWindy();
        setTimeout(function(){
          _this.windy.extent = _this.globalExtent;
          _this.windy.redraw();
          $('#' + _this.canvasId).show()
          // _this.showWindy();
        },200);
      }
    },ScreenSpaceEventType.WHEEL);
    //鼠标左键、右键按下
    this.handler.setInputAction(function(e) {
      mouse_down = true;
    },ScreenSpaceEventType.LEFT_DOWN);
    this.handler.setInputAction(function(e) {
      mouse_down = true;
    },ScreenSpaceEventType.RIGHT_DOWN);
    //鼠标移动
    this.handler.setInputAction(function(e) {
      if(_this.windyVisible === true){
        if(mouse_down){
          $('#' + _this.canvasId).hide()
          // _this.hideWindy();
          mouse_move = true;
        }
      }

    },ScreenSpaceEventType.MOUSE_MOVE);
    //鼠标左键、右键抬起
    this.handler.setInputAction(function(e) {
      if(_this.windyVisible === true){
        if(mouse_down && mouse_move){
          _this.windy.extent = _this.globalExtent;
          _this.windy.redraw();
        }
        $('#' + _this.canvasId).show()
        // _this.showWindy();
        mouse_down = false;
        mouse_move = false;
      }
    },ScreenSpaceEventType.LEFT_UP);
    this.handler.setInputAction(function(e) {
      if(_this.windyVisible === true){
        if(mouse_down && mouse_move){
          _this.windy.extent = _this.globalExtent;
          _this.windy.redraw();
        }
        $('#' + _this.canvasId).show()
        // _this.showWindy();
        mouse_down = false;
        mouse_move = false;
      }

    },ScreenSpaceEventType.RIGHT_UP);
  }
  //根据光标推算风场数据函数
  windyUtil.prototype.GetWindInformation = function (x, y, width, height) {
    let Wind_speed = 0
    let wind_direction = "无"
    let mycolumns = this.WindInformation.wind_field
    if (mycolumns.length !== 0) {
      let columns_x = mycolumns.length
      let columns_y = mycolumns[0].length
      // console.log(x+","+y+"")
      let i = Math.floor(x / (width / columns_x))
      let j = Math.floor(y / (height / columns_y))
      let myfield = mycolumns[i][j]
      if (myfield.length == 3) {
        Wind_speed = myfield[2].toFixed(2)
      } else if (myfield.length == 2) {
        Wind_speed = Math.sqrt(myfield[0] * myfield[0] + myfield[1] * myfield[1]).toFixed(2)
      }
      wind_direction = direction_calculation(myfield[0], myfield[1])
      this.WindInformation.Wind_speed = Wind_speed
      this.WindInformation.wind_direction = wind_direction
    } else {
      this.WindInformation.Wind_speed = 0
      this.WindInformation.wind_direction = "无"
    }

    function direction_calculation(u, v) {
      let fx = 0
      // let v = v2 * (-1)
      let text = "无"
      if (u == 0) {
        if (v > 0) {
          text = "南风"
        } else if (v < 0) {
          text = "北风"
        }
      } else if (v == 0) {
        if (u > 0) {
          text = "西风"
        } else if (u < 0) {
          text = "东风"
        }
      } else {
        if (u > 0 & v > 0) {//第一象限
          // fx = Math.atan(v / u) * 180 / Math.PI
          fx = 270 - Math.atan(v / u) * 180 / Math.PI
          if (fx <= 191.25) {
            text = "南风"
          } else if (fx > 191.25 && fx < 258.76) {
            text = "西南风"
          } else {
            text = "西风"
          }
          // if (fx <= 11.25) {
          //   text = "南风"
          // } else if (fx > 11.25 && fx < 78.76) {
          //   text = "西南风"
          // } else {
          //   text = "西风"
          // }
        }
        else if (u > 0 & v < 0) {//第二象限
          // fx = 180 + Math.atan(v / u) * 180 / Math.PI
          fx = 270 - Math.atan(v / u) * 180 / Math.PI
          if (fx <= 281.25) {
            text = "西风"
          } else if (fx > 281.25 && fx < 348.76) {
            text = "西北风"
          } else {
            text = "北风"
          }
          // if (fx <= 101.25) {
          //   text = "西风"
          // } else if (fx > 101.25 && fx < 168.76) {
          //   text = "西北风"
          // } else {
          //   text = "北风"
          // }
        }
        else if (u < 0 & v < 0) {//第三象限
          // fx = 180 + Math.atan(v / u) * 180 / Math.PI
          fx = 90 - Math.atan(v / u) * 180 / Math.PI
          if (fx <= 11.25) {
            text = "北风"
          } else if (fx > 11.25 && fx < 78.76) {
            text = "东北风"
          } else {
            text = "东风"
          }
          // if (fx <= 191.25) {
          //   text = "东风"
          // } else if (fx > 191.25 && fx < 258.76) {
          //   text = "东北风"
          // } else {
          //   text = "北风"
          // }
        }
        else if (u < 0 & v > 0) {//第四象限
          // fx = 360 + Math.atan(v / u) * 180 / Math.PI
          fx = 90 - Math.atan(v / u) * 180 / Math.PI
          if (fx <= 101.25) {
            text = "东风"
          } else if (fx > 101.25 && fx < 168.76) {
            text = "东南风"
          } else {
            text = "南风"
          }
        }
      }
      return text
    }
  }

  windyUtil.prototype.analysisWindyData = function(windydata) {
    this.allgrid = []
    let p = 0
    let east, north
    if (windydata[0].header.parameterNumberName == "eastward_wind") {
      east = windydata[0]
      north = windydata[1]
    } else {
      east = windydata[1]
      north = windydata[0]
    }
    for (let j = 0; j < north.header.ny; j++) {
      let row = []
      for (let i = 0; i < north.header.nx; i++, p++) {
        row[i] = [east.data[p], north.data[p]]
      }
      this.allgrid[j] = row
    }
  }
  windyUtil.prototype.getWindyDetail = function(coord) {
    let lng = coord[0]
    let lat = coord[1]
    // 与格网序列的数据转换
    // if (lng >= 0) {
    //   lng = Math.floor(lng)
    // } else {
    //   lng = 360 + Math.floor(lng)
    // }
    // lat = 90 - Math.floor(lat)
    // 获取对应的格网序列(此示例为中国区域0.25度规格)
    let xlength = Math.floor((coord[0]-70) / 0.25)
    let ylength = Math.floor((55-coord[1]) / 0.25)
    let xdata, ydata
    xdata = parseFloat(this.allgrid[Math.abs(ylength)][Math.abs(xlength)][0])
    ydata = parseFloat(this.allgrid[Math.abs(ylength)][Math.abs(xlength)][1])
    debugger
    let feng=this.direction_calculation(xdata,ydata)
    if (typeof xdata != "number" || typeof ydata != "number") {
      console.error("暂无该区域风向数据!")
      return
    }
    let v = Math.sqrt(Math.pow(xdata, 2) + Math.pow(ydata, 2))
    let angle = this.getWindyAngle(xdata, ydata)
    let result = {
      "direction": feng,
      "level": this.getWindyLevel(v),
      "speed": v.toFixed(2)
    }
    return result
  }
  windyUtil.prototype.direction_calculation = function (u, v) {
    let fx = 0
    // let v = v2 * (-1)
    let text = "无"
    if (u == 0) {
      if (v > 0) {
        text = "南风"
      } else if (v < 0) {
        text = "北风"
      }
    } else if (v == 0) {
      if (u > 0) {
        text = "西风"
      } else if (u < 0) {
        text = "东风"
      }
    } else {
      if (u > 0 & v > 0) {//第一象限
        // fx = Math.atan(v / u) * 180 / Math.PI
        fx = 270 - Math.atan(v / u) * 180 / Math.PI
        if (fx <= 191.25) {
          text = "南风"
        } else if (fx > 191.25 && fx < 258.76) {
          text = "西南风"
        } else {
          text = "西风"
        }
        // if (fx <= 11.25) {
        //   text = "南风"
        // } else if (fx > 11.25 && fx < 78.76) {
        //   text = "西南风"
        // } else {
        //   text = "西风"
        // }
      }
      else if (u > 0 & v < 0) {//第二象限
        // fx = 180 + Math.atan(v / u) * 180 / Math.PI
        fx = 270 - Math.atan(v / u) * 180 / Math.PI
        if (fx <= 281.25) {
          text = "西风"
        } else if (fx > 281.25 && fx < 348.76) {
          text = "西北风"
        } else {
          text = "北风"
        }
        // if (fx <= 101.25) {
        //   text = "西风"
        // } else if (fx > 101.25 && fx < 168.76) {
        //   text = "西北风"
        // } else {
        //   text = "北风"
        // }
      }
      else if (u < 0 & v < 0) {//第三象限
        // fx = 180 + Math.atan(v / u) * 180 / Math.PI
        fx = 90 - Math.atan(v / u) * 180 / Math.PI
        if (fx <= 11.25) {
          text = "北风"
        } else if (fx > 11.25 && fx < 78.76) {
          text = "东北风"
        } else {
          text = "东风"
        }
        // if (fx <= 191.25) {
        //   text = "东风"
        // } else if (fx > 191.25 && fx < 258.76) {
        //   text = "东北风"
        // } else {
        //   text = "北风"
        // }
      }
      else if (u < 0 & v > 0) {//第四象限
        // fx = 360 + Math.atan(v / u) * 180 / Math.PI
        fx = 90 - Math.atan(v / u) * 180 / Math.PI
        if (fx <= 101.25) {
          text = "东风"
        } else if (fx > 101.25 && fx < 168.76) {
          text = "东南风"
        } else {
          text = "南风"
        }
      }
    }
    return text
  }

  windyUtil.prototype.getWindyDirection = function(angle) {
    if ((angle >= 0 && angle <= 22.5) || (angle <= 360 && angle > 337.5)) {
      return "东风"
    }
    if (angle <= 337.5 && angle > 292.5) {
      return "西北风"
    }
    if (angle <= 292.5 && angle > 247.5) {
      return "北风"
    }
    if (angle <= 247.5 && angle > 202.5) {
      return "西南风"
    }
    if (angle <= 202.5 && angle > 157.5) {
      return "东风"
    }
    if (angle <= 157.5 && angle > 112.5) {
      return "东南风"
    }
    if (angle <= 112.5 && angle > 67.5) {
      return "南风"
    }
    if (angle <= 67.5 && angle > 22.5) {
      return "西南风"
    }
  }
  windyUtil.prototype.getWindyAngle = function(u, v) {
    let fx = 0
    if (u > 0 & v > 0) {
      fx = 90 - Math.atan(v / u) * 180 / Math.PI
    } else if (u < 0 & v > 0) {
      fx = 90 - Math.atan(v / u) * 180 / Math.PI
    } else if (u < 0 & v < 0) {
      fx = 270 - Math.atan(v / u) * 180 / Math.PI
    } else if (u > 0 & v < 0) {
      fx = 270 - Math.atan(v / u) * 180 / Math.PI
    } else if (u == 0 & v > 0) {
      fx = 180
    } else if (u == 0 & v < 0) {
      fx = 0
    } else if (u > 0 & v == 0) {
      fx = 270
    } else if (u < 0 & v == 0) {
      fx = 90
    } else if (u == 0 & v == 0) {
      fx = 999.9
    }
    return fx
  }
  windyUtil.prototype.getWindyLevel = function(v) {
    if (v < 0.3) {
      return 0
    }
    if (v >= 0.3 && v < 1.6) {
      return 1
    }
    if (v >= 1.6 && v < 3.4) {
      return 2
    }
    if (v >= 3.4 && v < 5.5) {
      return 3
    }
    if (v >= 5.5 && v < 8.0) {
      return 4
    }
    if (v >= 8.0 && v < 10.8) {
      return 5
    }
    if (v >= 10.8 && v < 13.9) {
      return 6
    }
    if (v >= 13.9 && v < 17.2) {
      return 7
    }
    if (v >= 17.2 && v < 20.8) {
      return 8
    }
    if (v >= 20.8 && v < 24.5) {
      return 9
    }
    if (v >= 24.5 && v < 28.5) {
      return 10
    }
    if (v >= 28.5 && v < 32.7) {
      return 11
    }
    if (v >= 32.7 && v < 37.0) {
      return 12
    }
    if (v >= 37.0 && v < 41.5) {
      return 13
    }
    if (v >= 41.5 && v < 46.2) {
      return 14
    }
    if (v >= 46.2 && v < 51.0) {
      return 15
    }
    if (v >= 51.0 && v < 56.1) {
      return 16
    }
    if (v >= 56.1 && v < 61.2) {
      return 17
    }
    if (v >= 61.2) {
      return 18
    }
  }
  return windyUtil
}())
export {windyUtil}

风流场工具类调用:

import {WindyUtil} from "./gisUtils/WindyUtil.js";
let windyUtil = new WindyUtil(this.smap)
windyUtil.addWindyLayer(windData)
var details = windyUtil.getWindyDetail(coordinate);
console.log(details);
alert(' 风向:' + details.direction + '\n 风级:' + details.level + '\n 风速:' + details.speed)

风流场数据格式:

风向分为 U向 和 V向

u表示经度方向上的风,v表示纬度方向上的风。

u为正,表示西风,从西边吹来的风。v为正,表示南风,从南边从来的风。假如u为1,v为1,则表示西南风。

data中数据个数应为 nx * ny 个
“dx”: 0.25,//网格横向间距
“dy”: 0.25,//网格纵向间距
“parameterUnit”: “m.s-1”,//风速单位
“lo1”: 70.0,// 起点经纬度
“lo2”: 140.0,
“la1”: 55.0,
“la2”: 0.0,

[{
    "header": {
      "productDefinitionTemplate": 0,
      "surface1Type": 100,
      "productStatus": 0,
      "disciplineName": "Meteorological products",
      "subDivisions": 0,
      "winds": "true",
      "parameterNumberName": "U-component_of_wind",//风向
      "discipline": 0,
      "surface2Value": 0,
      "significanceOfRTName": "Start of forecast",
      "surface2TypeName": "Missing",
      "surface2Type": 255,
      "productTypeName": "Forecast products",
      "dx": 0.25,//网格横向间距
      "dy": 0.25,//网格纵向间距
      "numberPoints": 62101,
      "productStatusName": "Operational products",
      "gribEdition": 2,
      "parameterUnit": "m.s-1",//风速单位
      "forecastTime": 9,
      "refTime": "2022.01.11 00:00:00 UTC",
      "productType": 1,
      "genProcessTypeName": "Forecast",
      "scanMode": 0,
      "genProcessType": 2,
      "gribLength": 132662,
      "significanceOfRT": 1,
      "productDefinitionTemplateName": "Analysis/forecast at horizontal level/layer at a point in time",
      "centerName": "US National Weather Service - NCEP(WMC)",
      "lo1": 70.0,//起点经纬度
      "lo2": 140.0,
      "la1": 55.0,//终点经纬度
      "la2": 0.0,
      "gridUnits": "degrees",
      "surface1Value": 50000,
      "shapeName": "Earth spherical with radius of 6,371,229.0 m",
      "surface1TypeName": "Isobaric surface",
      "nx": 281,//有多少列
      "ny": 221,//有多少行
      "basicAngle": 0,
      "gridDefinitionTemplate": 0,
      "shape": 6,
      "parameterNumber": 2,
      "subcenter": 0,
      "parameterCategoryName": "Momentum",
      "parameterCategory": 2,
      "center": 7,
      "gridDefinitionTemplateName": "Latitude_Longitude",
      "resolution": 48
    },
    "meta": { "date": "2022.01.11 00:00:00 UTC" },
    "data": [
      "0.72",
      "0.33",
      "0.04",
      "-0.19",
      "-0.30",
      "-0.44",
        ...
      "1.05",
      "0.18",
      "0.09",
      "0.08",
      "0.08",
      "-0.08"
    ]
  },
{
    "header": {
      "productDefinitionTemplate": 0,
      "surface1Type": 100,
      "productStatus": 0,
      "disciplineName": "Meteorological products",
      "subDivisions": 0,
      "winds": "true",
      "parameterNumberName": "V-component_of_wind",
      "discipline": 0,
      "surface2Value": 0,
      "significanceOfRTName": "Start of forecast",
      "surface2TypeName": "Missing",
      "surface2Type": 255,
      "productTypeName": "Forecast products",
      "dx": 0.25,
      "dy": 0.25,
      "numberPoints": 62101,
      "productStatusName": "Operational products",
      "gribEdition": 2,
      "parameterUnit": "m.s-1",
      "forecastTime": 9,
      "refTime": "2022.01.11 00:00:00 UTC",
      "productType": 1,
      "genProcessTypeName": "Forecast",
      "scanMode": 0,
      "genProcessType": 2,
      "gribLength": 132662,
      "significanceOfRT": 1,
      "productDefinitionTemplateName": "Analysis/forecast at horizontal level/layer at a point in time",
      "centerName": "US National Weather Service - NCEP(WMC)",
      "la1": 55.0,
      "la2": 0.0,
      "lo2": 140.0,
      "gridUnits": "degrees",
      "surface1Value": 50000,
      "shapeName": "Earth spherical with radius of 6,371,229.0 m",
      "surface1TypeName": "Isobaric surface",
      "nx": 281,
      "ny": 221,
      "lo1": 70.0,
      "basicAngle": 0,
      "gridDefinitionTemplate": 0,
      "shape": 6,
      "parameterNumber": 3,
      "subcenter": 0,
      "parameterCategoryName": "Momentum",
      "parameterCategory": 2,
      "center": 7,
      "gridDefinitionTemplateName": "Latitude_Longitude",
      "resolution": 48
    },
    "meta": { "date": "2022.01.11 00:00:00 UTC" },
    "data": [
      "-2.45",
      "-2.16",
      "-2.09",
      "-1.99",
     ...
      "-3.91",
      "-3.56",
      "-3.81",
      "-3.79",
      "-3.67",
      "-3.62",
      "-3.91"
    ]
  }
]

;