openlayers扩展:风场可视化(wind-layer)

windLayer api使用示例

这里主要为windlayer关于在openlayers3-4版本内使用的相关接口及参数说明

在加载时,主要读取的数据来源格式为json,而我们一般获取到的数据主要为netCDF或Grib等格式的数据,这里还涉及相关的数据转换操作

1.openlayers-wind 参数说明

TIP

对应于 openlayers 3-4 相关参数

图层参数

参数说明类型默认值
windOptions风场参数,具体配置如下object--
map地图对象,必须配置,不需要调用 addLayer,具体可以参考 openlayer 官方文档ol.Map--
zIndex图层层级number--

其他参数遵循 ol 基础图层参数。

windOptions

参数说明类型默认值
globalAlpha全局透明度,主要影响粒子路径拖尾效果number0.9
lineWidth粒子路径宽度number\|function1, 当为回调函数时,参数function(m:对应点风速值) => number
colorScale粒子颜色配置string\|function\|string[]#fff,当为回调函数时,参数function(m:对应点风速值) => string
velocityScale速度级别number1 / 25
maxAge \| particleAge(不推荐使用)粒子路径能够生成的最大帧数number90
paths生成的粒子路径数量number\|function800, 当为回调函数时,参数function(m:对应点风速值) => number
particleMultiplier粒子路径数量的系数,不推荐使用(视野宽度 * 高度 * 系数)number1 / 300
frameRate帧率(ms)number20

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接口,添加相应的图层对象

<!DOCTYPE html>
<html>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>
		ol4 wind-layer
	</title>
	<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/dat.gui@0.7.6/build/dat.gui.css">
	<script src="//cdn.jsdelivr.net/npm/openlayers/dist/ol.js">
	</script>
	<link rel="stylesheet" href="//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: '//{a-d}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png',
					})
				})],
			});

			let layer;

			// https://sakitam-fdd.github.io/wind-layer/data/wind.json
			fetch('data.json').then(res = >res.json()).then(res = >{
				// const range = vectorField.range || [0.02, 28.21618329965979];
				// const scale = chroma.scale('OrRd').domain(range);
				const windLayer = new OlWind.WindLayer(res, {
					windOptions: {
						/*colorScale: (m) => {
            // console.log(m);
            return '#ff473c';
          },*/
						// colorScale: scale,
						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)"],
						/*frameRate: 16,
          maxAge: 60,
          globalAlpha: 0.9,*/
						velocityScale: 1 / 30,
						paths: 3000,
						// particleMultiplier: 0.3 * 10,
					},
					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;
				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>

</html>

 在这里将相关的数据获取方式进行了声明,通过数据源,我们可以了解,气象预报数据是按照经纬度格网进行统计、展示的,所以我们在这里想格网数据重新做了一次数据分析,方便前台的交互展示。

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

熠熠仔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值