@mapgis/webclient-es6-mapboxgl
Version:
620 lines (565 loc) • 27.9 kB
JavaScript
import mapboxgl from "@mapgis/mapbox-gl";
import '../core/Base';
import { Util } from '../core/Util';
// import './graphic';
/**
* @class mapboxgl.zondy.DeckglLayer
* @category Visualization DeckGL
* @classdesc Deckgl 高效率图层,该图图层为综合图层,通过该图层可创建 高效率点图层、路径图层(线图层)、高效率面图层、曲线图层、
* 正六边形图层(蜂巢图层)、网格图层,只需给定相依配置,因此,在创建图层之前,请仔细阅读参数配置。
* @param {string} layerTypeID - 高效率图层类型 ID,包括 "scatter-plot" 高效率点图层、"path-layer" 路径图层(线图层)、
* "polygon-layer" 高效率面图层、 "arc-layer" 曲线图层、"hexagon-layer" 正六边形图层(蜂巢图层)、"screen-grid-layer" 网格图层。
* @param {Object} options - 图层配置项,包括以下参数:
* @param {Array.<GeoJSONObject>} options.data - 图层数据,支持 GeoJSON 规范数据类型。
* @param {Object} options.callback - deckgl 图层回调函数配置项。
* @param {Object} options.props - deckgl 图层配置项, 在该参数下配置图层配置项:
* @param {boolean} options.props.coverage - "hexagon-layer" 配置项:六边形半径乘数,介于0 - 1之间。六边形的最终半径通过覆盖半径计算。 注意:覆盖范围不会影响分数的分配方式。 分配方式的半径仅由半径属性确定;
* @param {boolean} options.props.hexagonAggregator - "hexagon-layer" 配置项:* @param {boolean}
* options.props.lightSettings - 公共配置项:光照,包含以下几个配置;
* @param {Array} options.props.lightSettings.lightsPosition - 光照配置项:指定为`[x,y,z]`的光在平面阵列中的位置`, 在一个平面阵列。 长度应该是 `3 x numberOfLights`。
* @param {Array} options.props.lightSettings.lightsStrength - 光照配置项:平面阵列中指定为“[x,y]`的灯的强度。 长度应该是`2 x numberOfLights`。
* @param {number} [options.props.lightSettings.numberOfLights=1] - 光照配置项:光照值,最大值为 `5`。
* @param {number} [options.props.lightSettings.coordinateSystem=COORDINATE_SYSTEM.LNGLAT] - 光照配置项:指定灯位置的坐标系。
* @param {number} [options.props.lightSettings.coordinateOrigin=[0, 0, 0]] - 光照配置项:指定灯位置的坐标原点。
* @param {number} [options.props.lightSettings.modelMatrix] - 光照配置项:光位置的变换矩阵。
* @param {number} [options.props.lightSettings.ambientRatio=0.4] - 光照配置项:光照的环境比例。
* @param {number} [options.props.lightSettings.diffuseRatio=0.6] - 光照配置项:光的漫反射率。
* @param {number} [options.props.lightSettings.specularRatio=0.8] - 光照配置项:光的镜面反射率。
* @param {Object} [options.layerId] - DeckglLayer 图层 Dom 元素 ID。默认使用 CommonUtil.createUniqueID("graphicLayer_" + this.layerTypeID + "_") 创建专题图层 ID。
* @param {number} [options.props.opacity=1] - 公共配置项:图层透明度。
* @param {boolean} [options.props.pickable=false] - 公共配置项:是否响应鼠标事件(鼠标点击,鼠标滑动)。
* @param {function} [options.props.autoHighlight=false] - 公共配置项:鼠标滑动高亮要素。
* @param {function} [options.props.highlightColor=[0, 0, 128, 128]] - 公共配置项:鼠标滑动高亮颜色。
* @param {function} [options.props.onClick] - 公共配置项:鼠标点击事件。
* @param {function} [options.props.onHover] - 公共配置项:鼠标滑动事件。
* @param {number} [options.props.radiusScale=1] - "scatter-plot" 配置项:散点半径比例。
* @param {boolean} [options.props.outline=false] - "scatter-plot" 配置项:是否边线显示。
* @param {number} [options.props.strokeWidth=1] - "scatter-plot" 配置项:边线宽度。
* @param {number} [options.props.radiusMinPixels=0] - "scatter-plot" 配置项:半径最小像素值。
* @param {number} [options.props.radiusMaxPixels=Number.MAX_SAFE_INTEGER] - "scatter-plot" 配置项:半径最大像素值。
* @param {boolean} [options.props.fp64=false] - "scatter-plot" 配置项:否应以高精度64位模式呈现图层。
* @param {number} [options.props.widthScale=1] - "path-layer" 配置项:线宽比例。
* @param {number} [options.props.widthMinPixels=0] - "path-layer" 配置项:线宽最小像素值。
* @param {number} [options.props.widthMaxPixels=Number.MAX_SAFE_INTEGER] - "path-layer" 配置项:线宽最大像素值。
* @param {boolean} [options.props.rounded=false] - "path-layer" 配置项:节点是否绘制为弧形。
* @param {number} [options.props.miterLimit=4] - "path-layer" 配置项:节点相对于线宽的最大范围,仅在 rounded 为 false 时有效;
* @param {boolean} [options.props.fp64=false] - "path-layer" 配置项:否应以高精度64位模式呈现图层。
* @param {boolean} [options.props.dashJustified=false] - "path-layer" 配置项:是否虚线形式显示,仅在 getDashArray() 回调函数被指定时有效。
* @param {boolean} [options.props.filled=true] - "polygon-layer" 配置项:是否填充面。
* @param {boolean} [options.props.stroked=true] - "polygon-layer" 配置项:是否绘制边线。
* @param {boolean} [options.props.extruded=false] - "polygon-layer" 配置项:是否拉伸建筑。
* @param {boolean} [options.props.wireframe=false] - "polygon-layer" 配置项:当面被拉伸为建筑时,是否描绘建筑物边线。
* @param {boolean} [options.props.elevationScale=1] - "polygon-layer" 配置项:海拔比例。
* @param {boolean} [options.props.lineWidthScale=1] - "polygon-layer" 配置项:线宽比例。
* @param {boolean} [options.props.lineWidthMinPixels=0] - "polygon-layer" 配置项:线宽最小像素值。
* @param {boolean} [options.props.lineWidthMaxPixels=Number.MAX_SAFE_INTEGER] - "polygon-layer" 配置项:线宽最大像素值。
* @param {boolean} [options.props.lineJointRounded=false] - "polygon-layer" 配置项:节点是否绘制为弧形。
* @param {boolean} [options.props.lineMiterLimit=4] - "polygon-layer" 配置项:节点相对于线宽的最大范围,仅在 lineJointRounded 为 false 时有效。
* @param {boolean} [options.props.lineDashJustified=false] - "polygon-layer" 配置项:是否虚线形式显示,仅在 getLineDashArray() 回调函数被指定时有效。
* @param {boolean} [options.props.fp64=false] - "polygon-layer" 配置项:否应以高精度64位模式呈现图层。
* @param {boolean} [options.props.fp64=false] - "arc-layer" 配置项:否应以高精度64位模式呈现图层。
* @param {boolean} [options.props.strokeWidth=1] - "arc-layer" 配置项:线宽。
* @param {boolean} [options.props.radius=1000] - "hexagon-layer" 配置项:六边形半径值。
* @param {boolean} [options.props.extruded=false] - "hexagon-layer" 配置项:是否拉伸要素。
* @param {boolean} [options.props.upperPercentile=100] - "hexagon-layer" 配置项:筛选箱并通过upperPercentile重新计算颜色。 颜色值大于upperPercentile的六边形将被隐藏。
* @param {boolean} [options.props.elevationScale=1] - "hexagon-layer" 配置项:高程乘数,实际海拔高度由 elevationScale * getElevation(d)计算。 elevationScale是一个方便的属性,可以在不更新数据的情况下缩放所有六边形。
* @param {boolean} [options.props.colorDomain=false] - "hexagon-layer" 配置项:色阶。
* @param {boolean} [options.props.colorRange=[[255,255,178,255],[254,217,118,255],[254,178,76,255],[253,141,60,255],[240,59,32,255],[189,0,38,255]]] - "hexagon-layer" 配置项:色带。
*/
export class DeckglLayer {
constructor(layerTypeID, options) {
// Util.extend(defaultProps, options);
/**
* @member {string} mapboxgl.zondy.DeckglLayer.prototype.id
* @description 高效率点图层 id。
*/
this.layerTypeID = layerTypeID;
/**
* @member {Array.<mapboxgl.zondy.Graphic>} mapboxgl.zondy.DeckglLayer.prototype.graphics
* @description 点要素对象数组。
*/
this.data = [].concat(options.data);
this.props = options.props ? options.props : {};
this.callback = options.callback ? options.callback : {};
this.id = options.layerId
? options.layerId
: 'graphicLayer_' + this.layerTypeID + '_';
/**
* @member {boolean} [mapboxgl.zondy.DeckglLayer.prototype.visibility=true]
* @description 图层显示状态属性。
*/
this.visibility = true;
}
/**
* @function mapboxgl.zondy.DeckglLayer.prototype.onAdd
* @param {mapboxgl.Map} map - Mapbox GL 地图对象。
* @returns {mapboxgl.zondy.DeckglLayer}
*/
onAdd(map) {
this.map = map;
if (this.canvas) {
this.mapContainer = this.map.getCanvasContainer();
this.mapContainer.appendChild(this.canvas);
return this;
}
//当使用扩展的mapboxgl代码时有效
if (map.getCRS && map.getCRS() !== mapboxgl.CRS.EPSG3857) {
this.coordinateSystem = 3;
this.isGeographicCoordinateSystem = true;
}
//创建图层容器
this._initContainer();
//创建 deckgl 图层
this._createLayerByLayerTypeID();
var mapState = this._getState();
let deckOptions = {};
for (let key in mapState) {
deckOptions[key] = mapState[key];
}
deckOptions.layers = [this.layer];
deckOptions.canvas = this.canvas;
this.deckGL = new window.DeckGL.experimental.DeckGLJS(deckOptions);
this.map.on('render', this._moveEvent.bind(this));
this.map.on('resize', this._resizeEvent.bind(this));
this._draw();
return this;
}
addTo(map){
this.map = map;
if (this.canvas) {
this.mapContainer = this.map.getCanvasContainer();
this.mapContainer.appendChild(this.canvas);
return this;
}
//当使用扩展的mapboxgl代码时有效
if (map.getCRS && map.getCRS() !== mapboxgl.CRS.EPSG3857) {
this.coordinateSystem = 3;
this.isGeographicCoordinateSystem = true;
}
//创建图层容器
this._initContainer();
//创建 deckgl 图层
this._createLayerByLayerTypeID();
var mapState = this._getState();
let deckOptions = {};
for (let key in mapState) {
deckOptions[key] = mapState[key];
}
deckOptions.layers = [this.layer];
deckOptions.canvas = this.canvas;
this.deckGL = new window.DeckGL.experimental.DeckGLJS(deckOptions);
this.map.on('render', this._moveEvent.bind(this));
this.map.on('resize', this._resizeEvent.bind(this));
this._draw();
return this;
}
/**
* @function mapboxgl.zondy.DeckglLayer.prototype.remove
* @description 删除该图层。
*/
remove() {
this.map.off('render', this._moveEvent.bind(this));
this.map.off('resize', this._resizeEvent.bind(this));
this.map.getCanvasContainer().removeChild(this.canvas);
}
/**
* @function mapboxgl.zondy.DeckglLayer.prototype.removeFromMap
* @deprecated
* @description 删除该图层。
*/
removeFromMap() {
this.remove();
this.clear();
}
/**
* @function mapboxgl.zondy.DeckglLayer.prototype.moveTo
* @description 将图层移动到某个图层之前。
* @param {string} layerID - 待插入的图层 ID。
* @param {boolean} [before=true] - 是否将本图层插入到图层 id 为 layerID 的图层之前(如果为 false 则将本图层插入到图层 id 为 layerID 的图层之后)。
*/
moveTo(layerID, before) {
var layer = document.getElementById(this.id);
before = before !== undefined ? before : true;
if (before) {
var beforeLayer = document.getElementById(layerID);
if (layer && beforeLayer) {
beforeLayer.parentNode.insertBefore(layer, beforeLayer);
}
return;
}
var nextLayer = document.getElementById(layerID);
if (layer) {
if (nextLayer.nextSibling) {
nextLayer.parentNode.insertBefore(layer, nextLayer.nextSibling);
return;
}
nextLayer.parentNode.appendChild(layer);
}
}
/**
* @function mapboxgl.zondy.DeckglLayer.prototype.setVisibility
* @description 设置图层可见性,设置图层的隐藏,显示,重绘的相应的可见标记。
* @param {boolean} [visibility] - 是否显示图层(当前地图的 resolution 在最大最小 resolution 之间)。
*/
setVisibility(visibility) {
if (this.canvas && visibility !== this.visibility) {
this.visibility = visibility;
this.canvas.style.display = visibility ? 'block' : 'none';
}
}
/**
* @function mapboxgl.zondy.DeckglLayer.prototype.setStyle
* @description 设置图层整体样式。
* @param {Object} styleOptions - 样式对象。
* @param {Array.<number>} [styleOptions.color=[0, 0, 0, 255]] - 点颜色。
* @param {number} [styleOptions.radius=10] - 点半径。
* @param {number} [styleOptions.opacity=0.8] - 不透明度。
* @param {Array} [styleOptions.highlightColor] - 高亮颜色,目前只支持 rgba 数组。
* @param {number} [styleOptions.radiusScale=1] - 点放大倍数。
* @param {number} [styleOptions.radiusMinPixels=0] - 半径最小值(像素)。
* @param {number} [styleOptions.radiusMaxPixels=Number.MAX_SAFE_INTEGER] - 半径最大值(像素)。
* @param {number} [styleOptions.strokeWidth=12] - 边框大小。
* @param {boolean} [styleOptions.outline=false] - 是否显示边框。
*/
setStyle(styleOptions) {
Util.extend(this.props, styleOptions);
this._createLayerByLayerTypeID();
this.update();
}
/**
* @function mapboxgl.zondy.DeckglLayer.prototype.setData
* @description 设置绘制的点要素数据,会覆盖之前的所有要素。
* @param {Array.<Object>} data - 点要素对象数组。
*/
setData(data) {
this.data = this.data || [];
this.data.length = 0;
let dataTemp = !Util.isArray(data) ? [data] : [].concat(data);
//this.layer.props.data不能被重新赋值,只能在原数组上进行操作
if (!this.layer.props.data) {
this.layer.props.data = [];
}
this.layer.props.data.length = 0;
for (let i = 0; i < dataTemp.length; i++) {
this.layer.props.data.push(dataTemp[i]);
}
this.data = this.layer.props.data;
this.update();
}
/**
* @function mapboxgl.zondy.DeckglLayer.prototype.addData
* @description 添加点要素,不会覆盖之前的要素。
* @param {Array.<Object>} data - 点要素对象数组。
*/
addData(data) {
this.data = this.data || [];
let dataTemp = !Util.isArray(data) ? [data] : [].concat(data);
//this.layer.props.data不能被重新赋值,只能在原数组上进行操作
if (!this.layer.props.data) {
this.layer.props.data = [];
}
for (let i = 0; i < dataTemp.length; i++) {
this.layer.props.data.push(dataTemp[i]);
}
this.update();
}
/**
* @function mapboxgl.zondy.DeckglLayer.prototype.update
* @description 更新图层。
*/
update() {
if (this.layer.lifecycle !== 'Awaiting state') {
let changeFlags = {
dataChanged: true,
propsChanged: true,
viewportChanged: true,
updateTriggersChanged: true
};
this.layer.setChangeFlags(changeFlags);
}
this._draw();
}
/**
* @function mapboxgl.zondy.DeckglLayer.prototype.clear
* @description 释放图层资源。
*/
// todo 还有哪些资源应该被释放?
clear() {
this.removeData();
this.deckGL.finalize();
}
/**
* @function mapboxgl.zondy.DeckglLayer.prototype.removeData
* @description 移除所有要素。
*/
removeData() {
this.data.length = 0;
if (this.layer.props.data) {
this.layer.props.data.length = 0;
}
this.update();
}
_draw() {
let deckOptions = this._getState();
deckOptions.layers = [this.layer];
deckOptions.canvas = this.canvas;
// this.deckGL.updateLayers();
this.deckGL.setProps(deckOptions);
}
_getState() {
//获取地图信息构建state
let map = this.map;
let width = parseInt(this.canvas.style.width);
let height = parseInt(this.canvas.style.height);
let center = map.getCenter();
let longitude = center.lng;
let latitude = center.lat;
let zoom = map.getZoom();
let maxZoom = map.getMaxZoom();
let pitch = map.getPitch();
let bearing = map.getBearing();
let mapViewport = {
width: width,
height: height,
longitude: longitude,
latitude: latitude,
zoom: zoom,
maxZoom: maxZoom,
pitch: pitch,
bearing: bearing
};
let state = {};
//克隆 mapViewport
for (let key in mapViewport) {
state[key] = mapViewport[key];
}
//克隆 props
for (let key in this.props) {
state[key] = this.props[key];
}
//当使用扩展的mapboxgl代码时有效
if (map.getCRS && map.getCRS() !== mapboxgl.CRS.EPSG3857) {
state.coordinateSystem = this.coordinateSystem;
state.isGeographicCoordinateSystem = this.isGeographicCoordinateSystem;
}
//更行数据
state.data = this.data;
return state;
}
/**
* @function mapboxgl.zondy.DeckglLayer.prototype._createLayerByLayerTypeID
* @description 判别当前创建图层类型。
* @private
*/
_createLayerByLayerTypeID() {
//统一处理公共属性:
this.props.data = this.data;
this.props.isGeographicCoordinateSystem = this.isGeographicCoordinateSystem;
this.props.coordinateSystem = this.coordinateSystem;
//添加事件监听
this.props.pickable = Boolean(this.props.onClick) || Boolean(this.props.onHover);
//各类型各自从 defaultProps 取出相形的参数:
if (this.layerTypeID === 'scatter-plot') {
this.props.id = 'scatter-plot';
this._createScatterPlotLayer();
} else if (this.layerTypeID === 'path-layer') {
this.props.id = 'path-layer';
this._createPathLayer();
} else if (this.layerTypeID === 'polygon-layer') {
this.props.id = 'polygon-layer';
this._createPolygonLayer();
} else if (this.layerTypeID === 'arc-layer') {
this.props.id = 'arc-layer';
this._createArcLineLayer();
} else if (this.layerTypeID === 'hexagon-layer') {
this.props.id = 'hexagon-layer';
this._createHexagonLayer();
} else {
throw new Error(this.layerTypeID + ' does not support');
}
}
/**
* @description scatter-plot
* @private
*/
_createScatterPlotLayer() {
//处理回调
/* this.props.getPosition = this.callback.getPosition ? this.callback.getPosition : function (point) {
if (!point) {
return [0, 0, 0];
}
return point.geometry.coordinates;
};*/
var me = this;
this.props.getPosition = this.callback.getPosition
? this.callback.getPosition
: function(point) {
if (!point) {
return [0, 0, 0];
}
let lngLat = point.getLngLat();
return lngLat && [lngLat.lng, lngLat.lat, 0];
};
if (this.callback.getColor) {
this.props.getColor = this.callback.getColor
? this.callback.getColor
: function(point) {
let style = point && point.getStyle();
return (style && style.color) || me.props.color;
};
}
if (this.callback.getRadius) {
this.props.getRadius = this.callback.getRadius
? this.callback.getRadius
: function(point) {
let style = point && point.getStyle();
return (style && style.radius) || me.props.radius;
};
}
if (this.props.color || this.props.radius) {
this.props.updateTriggers = {};
if (this.props.radius) {
this.props.updateTriggers.getRadius = [this.props.radius];
}
if (this.props.color) {
this.props.updateTriggers.getColor = [this.props.color];
}
}
this.layer = new window.DeckGL.ScatterplotLayer(this.props);
}
/**
* @description path-layer
* @private
*/
_createPathLayer() {
this.props.getPath = this.callback.getPath
? this.callback.getPath
: function(feature) {
return feature.geometry.coordinates;
};
//以下几个函数也可走默认值
if (this.callback.getColor) {
this.props.getColor = this.callback.getColor;
}
if (this.callback.getWidth) {
this.props.getWidth = this.callback.getWidth;
}
if (this.callback.getDashArray) {
this.props.getDashArray = this.callback.getDashArray;
}
this.layer = new window.DeckGL.PathLayer(this.props);
}
/**
* @description polygon-layer
* @private
*/
_createPolygonLayer() {
this.props.getPolygon = this.callback.getPolygon
? this.callback.getPolygon
: function(feature) {
return feature.geometry.coordinates;
};
//todo 思考下真的让用户配这么多回调么,或者先判断下数据属性里面有没有配置的属性值?
if (this.callback.getElevation) {
this.props.getElevation = this.callback.getElevation;
}
if (this.callback.getFillColor) {
this.props.getFillColor = this.callback.getFillColor;
}
if (this.callback.getLineColor) {
this.props.getLineColor = this.callback.getLineColor;
}
if (this.callback.getLineWidth) {
this.props.getLineWidth = this.callback.getLineWidth;
}
this.props.updateTriggers = {};
this.props.updateTriggers.getColor = this.props.color ? this.props.color : [0, 0, 128, 128];
this.layer = new window.DeckGL.PolygonLayer(this.props);
}
/**
* @description arc-layer
* @private
*/
_createArcLineLayer() {
//todo ArcLineLayer geojson coordinates数组中只能有一个线段
this.props.getSourcePosition = this.callback.getSourcePosition
? this.callback.getSourcePosition
: function(feature) {
if (!feature) {
return [0, 0, 0];
}
return feature.geometry.coordinates[0];
};
this.props.getTargetPosition = this.callback.getTargetPosition
? this.callback.getTargetPosition
: function(feature) {
if (!feature) {
return [0, 0, 0];
}
return feature.geometry.coordinates[1];
};
if (this.callback.getStrokeWidth) {
this.props.getStrokeWidth = this.callback.getStrokeWidth;
}
if (this.callback.getSourceColor) {
this.props.getSourceColor = this.callback.getSourceColor;
}
if (this.callback.getTargetColor) {
this.props.getTargetColor = this.callback.getTargetColor;
}
this.layer = new window.DeckGL.ArcLayer(this.props);
}
/**
* @description hexagon-layer
* @private
*/
_createHexagonLayer() {
this.props.getPosition = this.callback.getPosition
? this.callback.getPosition
: function(feature) {
if (!feature) {
return [0, 0, 0];
}
return feature.geometry.coordinates;
};
if (this.callback.getColorValue) {
this.props.getColorValue = this.callback.getColorValue;
}
if (this.callback.getElevationValue) {
this.props.getElevationValue = this.callback.getElevationValue;
}
this.layer = new window.DeckGL.HexagonLayer(this.props);
}
_initContainer() {
this.canvas = this._createCanvas();
this.mapContainer = this.map.getCanvasContainer();
this.mapContainer.appendChild(this.canvas);
}
_createCanvas() {
let canvas = document.createElement('canvas');
canvas.id = this.id;
canvas.style.position = 'absolute';
canvas.style.top = 0 + 'px';
canvas.style.left = 0 + 'px';
canvas.style.cursor = '';
let map = this.map;
canvas.width = parseInt(map.getCanvas().style.width);
canvas.height = parseInt(map.getCanvas().style.height);
canvas.style.width = map.getCanvas().style.width;
canvas.style.height = map.getCanvas().style.height;
return canvas;
}
_moveEvent() {
this._draw();
}
_resizeEvent() {
let canvas = this.canvas;
let map = this.map;
canvas.width = parseInt(map.getCanvas().style.width);
canvas.height = parseInt(map.getCanvas().style.height);
canvas.style.width = map.getCanvas().style.width;
canvas.style.height = map.getCanvas().style.height;
this._draw();
}
}
mapboxgl.zondy.DeckglLayer = DeckglLayer;