1. 程式人生 > 其它 >風場視覺化學習筆記:openlayers

風場視覺化學習筆記:openlayers

最近在弄地圖控制元件方面的東西,這裡分享一個我找到的一個新增風場的教程和demo,需要對大家有所幫助(以下為轉載內容)載於https://blog.csdn.net/u010065726/article/details/106338194/

windLayer api使用示例

這裡主要為windlayer關於在openlayers3-4版本內使用的相關介面及引數說明

在載入時,主要讀取的資料來源格式為json,而我們一般獲取到的資料主要為netCDF或Grib等格式的資料,這裡還涉及相關的資料轉換操作

1.openlayers-wind引數說明

TIP

對應於 openlayers 3-4 相關引數

圖層引數

其他引數遵循ol

基礎圖層引數。

windOptions

colorScale

關於顏色配置,在以往的配置中傳入的是顏色陣列會根據以下函式和格點資料的資料範圍去計算匹配的顏色值,

const indexFor = function (m) {  // map velocity speed to a style
    return Math.max(0, Math.min((that.COLOR_SCALE.length - 1),
      Math.round((m - min) / (max - min) * (that.COLOR_SCALE.length - 1))));
    
}

這樣實現只能按照風速值範圍等間隔渲染,無法做到精確匹配對應值的顏色。

在最新的版本中新增了此引數的型別,可以通過回撥函式精確對應顏色值(但是會有一定的效能損失)

顏色配置支援三種方式:

String:固定顏色值

Function: 通過回撥函式的風速值設定顏色(但是會有一定的效能損失)

String[]: 按照風速值範圍等間隔渲染,無法做到精確匹配對應值的顏色。

2.資料來源
風場資料來源主要為氣象的資料檔案,可以詳見另一篇文章 windy網站資料分析 。文章中提到的windy基本涵蓋了大部分的氣象資料來源,大家可以參考一下。

在使用wind-layer需要了解相關的資料轉換,大家可以多下下功夫,有什麼內容也可以分享出來,大家互相學習下。

3.圖層初始化並格網分析資料來源
呼叫相關的API介面,新增相應的圖層物件

demo程式碼如下:

<!DOCTYPE html>
<html>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>
    風向demo
</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/build/dat.gui.css">
<script src="https://cdn.jsdelivr.net/npm/openlayers/dist/ol.js">
</script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/openlayers/dist/ol.css">
<script src="https://cdn.jsdelivr.net/npm/openlayers-wind/dist/ol-wind.js">
</script>
<style type="text/css">
    html, body { margin: 0; height: 100%; width: 100% }
    .container { width: 100%; height: 100% }
</style>
<body>
<div id="map" class="container">
</div>
<script>
  const map = new ol.Map({
    target: 'map',
    view: new ol.View({
      center: [113.53450137499999, 34.44104525],
      // center: ol.proj.fromLonLat([113.53450137499999, 34.44104525]),
      zoom: 5,
      projection: 'EPSG:4326',
    }),
    layers: [new ol.layer.Tile({
      source: new ol.source.OSM({
        url: 'https://{a-d}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png',
      })
    })],
  });

  let layer;

  // https://sakitam-fdd.github.io/wind-layer/data/wind.json
  fetch('https://sakitam-fdd.github.io/wind-layer/data/wind.json').then(res => res.json()).then(res =>{
    const windLayer = new OlWind.WindLayer(res, {
      windOptions: {
        colorScale: ["rgb(36,104, 180)", "rgb(60,157, 194)", "rgb(128,205,193 )", "rgb(151,218,168 )", "rgb(198,231,181)", "rgb(238,247,217)", "rgb(255,238,159)", "rgb(252,217,125)", "rgb(255,182,100)", "rgb(252,150,75)", "rgb(250,112,52)", "rgb(245,64,32)", "rgb(237,45,28)", "rgb(220,24,32)", "rgb(180,0,35)"],
        velocityScale: 1 / 100,
        paths: 1000,
      },
      map: map,
    });

    analysisWindyData(res);
    console.log(map, windLayer);

    layer = windLayer;

    // layer.appendTo(map);
    map.addLayer(windLayer);
    map.on('singleclick', e =>{
      var details = getWindyDetail(e.coordinate);
      console.log(details);
      alert(' 風向:' + details.direction + '\n 風級:' + details.level + '\n 風速:' + details.speed);
    });
  });

  var allgrid = [];
  function analysisWindyData(windydata) {
    var p = 0;0.
    var east, north;
    if (windydata[0].header.parameterNumberName == "eastward_wind") {
      east = windydata[0];
      north = windydata[1];
    } else {
      east = windydata[1];
      north = windydata[0];
    }
    for (var j = 0; j < north.header.ny; j++) {
      var row = [];
      for (var i = 0; i < north.header.nx; i++, p++) {
        row[i] = [east.data[p], north.data[p]];
      }
      allgrid[j] = row;
    }
  }
  function getWindyDetail(coord) {
    var lng = coord[0];
    var lat = coord[1];
    // 與格網序列的資料轉換
    if (lng >= 0) {
      lng = Math.floor(lng);
    } else {
      lng = 360 + Math.floor(lng)
    }
    lat = 90 - Math.floor(lat);
    // 獲取對應的格網序列
    var xlength = lng;
    var ylength = lat;
    var xdata, ydata;
    xdata = allgrid[Math.abs(ylength)][Math.abs(xlength)][0];
    ydata = allgrid[Math.abs(ylength)][Math.abs(xlength)][1];
    console.log(xdata);
    console.log(ydata);
    if (typeof xdata != "number" || typeof ydata != "number") {
      console.error("暫無該區域風向資料!");
      return;
    }
    var v = Math.sqrt(Math.pow(xdata, 2) + Math.pow(ydata, 2));
    var angle = getWindyAngle(xdata, ydata);
    var result = {
      "direction": getWindyDirection(angle),
      "level": getWindyLevel(v),
      "speed": v.toFixed(2)
    };
    return result;
  }

  function getWindyDirection(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 "東北風";
    }
  }

  function getWindyAngle(u, v) {
    var fx = 0;
    if (u > 0 & v > 0) {
      fx = 270 - 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 = 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 = 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;
  }

  function getWindyLevel(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;
    }

  }
</script>
</body>

如果對您有所幫助,歡迎您點個關注,我會定時更新技術文件,大家一起討論學習,一起進步。