wind-layer
Version:
a openlayers ol bmap amap maptalks extension to windjs
336 lines (309 loc) • 8.14 kB
JavaScript
import Windy from '../windy/windy';
import { createCanvas, getDirection, getSpeed } from '../helper';
const global = typeof window === 'undefined' ? {} : window;
const AMap = global.AMap || {};
class AMapWind {
constructor (data, options = {}) {
this.options = options;
/**
* internal
* @type {null}
*/
this.canvas = null;
/**
* windy 数据
*/
this.data = data;
/**
* canvas layer
* @type {null}
* @private
*/
this.layer_ = null;
/**
* windy layer
* @type {null}
*/
this._windy = null;
if (options.map) {
this.appendTo(options.map)
}
/**
* bind context
* @type {{new(...args: any[][]): any} | ((...args: any[][]) => any) | ((...args: any[]) => any) | any | {new(...args: any[]): any}}
*/
this.init = this.init.bind(this);
this.handleResize = this.handleResize.bind(this);
this.canvasFunction = this.canvasFunction.bind(this);
this._addReFreshHandle = this._addReFreshHandle.bind(this);
}
/**
* append layer to map
* @param map
*/
appendTo (map) {
if (map) {
this.init(map)
} else {
throw new Error('not map object');
}
}
/**
* get layer data
* @returns {*}
*/
getData () {
return this.data;
}
/**
* set data
* @param data
*/
setData (data) {
this.data = data;
if (this.map && this.canvas && this.data) {
this.render(this.canvas);
}
}
/**
* init windy layer
* @param map
* @param options
*/
init (map, options) {
if (map) {
this.map = map;
this.context = this.options.context || '2d';
this.getCanvasLayer();
this.map.on('resize', this.handleResize, this)
} else {
throw new Error('not map object')
}
}
handleResize () {
if (this.canvas) {
this.canvasFunction()
}
}
/**
* render layer
* @param canvas
* @returns {*}
*/
render (canvas) {
if (!canvas) return;
const extent = this._getExtent();
if (!this.getData() || !extent) return this;
if (canvas && !this._windy) {
const {
minVelocity,
maxVelocity,
velocityScale,
particleAge,
lineWidth,
particleMultiplier,
colorScale
} = this.options;
this._windy = new Windy({
canvas: canvas,
data: this.getData(),
'onDraw': () => {
},
minVelocity,
maxVelocity,
velocityScale,
particleAge,
lineWidth,
particleMultiplier,
colorScale
});
this._windy.start(extent[0], extent[1], extent[2], extent[3]);
} else if (canvas && this._windy) {
this._windy.start(extent[0], extent[1], extent[2], extent[3]);
}
// 2D视图时可以省略
this._addReFreshHandle();
return this;
}
/**
* 3D模式下手动刷新
* @private
*/
_addReFreshHandle () {
if (!this.map) return;
const type = this.map.getViewMode_();
if (type.toLowerCase() === '3d') {
this.layer_ && this.layer_.reFresh();
AMap.Util.requestAnimFrame(this._addReFreshHandle);
}
}
/**
* get canvas layer
*/
getCanvasLayer () {
if (!this.canvas && !this.layer_) {
const canvas = this.canvasFunction();
const bounds = this._getBounds();
this.layer_ = new AMap.CanvasLayer({
canvas: canvas,
bounds: this.options.bounds || bounds,
zooms: this.options.zooms || [0, 22],
zIndex: this.options.zIndex || 12,
opacity: this.options.opacity || 1
});
this.map.on('mapmove', this.canvasFunction, this);
this.map.on('zoomchange', this.canvasFunction, this);
this.layer_.setMap(this.map);
}
}
/**
* canvas constructor
* @returns {*}
*/
canvasFunction () {
const [width, height] = [this.map.getSize().width, this.map.getSize().height];
if (!this.canvas) {
this.canvas = createCanvas(width, height, null);
} else {
this.canvas.width = width;
this.canvas.height = height;
const bounds = this._getBounds();
if (this.layer_) {
this.layer_.setBounds(this.options.bounds || bounds);
}
}
this.render(this.canvas);
return this.canvas;
}
/**
* fixed viewMode
* @private
*/
_getBounds () {
const type = this.map.getViewMode_();
let [southWest, northEast] = [];
const bounds = this.map.getBounds()
if (type.toLowerCase() === '2d') {
northEast = bounds.getNorthEast(); // xmax ymax
southWest = bounds.getSouthWest(); // xmin ymin
} else {
// TODO: 高德地图3D模式下目前返回的bounds顺序为左上-右上-右下-左下-左上
const arrays = bounds.bounds.map(item => {
return [item.getLng(), item.getLat()];
});
// const extent = getExtent(arrays);
southWest = new AMap.LngLat(...arrays[3]);
northEast = new AMap.LngLat(...arrays[1]);
}
return new AMap.Bounds(southWest, northEast);
}
/**
* bounds, width, height, extent
* @returns {*}
* @private
*/
_getExtent () {
const [width, height] = [this.map.getSize().width, this.map.getSize().height];
const bounds = this._getBounds();
const _ne = bounds.getNorthEast();
const _sw = bounds.getSouthWest();
const min = _sw.lng;
// TODO: 当东北角坐标小于0时需要按照经度递增处理,而且目前高德最多只会有一个世界
const max = _ne.lng < 0 ? 360 + _ne.lng : _ne.lng;
const xmin = Math.min(min, max);
const xmax = Math.max(min, max);
return [
[
[0, 0], [width, height]
],
width,
height,
[
[xmin, _sw.lat], [xmax, _ne.lat] // [xmin, ymin, xmax, ymax]: 西南 和 东北
]
]
}
/**
* remove layer
*/
removeLayer () {
if (!this.map) return;
this.layer_.setMap(null);
this.map.off('resize', this.handleResize, this);
this.map.off('mapmove', this.canvasFunction, this);
this.map.off('zoomchange', this.canvasFunction, this);
delete this.map;
delete this.layer_;
delete this.canvas;
}
/**
* get canvas context
* @returns {*}
*/
getContext () {
return this.canvas.getContext(this.context);
}
/**
* get mouse point data
* @param coordinates
* @returns {{direction: number, speed: *}}
*/
getPointData (coordinates) {
const gridValue = this._windy.interpolatePoint(coordinates[0], coordinates[1]);
if (gridValue && !isNaN(gridValue[0]) && !isNaN(gridValue[1]) && gridValue[2]) {
return {
direction: getDirection(gridValue[0], gridValue[1], this.options.angleConvention || 'bearingCCW'),
speed: getSpeed(gridValue[0], gridValue[1], this.options.speedUnit)
}
}
}
/**
* clear wind
*/
clearWind () {
if (this._windy) this._windy.stop();
}
/**
* update windy config
* @param params
* @returns {AMapWind}
*/
updateParams (params) {
this.options = Object.assign(this.options, params);
if (this._windy) {
const {
minVelocity, // 粒子强度最小的速度 (m/s)
maxVelocity, // 粒子强度最大的速度 (m/s)
velocityScale, // 风速的比例
particleAge, // 重绘之前生成的离子数量的最大帧数
lineWidth, // 绘制粒子的线宽
particleMultiplier, // 离子数量
colorScale
} = this.options;
if (this._windy) {
// this._windy.stop();
this._windy.updateParams({
minVelocity,
maxVelocity,
velocityScale,
particleAge,
lineWidth,
particleMultiplier,
colorScale
});
if (this.map && this.canvas && this.data) {
this.render(this.canvas);
}
}
}
return this;
}
/**
* get windy config
* @returns {null|*|Windy.params|{velocityScale, minVelocity, maxVelocity, colorScale, particleAge, lineWidth, particleMultiplier}}
*/
getParams () {
return this._windy && this._windy.getParams();
}
}
export default AMapWind