@mapgis/webclient-es6-mapboxgl
Version:
625 lines (571 loc) • 22 kB
JavaScript
import { mapboxgl } from '@mapgis/mapbox-gl';
import { Common } from '@mapgis/webclient-es6-service';
const { extend, Zondy } = Common;
/**
* @class Theme3DLayer
* @classdesc 三维专题图基类,不能直接实例化
* @param id -{string} 专题图图层id
* @param layerOptions -{Object} 专题图图层配置项<br>
* opacity -{number} 图层透明度,默认1<br>
* parseNumber -{boolean} 是否预处理数据,将数据转换为number,默认false<br>
* enableHighlight -{boolean} 是否开启高亮,默认false<br>
* highlight -{Object} 高亮颜色,默认"#ADA91E"<br>
* baseHeightField -{string} 数据中表示基础高度的字段<br>
* height -{number} 高度。如果数据指定的heightField(默认height)没有可以表示高度的字段,可以为所有数据统一设置一个高度<br>
* heightField -{string} 数据中表示高度的字段<br>
* themeField -{string} 专题展示的字段<br>
* showLegend -{boolean} 是否显示图例,默认显示<br>
* legendTitle -{string} 图例标题<br>
* legendTheme -{string} 图例主题,取值:'light','dark',默认:'light'<br>
* legendOrientation -{string} 图例方向,取值:'horizontal','vertical',默认:'horizontal'<br>
* legendPosition -{string} 图例位置,取值:'top-right'|'top-left'|'bottom-left'|'bottom-right'<br>
*/
class Theme3DLayer {
constructor(id, layerOptions) {
/**
* @member id -{string}
* @description mapbox gl图层id
*/
this.id = id;
/**
* @member map -{object}
* @description mapbox gl地图对象
*/
this.map = null;
/**
* @member opacity -{number}
* @description 图层透明度,默认1
*/
this.opacity = 1;
/**
* @member parseNumber -{boolean}
* @description 是否进行数据预处理,有些字段是string类型,需要转换为number
*/
this.parseNumber = false;
/**
* @member enableHighlight -{boolean}
* @description 是否开启高亮,默认false
*/
this.enableHighlight = false;
/**
* @member highlight -{Object}
* @description 高亮相关配置,默认null
*/
this.highlight = { color: '#ADA91E' };
/**
* @member baseHeightField -{string}
* @description 数据中表示基础高度的字段
*/
this.baseHeightField = null;
/**
* @member height -{number}
* @description 高度。如果数据指定的heightField(默认height)没有可以表示高度的字段,可以为所有数据统一设置一个高度
*/
this.height = null;
/**
* @member heightField -{string}
* @description 数据中表示高度的字段
*/
this.heightField = 'height';
/**
* @member themeField -{string}
* @description 专题展示的字段
*/
this.themeField = this.heightField;
/**
* @member showLegend -{Boolean}
* @description 是否显示图例
*/
this.showLegend = true;
/**
* @member legendTitle -{string}
* @description 图例标题
*/
this.legendTitle = null;
/**
* @member legendTheme -{string}
* @description 图例主题,取值:'light','dark'
* @default 'light'
*/
this.legendTheme = 'light';
/**
* @member legendOrientation -{string}
* @description 图例方向,取值:'horizontal','vertical',默认:'horizontal'
* @default 'horizontal'
*/
this.legendOrientation = 'horizontal';
/**
* @member legendPosition -{string}
* @description 图例位置,取值:'top-right'|'top-left'|'bottom-left'|'bottom-right'
* @default 'bottom-right'
*/
this.legendPosition = 'bottom-right';
this.mousemoveOn = true;
extend(this, layerOptions);
}
/**
* @function setLayerOptions
* @description 设置图层相关参数
* @param layerOptions -{object} 该专题图图层相关参数<br>
* * opacity -{number} 图层透明度,默认1<br>
* parseNumber -{boolean} 是否预处理数据,将数据转换为number,默认false<br>
* baseHeightField -{string} 数据中表示基础高度的字段<br>
* height -{number} 高度。如果数据指定的heightField(默认height)没有可以表示高度的字段,可以为所有数据统一设置一个高度<br>
* heightField -{string} 数据中表示高度的字段<br>
* themeField -{string} 专题展示的字段<br>
* showLegend -{boolean} 是否显示图例,默认显示<br>
* legendTitle -{string} 图例标题<br>
* legendTheme -{string} 图例主题,取值:'light','dark',默认:'light'<br>
* legendOrientation -{string} 图例方向,取值:'horizontal','vertical',默认:'horizontal'<br>
* legendPosition -{string} 图例位置,取值:'top-right'|'top-left'|'bottom-left'|'bottom-right'<br>
* @returns {this}
*/
setLayerOptions(layerOptions) {
extend(this, layerOptions);
return this;
}
/**
* @function mapboxgl.zondy.Theme3DLayer.prototype.setHighlightStyleOptions
* @description 设置图层高亮相关参数
* @param highlightOptions -{object} 该专题图图层高亮相关参数<br>
* color -{string} 颜色<br>
* callback -{function} 回调,返回数据参数(data,event)<br>
* @returns {this}
*/
setHighlightStyleOptions(highlightOptions) {
extend(this.highlight, highlightOptions);
return this;
}
/**
* @function setData
* @description 设置数据,数据格式必须为geojson格式
* @param data -{object} geojson格式数据
* @param parseNumber -{object} 是否进行数据预处理,有些字段是string类型,需要转换为number
*/
setData(data, parseNumber) {
var me = this;
me.data = data;
if (parseNumber != null) {
me.parseNumber = parseNumber;
}
me.parseNumber &&
me.data &&
me.data.features &&
me.data.features.map(function (val) {
if (me.baseHeightField && val.properties[me.baseHeightField]) {
val.properties[me.baseHeightField] = parseFloat(val.properties[me.baseHeightField]);
}
if (me.heightField && val.properties[me.heightField]) {
val.properties[me.heightField] = parseFloat(val.properties[me.heightField]);
}
return val;
});
return this;
}
WebMercator2lonLat(cx, cy) {
var x = (cx / 20037508.34) * 180;
var y = (cy / 20037508.34) * 180;
y = (180 / Math.PI) * (2 * Math.atan(Math.exp((y * Math.PI) / 180)) - Math.PI / 2);
return [x, y];
}
convertData(data) {
if (data != null && data.features != null && data.features.length > 0) {
for (var i = 0; i < data.features.length; i++) {
var temGeom = data.features[i].geometry;
if (temGeom != null && temGeom.coordinates != null && temGeom.coordinates.length > 0) {
if (temGeom.type == 'Polygon') {
var rings = temGeom.coordinates;
for (var j = 0; j < rings.length; j++) {
var ring = rings[j];
if (ring != null && ring.length > 0) {
for (var k = 0; k < ring.length; k++) {
let coordinate = new mapboxgl.LngLat(ring[k][0], ring[k][1]);
let tempPoint = map.project(coordinate);
data.features[i].geometry.coordinates[j][k] = [tempPoint.x, tempPoint.y];
}
}
}
} else if (temGeom.type == 'MultiPolygon') {
var polygons = temGeom.coordinates;
for (var m = 0; m < polygons.length; m++) {
var polygon = polygons[m];
if (polygon != null && polygon.length > 0) {
for (var j = 0; j < polygon.length; j++) {
var ring = polygon[j];
if (ring != null && ring.length > 0) {
for (var k = 0; k < ring.length; k++) {
let coordinate = new mapboxgl.LngLat(ring[k][0], ring[k][1]);
let tempPoint = map.project(coordinate);
data.features[i].geometry.coordinates[m][j][k] = [tempPoint.x, tempPoint.y];
}
}
}
}
}
}
}
}
}
return data;
}
/**
* @function getData
* @description 获取数据,返回的数据格式为geojson
* @returns {Object}
*/
getData() {
return this.data;
}
/**
* @function addTo
* @description 添加图层到地图上
* @param map -{Object} mapboxgl 地图对象
* @returns {this}
*/
addTo(map) {
this.map = map;
if (!this.map) {
return this;
}
this.show();
return this;
}
/**
* @function show
* @description 显示图层
* @param options -{Object} 图层配置项
* @returns {this}
*/
show(options) {
extend(this, options);
this._addLayer();
if (this.enableHighlight) {
this._addHighLightLayer();
}
if (this.showLegend) {
if (!this.legend) {
this.legend = this._createLegendControl();
}
this.map.addControl(this.legend, this.legendPosition);
}
return this;
}
/**
* @function remove
* @description 从地图上移除图层
* @returns {this}
*/
remove() {
if (!this.map) {
return this;
}
var me = this;
me.mousemoveOn = false;
// this.map.off('mousemove',function(e){
// me.mousemoveCallback(e,me);
// });
//移除图层
var layerId = this.id ? this.id : 'theme3DLayer';
if (this.map.getLayer(layerId)) {
this.map.removeLayer(layerId);
}
//移除高亮图层
var highlightLayerId = 'highlightLayer';
if (this.map.getLayer(highlightLayerId)) {
this.map.removeLayer(highlightLayerId);
}
// if (!!this.map.getSource(this.sourceId)) {
// this.map.removeSource(this.sourceId);
// }
// if (!!this.map.getSource("highlight")) {
// this.map.removeSource("highlight");
// }
//移除图例
if (this.legend) {
this.map.removeControl(this.legend);
}
return this;
}
/**
* @function getLayerStyleOptions
* @description 获取图层样式
* @return {Object} mapbox gl样式对象
*/
getLayerStyleOptions() {
//子类重写实现
}
/**
* @function mapboxgl.zondy.Theme3DLayer.prototype.getHighlightStyleOptions
* @description 获取高亮样式,子类重写实现
* @returns {Object} mapbox gl样式对象
*/
getHighlightStyleOptions() {
//子类重写实现
}
_createLegendControl(html) {
var me = this;
function LegendControl() {}
LegendControl.prototype.onAdd = function (map) {
this._map = map;
this._container = document.createElement('div');
var className = 'mapboxgl-ctrl legend ';
var theme = 'legend-light';
if (me.legendTheme === 'dark') {
theme = 'legend-dark';
}
var orientation = ' legend-horizontal';
if (me.legendOrientation === 'vertical') {
orientation = ' legend-vertical';
}
this._container.className = className + theme + orientation;
if (html) {
this._container.innerHTML = html;
} else {
var legendTitle = me.legendTitle || '';
var titleElement = " <div class='legend-title'>" + legendTitle + '</div>';
var content = me._createLegendElement.call(me) || '';
var contentElement = "<div class='legend-content'>" + content + '</div>';
this._container.innerHTML = titleElement + contentElement;
}
me._appendLegendCSSStyle();
return this._container;
};
LegendControl.prototype.onRemove = function () {
this._container.parentNode.removeChild(this._container);
this._map = undefined;
};
return new LegendControl();
}
_createLegendElement() {
//子类实现
}
_addLayer() {
var paintOptions = this.getLayerStyleOptions();
var id = this.id ? this.id : 'theme3DLayer';
this.id = id;
var sourceId = (this.sourceId = id + 'Source');
if (!!this.map.getSource(sourceId)) {
this.map.removeSource(sourceId);
}
this.map.addSource(sourceId, {
type: 'geojson',
data: this.data
});
if (!!this.map.getLayer(id)) {
this.map.removeLayer(id);
}
this.map.addLayer({
id: id,
type: 'fill-extrusion',
source: sourceId,
paint: paintOptions
});
this.map.moveLayer(id);
}
//添加高亮图层
_addHighLightLayer() {
if (!this.map) {
return;
}
var map = this.map;
if (map.getSource('highlight')) {
map.removeSource('highlight');
}
map.addSource('highlight', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: []
}
});
if (!!map.getLayer('highlightLayer')) {
map.removeLayer('highlightLayer');
}
map.addLayer({
id: 'highlightLayer',
type: 'fill-extrusion',
source: 'highlight',
paint: this.getHighlightStyleOptions()
});
var me = this;
var featureId;
//map.on('mousemove',this.mousemoveCallback);
// map.on('mousemove',function(e){
// if(!me.mousemoveOn)
// {
// return;
// }
// me.mousemoveCallback(e,me);
// });
map.on('mousemove', function (e) {
if (!me.mousemoveOn) {
return;
}
me.mousemoveOn = true;
var canvas = document.querySelector('.mapboxgl-canvas-container.mapboxgl-interactive');
canvas.style.cursor = 'auto';
var features = me.map.queryRenderedFeatures(e.point, { layers: [me.id] });
//var sta = me.map.getFeatureState(features[0]);
if (me.highlight && me.highlight.callback) {
me.highlight.callback(features, e);
}
if (!features || features.length < 1) {
me._clearHighlight.call(me);
return;
}
var id = features[0].properties.fid;
if (featureId === id) {
return;
}
featureId = id;
me._clearHighlight.call(me);
var sourceFeatures = me.map.querySourceFeatures(me.sourceId, {
filter: ['==', 'fid', id]
});
var i,
len = sourceFeatures.length;
var geoFeatures = { type: 'FeatureCollection', features: [] };
for (i = 0; i < len; i++) {
geoFeatures['features'].push(sourceFeatures[i].toJSON());
}
me.map.getSource('highlight').setData(geoFeatures);
});
}
// mousemoveCallback (e,obj) {
// obj.mousemoveOn= true;
// var canvas = document.querySelector('.mapboxgl-canvas-container.mapboxgl-interactive');
// canvas.style.cursor = 'auto';
// var features = obj.map.queryRenderedFeatures(e.point, {layers: [obj.id]});
// if (obj.highlight && obj.highlight.callback) {
// obj.highlight.callback(features, e);
// }
// if (!features || features.length < 1) {
// obj._clearHighlight.call(obj);
// return;
// }
// var id = features[0].properties.fid;
// if (featureId === id) {
// return;
// }
// featureId = id;
// obj._clearHighlight.call(obj);
// var sourceFeatures = obj.map.querySourceFeatures(obj.sourceId, {filter: ['==','fid',id]});
// var i, len = sourceFeatures.length;
// var geoFeatures = {'type': 'FeatureCollection', 'features': []};
// for (i = 0; i < len; i++) {
// geoFeatures['features'].push(sourceFeatures[i].toJSON());
// }
// obj.map.getSource('highlight').setData(geoFeatures);
// }
_clearHighlight() {
if (this.map) {
if (!!this.map.getSource('highlight')) {
this.map.getSource('highlight').setData({
type: 'FeatureCollection',
features: []
});
}
}
}
_appendLegendCSSStyle() {
var legendStyle = document.createElement('style');
legendStyle.type = 'text/css';
var baseStyle = `
.legend {
display: inline-block;
border-radius: 2px;
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
font-size: 12px;
color: rgba(0, 0, 0, 0.8);
background-color: rgb(255, 255, 255);
}
.legend-light {
color: rgba(0, 0, 0, 0.8);
background-color: rgb(255, 255, 255);
box-shadow: 0px 0px 6px #bbbbbb;
-moz-box-shadow: 0px 6px 10px #bbbbbb;
-webkit-box-shadow: 0px 0px 6px #bbbbbb;
}
.legend-dark {
color: rgba(255, 255, 255, 0.8);
background-color: rgb(64, 64, 64);
}
.legend .legend-title {
min-height: 14px;
max-width: 500px;
padding:6px 10px;
}
.legend-light .legend-title {
color: rgba(0, 0, 0, 0.8);
}
.legend-dark .legend-title {
color: rgba(255, 255, 255, 0.8);
}
.legend-content{
padding:6px 10px;
}
`;
legendStyle.innerHTML = baseStyle + this._legendCSSStyle();
document.getElementsByTagName('head')[0].appendChild(legendStyle);
}
//各种图层对应的自己的图例的样式
_legendCSSStyle() {
//子类可重写实现
return `
.legend ul {
padding: 0;
margin: 0 16px;
height: 100%;
display: block;
list-style: none;
}
.legend li {
vertical-align: middle;
}
.legend li span:first-child {
vertical-align: middle;
}
.legend li span:last-child {
line-height: 28px;
max-width: 200px;
vertical-align: middle;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
-ms-text-overflow: ellipsis;
}
.legend-vertical li {
height: 28px;
}
.legend-vertical li span:first-child {
display: inline-block;
width: 60px;
height: 100%;
}
.legend-vertical li span:last-child {
display: inline-block;
margin-left: 16px;
height: 100%;
}
.legend-horizontal li {
height: 56px;
float: left;
}
.legend-horizontal li span:first-child {
display: block;
width: 100%;
height: 50%;
}
.legend-horizontal li span:last-child {
display: block;
vertical-align: middle;
width: 60px;
height: 50%;
text-align: center;
}
`;
}
}
export { Theme3DLayer };
Zondy.Map.Theme3DLayer = Theme3DLayer;