vue+cesium实现风场

一、风场类编写

/***
 * 风场类
 * date:2022-03-14
 * author:zdh
 * @type {CanvasWindy}
 */
import {Cartesian3,EllipsoidalOccluder,SceneTransforms,Ellipsoid,Cartesian2,Math as CMath} from 'cesium/Cesium'
export let 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 () {
    let 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 (let 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();

    let then = Date.now();
    (function frame() {
      if(!self.isdistory){
        self.animateFrame = requestAnimationFrame(frame);
        let now = Date.now();
        let delta = now - then;
        if (delta > self.frameTime) {
          then = now - delta % self.frameTime;
          self.animate();
        }
      }else{
        self.removeLines();
      }
    })();
  },
  //计算经纬度步进长度
  _calcStep:function(){
    let isextent = (this.extent.length!=0);
    let calcExtent = isextent?this.extent:this.initExtent;
    let 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 () {
    let data = this._parseWindJson();
    return new CanvasWindField(data);
  },
  animate: function () {
    let self = this,
      field = self.windField;
    let nextLng = null,
      nextLat = null,
      uv = null;
    self.particles.forEach(function (particle) {
      if (particle.age == null || particle.age <= 0) {
        self.randomParticle(particle);
      }
      if (particle.age > 0) {
        let x = particle.x,
          y = particle.y,
          tlng = particle.tlng,
          tlat = particle.tlat;
        let gridpos = self._togrid(tlng,tlat);
        let tx = gridpos[0];
        let 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){
    let 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 () {
    let uComponent = null,
      vComponent = null,
      header = null;
    this.windData.forEach(function (record) {
      let 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) {
    let ct3 = Cartesian3.fromDegrees(lng,lat,0);
    // 判断当前点是否在地球可见端
    let isVisible = new EllipsoidalOccluder(Ellipsoid.WGS84, this.viewer.camera.position).isPointVisible(ct3);
    let pos = 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) {
    let field = this.windField;
    let x = (lng-this.initExtent[0])/(this.initExtent[1]-this.initExtent[0])*(field.cols-1);
    let y = (this.initExtent[3]-lat)/(this.initExtent[3]-this.initExtent[2])*(field.rows-1);
    return [x,y];
  },
  _drawLines: function () {
    let self = this;
    let 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) {
      let movetopos = self._tomap(particle.lng, particle.lat,particle);
      let 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) {
    let safe = 30,x=-1, y=-1,lng=null,lat=null;
    let hasextent = this.extent.length!=0;
    let calc_extent = hasextent?this.extent:this.initExtent;
    try{
      do {
        try{
          if(hasextent){
            let pos_x = this.fRandomBy(0,this.canvasWidth);
            let pos_y = this.fRandomBy(0,this.canvasHeight);
            let cartesian = this.viewer.camera.pickEllipsoid(new Cartesian2(pos_x, pos_y), this.viewer.scene.globe.ellipsoid);
            let cartographic = this.viewer.scene.globe.ellipsoid.cartesianToCartographic(cartesian);
            if(cartographic){
              //将弧度转为度的十进制度表示
              lng = CMath.toDegrees(cartographic.longitude);
              lat = CMath.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){
          let gridpos = this._togrid(lng,lat);
          x = gridpos[0];
          y = gridpos[1];
        }
      } while (this.windField.getIn(x, y)[2] <= 0 && safe++ < 30);
    }catch (e) {
    }
    try{
      let field = this.windField;
      let uv = field.getIn(x, y);
      particle.speed = uv[2];
      particle.lng = lng;
      particle.lat = lat;
      particle.x = x;
      particle.y = y;
      let nextLng = lng +  this.calc_speedRate[0] * uv[0];
      let nextLat = lat +  this.calc_speedRate[1] * uv[1];
      particle.tlng = nextLng;
      particle.tlat = nextLat;
      particle.age = Math.round(Math.random() * this.maxAge);//每一次生成都不一样

    }
    catch (e) {

    }
    return particle;
  }
};


/****
 *棋盘类
 *根据风场数据生产风场棋盘网格
 ****/
let 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) {
    let header = obj.header,
      uComponent = obj['uComponent'],
      vComponent = obj['vComponent'];
    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 = [];
    let k = 0,
      rows = null,
      uv = null;
    for (let j = 0; j < this.rows; j++) {
      rows = [];
      for (let 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) {
    let rx = (1 - x);
    let ry = (1 - y);
    let a = rx * ry, b = x * ry, c = rx * y, d = x * y;
    let u = g00[0] * a + g10[0] * b + g01[0] * c + g11[0] * d;
    let 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];
    }
    let 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;

    let g00 = this.getIn(x0, y0),
      g10 = this.getIn(x1, y0),
      g01 = this.getIn(x0, y1),
      g11 = this.getIn(x1, y1);
    let 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;
  }
};


/****
 *粒子对象
 ****/
let 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;//粒子移动速度,可以根据速度渲染不同颜色
};


二、风场工具类编写

/***
 * 风场通用工具
 * date:2022-03-14
 * author:zdh
 * @type {windyUtil}
 */
import $ 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}


三、调用代码编写

let windyUtil1 = new windyUtil(zdhCesium,layerId)
windyUtil1.createWindLayer(res)
if(layersInfo[layerId].options.isShow == false){
  windyUtil1.hideWindy()
}
layersInfo[layerId].entities.push(windyUtil1)


四、成果展示


————————————————
版权声明:本文为CSDN博主「兴诚」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42496466/article/details/123501757