@mapgis/webclient-es6-mapboxgl
Version:
483 lines (457 loc) • 20.5 kB
JavaScript
import mapboxgl from '@mapgis/mapbox-gl';
import { Common } from '@mapgis/webclient-es6-service';
import { ThemeLayer } from './ThemeLayer';
import { Theme as FeatureTheme } from './common/overlay/feature/Theme';
const { FeatureSet, Zondy, Point2D } = Common;
/**
* @class Zondy.Map.graphThemeLayer
* @classdesc 统计专题图层。
* @param {string} name - 图层名。
* @param {string} chartsType - 图表类别。
* @param {Object} options - 参数。
* @param {string} [options.id] - 专题图层 ID。
* @param {boolean} [options.loadWhileAnimating=true] - 是否实时重绘。
* @param {mapboxgl.Map} options.map - 当前mapboxgl map对象。
* @param {number} [options.opacity=1] - 图层透明度。
* @param {string} options.themeFields - 指定创建专题图字段。
* @param {boolean} [options.isOverLay=true] - 是否进行压盖处理,如果设为 true,图表绘制过程中将隐藏对已在图层中绘制的图表产生压盖的图表。
* @param {string} [options.chartsType] - 图表类型。目前可用:"Bar", "Line", "Pie"。
* @param {Object} options.chartsSetting - 符号 Circle 配置对象。
* @param {Array.<number>} options.chartsSetting.codomain - 图表允许展示的数据值域,长度为 2 的一维数组,第一个元素表示值域下限,第二个元素表示值域上限。
* @param {number} [options.chartsSetting.maxR] - 圆形的最大半径。
* @param {number} [options.chartsSetting.minR] - 圆形的最小半径。
* @param {string} options.chartsSetting.fillColor - 圆形的填充色,如:fillColor: "#FFB980"。
* @param {Object} options.chartsSetting.circleStyle - 圆形的基础 style,此参数控制圆形基础样式,优先级低于 circleStyleByFields 和 circleStyleByCodomain。
* @param {number} options.chartsSetting.decimalNumber - 数据值数组 dataValues 元素值小数位数,数据的小数位处理参数,取值范围:[0, 16]。如果不设置此参数,在取数据值时不对数据做小数位处理。
* @param {Object} options.chartsSetting.circleHoverStyle - 圆形 hover 状态时的样式,circleHoverAble 为 true 时有效。
* @param {boolean} [options.chartsSetting.circleHoverAble=true] - 是否允许圆形使用 hover 状态。同时设置 circleHoverAble 和 circleClickAble 为 false,可以直接屏蔽图形对专题图层事件的响应。
* @param {boolean} [options.chartsSetting.circleClickAble=true] - 是否允许圆形被点击。同时设置 circleHoverAble 和 circleClickAble 为 false,可以直接屏蔽图形对专题图层事件的响应。
* @extends {Zondy.Map.ThemeLayer}
* @fires Zondy.Map.graphThemeLayer#beforefeaturesadded
*/
class GraphThemeLayer extends ThemeLayer {
constructor(name, chartsType, options) {
super(name, options);
this.chartsSetting = options.chartsSetting || {};
this.themeFields = options.themeFields || null;
this.overlayWeightField = options.overlayWeightField || null;
this.isOverLay = options.isOverLay || true;
this.charts = options.charts || [];
this.cache = options.cache || {};
this.chartsType = chartsType;
this.options = {
calGravity: options.calGravity || true
};
}
/**
* @function Zondy.Map.graphThemeLayer.prototype.setChartsType
* @description 设置图表类型,此函数可动态改变图表类型。在调用此函数前请通过 chartsSetting 为新类型的图表做相关配置。
* @param {string} [chartsType] - 图表类型。目前可用:"Bar", "Line", "Pie"。
*/
setChartsType(chartsType) {
this.chartsType = chartsType;
this.redraw();
}
/**
* @function Zondy.Map.graphThemeLayer.prototype.addFeatures
* @description 向专题图图层中添加数据,支持的 feature 类型为:iServer 返回的 feature JSON 对象。
* @param {Object} features - 待添加的要素。
*/
addFeatures(features) {
var me = this;
mapboxgl.Evented.prototype.fire('beforefeaturesadded', {
features: features
});
if (features && (true || features instanceof FeatureSet)) {
var attrs = null;
var LabelDots = features.LabelDots;
var attstruct = features.AttStruct;
var feaArr = features.SFEleArray;
if (feaArr != null && feaArr.length > 0) {
for (var j = 0; j < feaArr.length; j++) {
var feature = feaArr[j];
if (feature.AttValue != null && feature.AttValue.length > 0) {
var attrs = {};
for (var i = 0; i < feature.AttValue.length; i++) {
attrs[attstruct.FldName[i]] = feature.AttValue[i];
}
attrs['FID'] = feature.FID;
}
feature.attributes = attrs;
LabelDots && LabelDots[j] && (feature.LabelDot = LabelDots[j]);
me.features.push(feature);
}
}
}
//绘制专题要素
if (this.renderer) {
this.redrawThematicFeatures(this.map.getBounds());
}
}
/**
* @function Zondy.Map.graphThemeLayer.prototype.redrawThematicFeatures
* @description 重绘所有专题要素。
* 此方法包含绘制专题要素的所有步骤,包含用户数据到专题要素的转换,抽稀,缓存等步骤。
* 地图漫游时调用此方法进行图层刷新。
* @param {mapboxgl.LngLatBounds} extent - 重绘的范围。
*/
redrawThematicFeatures(extent) {
// eslint-disable-line no-unused-vars
this.clearCache();
//清除当前所有可视元素
this.renderer.clearAll();
var features = this.features;
// var bounds = null;
// if (extent && extent instanceof window.mapboxgl.LngLatBounds) {
// var lb = extent.getSouthWest();
// var rt = extent.getNorthEast();
// var lbM = this.lonLat2WebMercator(lb.lng,lb.lat);
// var rtM = this.lonLat2WebMercator(rt.lng,rt.lat);
// bounds = new Rectangle(lbM.x,lbM.y,rtM.x,rtM.y);
// }
for (var i = 0, len = features.length; i < len; i++) {
var feature = features[i];
// // 要素范围判断
// var feaBounds = feature.bound;
// //剔除当前视图(地理)范围以外的数据
// if (bounds && !bounds.intersectsBounds(feaBounds)) {
// continue;
// }
var cache = this.cache;
// 用feature id 做缓存标识
var cacheField = feature.FID;
// 数据对应的图表是否已缓存,没缓存则重新创建图表
if (cache[cacheField]) {
continue;
}
cache[cacheField] = cacheField;
var chart = this.createThematicFeature(feature);
// 压盖处理权重值
if (chart && this.overlayWeightField) {
if (feature.attributes[this.overlayWeightField] && !isNaN(feature.attributes[this.overlayWeightField])) {
chart['__overlayWeight'] = feature.attributes[this.overlayWeightField];
}
}
if (chart) {
this.charts.push(chart);
}
}
this.drawCharts();
}
/**
* @function Zondy.Map.graphThemeLayer.prototype.createThematicFeature
* @description 向专题图图层中添加数据, 支持的 feature 类型为:iServer 返回的 feature json 对象。
* @param {Object} feature - 待添加的要素。
*
*/
createThematicFeature(feature) {
var thematicFeature;
// 检查图表创建条件并创建图形
if (FeatureTheme[this.chartsType] && this.themeFields && this.chartsSetting) {
thematicFeature = new FeatureTheme[this.chartsType](feature, this, this.themeFields, this.chartsSetting, null, this.options);
}
// thematicFeature 是否创建成功
if (!thematicFeature) {
return false;
}
// 对专题要素执行图形装载
thematicFeature.assembleShapes();
return thematicFeature;
}
/**
* @function Zondy.Map.graphThemeLayer.prototype.drawCharts
* @description 绘制图表。包含压盖处理。
*
*/
drawCharts() {
// 判断 rendere r就绪
if (!this.renderer) {
return;
}
var charts = this.charts;
// 图表权重值处理des
if (this.overlayWeightField) {
charts.sort(function (cs, ce) {
if (typeof cs['__overlayWeight'] == 'undefined' && typeof ce['__overlayWeight'] == 'undefined') {
return 0;
} else if (typeof cs['__overlayWeight'] != 'undefined' && typeof ce['__overlayWeight'] == 'undefined') {
return -1;
} else if (typeof cs['__overlayWeight'] == 'undefined' && typeof ce['__overlayWeight'] != 'undefined') {
return 1;
} else if (typeof cs['__overlayWeight'] != 'undefined' && typeof ce['__overlayWeight'] != 'undefined') {
if (parseFloat(cs['__overlayWeight']) < parseFloat(ce['__overlayWeight'])) {
return 1;
} else {
return -1;
}
}
return 0;
});
}
// 不进行避让
if (!this.isOverLay) {
for (var m = 0, len_m = charts.length; m < len_m; m++) {
var chart_m = charts[m];
// 图形参考位置 (reSetLocation 会更新 chartBounds)
var shapeROP_m = chart_m.resetLocation();
// 添加图形
var shapes_m = chart_m.shapes;
for (var n = 0, slen_n = shapes_m.length; n < slen_n; n++) {
shapes_m[n].refOriginalPosition = shapeROP_m;
this.renderer.addShape(shapes_m[n]);
}
}
} else {
// 压盖判断所需 chartsBounds 集合
var chartsBounds = [];
// 压盖处理 & 添加图形
for (let i = 0, len = charts.length; i < len; i++) {
var chart = charts[i];
// 图形参考位置 (reSetLocation 会更新 chartBounds)
var shapeROP = chart.resetLocation();
// 图表框
var cbs = chart.chartBounds;
var cBounds = [
{
x: cbs.xmin,
y: cbs.ymin
},
{
x: cbs.xmin,
y: cbs.ymax
},
{
x: cbs.xmax,
y: cbs.ymax
},
{
x: cbs.xmax,
y: cbs.ymin
},
{
x: cbs.xmin,
y: cbs.ymax
}
];
// // 地图范围外不绘制
// if (mBounds) {
// // if (!this.isChartInMap(mBounds, cBounds)) continue;
// }
// 是否压盖
var isOL = false;
if (i !== 0) {
for (let j = 0; j < chartsBounds.length; j++) {
//压盖判断
if (this.isQuadrilateralOverLap(cBounds, chartsBounds[j])) {
isOL = true;
break;
}
}
}
if (isOL) {
continue;
} else {
chartsBounds.push(cBounds);
}
// 添加图形
var shapes = chart.shapes;
for (let j = 0, slen = shapes.length; j < slen; j++) {
shapes[j].refOriginalPosition = shapeROP;
this.renderer.addShape(shapes[j]);
}
}
}
// 绘制图形
this.renderer.render();
}
/**
* @function Zondy.Map.graphThemeLayer.prototype.getShapesByFeatureID
* @description 通过 FeatureID 获取 feature 关联的所有图形。如果不传入此参数,函数将返回所有图形。
* @param {number} featureID - 要素 ID。
*/
getShapesByFeatureID(featureID) {
var list = [];
var shapeList = this.renderer.getAllShapes();
if (!featureID) {
return shapeList;
}
for (var i = 0, len = shapeList.length; i < len; i++) {
var si = shapeList[i];
if (si.refDataID && featureID === si.refDataID) {
list.push(si);
}
}
return list;
}
/**
* @function Zondy.Map.graphThemeLayer.prototype.isQuadrilateralOverLap
* @description 判断两个四边形是否有压盖。
* @param {Array.<Object>} quadrilateral - 四边形节点数组。
* @param {Array.<Object>} quadrilateral2 - 第二个四边形节点数组。
*/
isQuadrilateralOverLap(quadrilateral, quadrilateral2) {
var quadLen = quadrilateral.length,
quad2Len = quadrilateral2.length;
if (quadLen !== 5 || quad2Len !== 5) {
return null;
} //不是四边形
var OverLap = false;
//如果两四边形互不包含对方的节点,则两个四边形不相交
for (let i = 0; i < quadLen; i++) {
if (this.isPointInPoly(quadrilateral[i], quadrilateral2)) {
OverLap = true;
break;
}
}
for (let i = 0; i < quad2Len; i++) {
if (this.isPointInPoly(quadrilateral2[i], quadrilateral)) {
OverLap = true;
break;
}
}
//加上两矩形十字相交的情况
for (let i = 0; i < quadLen - 1; i++) {
if (OverLap) {
break;
}
for (let j = 0; j < quad2Len - 1; j++) {
var isLineIn = this.lineIntersection(quadrilateral[i], quadrilateral[i + 1], quadrilateral2[j], quadrilateral2[j + 1]);
if (isLineIn.CLASS_NAME === 'Zondy.Object.Point2D') {
OverLap = true;
break;
}
}
}
return OverLap;
}
/**
* @description 判断两条线段是不是有交点。
* @param a1 - {Zondy.Geometry.Point} 第一条线段的起始节点。
* @param a2 - {Zondy.Geometry.Point} 第一条线段的结束节点。
* @param b1 - {Zondy.Geometry.Point} 第二条线段的起始节点。
* @param b2 - {Zondy.Geometry.Point} 第二条线段的结束节点。
* @return {Object} 如果相交返回交点,如果不相交返回两条线段的位置关系。
*/
lineIntersection(a1, a2, b1, b2) {
var intersectValue = null;
var k1;
var k2;
var b = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x);
var a = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x);
var ab = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y);
//ab==0代表两条线断的斜率一样
if (ab !== 0) {
k1 = b / ab;
k2 = a / ab;
if (k1 >= 0 && k2 <= 1 && k1 <= 1 && k2 >= 0) {
intersectValue = new Point2D(a1.x + k1 * (a2.x - a1.x), a1.y + k1 * (a2.y - a1.y));
} else {
intersectValue = 'No Intersection';
}
} else {
if (b === 0 && a === 0) {
var maxy = Math.max(a1.y, a2.y);
var miny = Math.min(a1.y, a2.y);
var maxx = Math.max(a1.x, a2.x);
var minx = Math.min(a1.x, a2.x);
if (
(((b1.y >= miny && b1.y <= maxy) || (b2.y >= miny && b2.y <= maxy)) && b1.x >= minx && b1.x <= maxx) ||
(b2.x >= minx && b2.x <= maxx)
) {
intersectValue = 'Coincident'; //重合
} else {
intersectValue = 'Parallel'; //平行
}
} else {
intersectValue = 'Parallel'; //平行
}
}
return intersectValue;
}
/**
* @function Zondy.Map.graphThemeLayer.prototype.isPointInPoly
* @description 判断一个点是否在多边形里面。(射线法)
* @param {Object} pt - 需要判定的点对象,该对象含有属性 x (横坐标),属性 y (纵坐标)。
* @param {Array.<Object>} poly - 多边形节点数组。
*/
isPointInPoly(pt, poly) {
for (var isIn = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i) {
((poly[i].y <= pt.y && pt.y < poly[j].y) || (poly[j].y <= pt.y && pt.y < poly[i].y)) &&
pt.x < ((poly[j].x - poly[i].x) * (pt.y - poly[i].y)) / (poly[j].y - poly[i].y) + poly[i].x &&
(isIn = !isIn);
}
return isIn;
}
/**
* @function Zondy.Map.graphThemeLayer.prototype.isChartInMap
* @description 判断图表是否在地图里。
* @param {Zondy.Bounds} mapPxBounds - 地图像素范围。
* @param {Array.<Object>} chartPxBounds - 图表范围的四边形节点数组。
*/
isChartInMap(mapPxBounds, chartPxBounds) {
var mb = mapPxBounds;
var isIn = false;
for (var i = 0, len = chartPxBounds.length; i < len; i++) {
var cb = chartPxBounds[i];
if (cb.x >= mb.xmin && cb.x <= mb.xmax && cb.y >= mb.ymin && cb.y <= mb.ymax) {
isIn = true;
break;
}
}
return isIn;
}
/**
* @function Zondy.Map.graphThemeLayer.prototype.clearCache
* @description 清除缓存
*/
clearCache() {
this.cache = {};
this.charts = [];
}
/**
* @function Zondy.Map.graphThemeLayer.prototype.removeFeatures
* @description 从专题图中删除 feature。这个函数删除所有传递进来的矢量要素。参数中的 features 数组中的每一项,必须是已经添加到当前图层中的 feature。
* @param {Zondy.Feature.Vector} features - 要删除的要素。
*/
removeFeatures(features) {
this.clearCache();
super.removeFeatures(features);
}
/**
* @function Zondy.Map.graphThemeLayer.prototype.removeAllFeatures
* @description 移除所有的要素。
*/
removeAllFeatures() {
this.clearCache();
super.removeAllFeatures();
}
/**
* @function Zondy.Map.graphThemeLayer.prototype.redraw
* @description 重绘该图层。
*/
redraw() {
this.clearCache();
if (this.renderer) {
this.redrawThematicFeatures(this.map.getBounds());
return true;
}
return false;
}
/**
* @function Zondy.Map.graphThemeLayer.prototype.clear
* @description 清除的内容包括数据(features) 、专题要素、缓存。
*/
clear() {
if (this.renderer) {
this.renderer.clearAll();
this.renderer.refresh();
}
this.removeAllFeatures();
this.clearCache();
}
}
export var graphThemeLayer = function (name, chartsType, options) {
return new GraphThemeLayer(name, chartsType, options);
};
export { GraphThemeLayer };
Zondy.Map.graphThemeLayer = graphThemeLayer;