风场可视化学习笔记: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

关于颜色配置,在以往的配置中传入的是颜色数组会根据以下函数和格点数据的数据范围去计算匹配的颜色值,

1
2
3
4
5
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代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
<!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/dat.gui@0.7.6/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>

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 

posted @   林恒  阅读(1217)  评论(0编辑  收藏  举报
编辑推荐:
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
阅读排行:
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· 【.NET】调用本地 Deepseek 模型
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)
欢迎阅读『风场可视化学习笔记:openlayers』
点击右上角即可分享
微信分享提示