407 lines
14 KiB
Plaintext
407 lines
14 KiB
Plaintext
define(function (require) {
|
|
|
|
var zrUtil = require('zrender/core/util');
|
|
var List = require('../../data/List');
|
|
var formatUtil = require('../../util/format');
|
|
var modelUtil = require('../../util/model');
|
|
var numberUtil = require('../../util/number');
|
|
|
|
var addCommas = formatUtil.addCommas;
|
|
var encodeHTML = formatUtil.encodeHTML;
|
|
|
|
var markerHelper = require('./markerHelper');
|
|
|
|
var LineDraw = require('../../chart/helper/LineDraw');
|
|
|
|
var markLineTransform = function (seriesModel, coordSys, mlModel, item) {
|
|
var data = seriesModel.getData();
|
|
// Special type markLine like 'min', 'max', 'average'
|
|
var mlType = item.type;
|
|
|
|
if (!zrUtil.isArray(item)
|
|
&& (
|
|
mlType === 'min' || mlType === 'max' || mlType === 'average'
|
|
// In case
|
|
// data: [{
|
|
// yAxis: 10
|
|
// }]
|
|
|| (item.xAxis != null || item.yAxis != null)
|
|
)
|
|
) {
|
|
var valueAxis;
|
|
var valueDataDim;
|
|
var value;
|
|
|
|
if (item.yAxis != null || item.xAxis != null) {
|
|
valueDataDim = item.yAxis != null ? 'y' : 'x';
|
|
valueAxis = coordSys.getAxis(valueDataDim);
|
|
|
|
value = zrUtil.retrieve(item.yAxis, item.xAxis);
|
|
}
|
|
else {
|
|
var axisInfo = markerHelper.getAxisInfo(item, data, coordSys, seriesModel);
|
|
valueDataDim = axisInfo.valueDataDim;
|
|
valueAxis = axisInfo.valueAxis;
|
|
value = markerHelper.numCalculate(data, valueDataDim, mlType);
|
|
}
|
|
var valueIndex = valueDataDim === 'x' ? 0 : 1;
|
|
var baseIndex = 1 - valueIndex;
|
|
|
|
var mlFrom = zrUtil.clone(item);
|
|
var mlTo = {};
|
|
|
|
mlFrom.type = null;
|
|
|
|
mlFrom.coord = [];
|
|
mlTo.coord = [];
|
|
mlFrom.coord[baseIndex] = -Infinity;
|
|
mlTo.coord[baseIndex] = Infinity;
|
|
|
|
var precision = mlModel.get('precision');
|
|
if (precision >= 0) {
|
|
value = +value.toFixed(precision);
|
|
}
|
|
|
|
mlFrom.coord[valueIndex] = mlTo.coord[valueIndex] = value;
|
|
|
|
item = [mlFrom, mlTo, { // Extra option for tooltip and label
|
|
type: mlType,
|
|
valueIndex: item.valueIndex,
|
|
// Force to use the value of calculated value.
|
|
value: value
|
|
}];
|
|
}
|
|
|
|
item = [
|
|
markerHelper.dataTransform(seriesModel, item[0]),
|
|
markerHelper.dataTransform(seriesModel, item[1]),
|
|
zrUtil.extend({}, item[2])
|
|
];
|
|
|
|
// Avoid line data type is extended by from(to) data type
|
|
item[2].type = item[2].type || '';
|
|
|
|
// Merge from option and to option into line option
|
|
zrUtil.merge(item[2], item[0]);
|
|
zrUtil.merge(item[2], item[1]);
|
|
|
|
return item;
|
|
};
|
|
|
|
function isInifinity(val) {
|
|
return !isNaN(val) && !isFinite(val);
|
|
}
|
|
|
|
// If a markLine has one dim
|
|
function ifMarkLineHasOnlyDim(dimIndex, fromCoord, toCoord, coordSys) {
|
|
var otherDimIndex = 1 - dimIndex;
|
|
var dimName = coordSys.dimensions[dimIndex];
|
|
return isInifinity(fromCoord[otherDimIndex]) && isInifinity(toCoord[otherDimIndex])
|
|
&& fromCoord[dimIndex] === toCoord[dimIndex] && coordSys.getAxis(dimName).containData(fromCoord[dimIndex]);
|
|
}
|
|
|
|
function markLineFilter(coordSys, item) {
|
|
if (coordSys.type === 'cartesian2d') {
|
|
var fromCoord = item[0].coord;
|
|
var toCoord = item[1].coord;
|
|
// In case
|
|
// {
|
|
// markLine: {
|
|
// data: [{ yAxis: 2 }]
|
|
// }
|
|
// }
|
|
if (
|
|
fromCoord && toCoord &&
|
|
(ifMarkLineHasOnlyDim(1, fromCoord, toCoord, coordSys)
|
|
|| ifMarkLineHasOnlyDim(0, fromCoord, toCoord, coordSys))
|
|
) {
|
|
return true;
|
|
}
|
|
}
|
|
return markerHelper.dataFilter(coordSys, item[0])
|
|
&& markerHelper.dataFilter(coordSys, item[1]);
|
|
}
|
|
|
|
function updateSingleMarkerEndLayout(
|
|
data, idx, isFrom, mlType, valueIndex, seriesModel, api
|
|
) {
|
|
var coordSys = seriesModel.coordinateSystem;
|
|
var itemModel = data.getItemModel(idx);
|
|
|
|
var point;
|
|
var xPx = itemModel.get('x');
|
|
var yPx = itemModel.get('y');
|
|
if (xPx != null && yPx != null) {
|
|
point = [
|
|
numberUtil.parsePercent(xPx, api.getWidth()),
|
|
numberUtil.parsePercent(yPx, api.getHeight())
|
|
];
|
|
}
|
|
else {
|
|
// Chart like bar may have there own marker positioning logic
|
|
if (seriesModel.getMarkerPosition) {
|
|
// Use the getMarkerPoisition
|
|
point = seriesModel.getMarkerPosition(
|
|
data.getValues(data.dimensions, idx)
|
|
);
|
|
}
|
|
else {
|
|
var dims = coordSys.dimensions;
|
|
var x = data.get(dims[0], idx);
|
|
var y = data.get(dims[1], idx);
|
|
point = coordSys.dataToPoint([x, y]);
|
|
}
|
|
// Expand line to the edge of grid if value on one axis is Inifnity
|
|
// In case
|
|
// markLine: {
|
|
// data: [{
|
|
// yAxis: 2
|
|
// // or
|
|
// type: 'average'
|
|
// }]
|
|
// }
|
|
if (coordSys.type === 'cartesian2d') {
|
|
var xAxis = coordSys.getAxis('x');
|
|
var yAxis = coordSys.getAxis('y');
|
|
var dims = coordSys.dimensions;
|
|
if (isInifinity(data.get(dims[0], idx))) {
|
|
point[0] = xAxis.toGlobalCoord(xAxis.getExtent()[isFrom ? 0 : 1]);
|
|
}
|
|
else if (isInifinity(data.get(dims[1], idx))) {
|
|
point[1] = yAxis.toGlobalCoord(yAxis.getExtent()[isFrom ? 0 : 1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
data.setItemLayout(idx, point);
|
|
}
|
|
|
|
var markLineFormatMixin = {
|
|
formatTooltip: function (dataIndex) {
|
|
var data = this._data;
|
|
var value = this.getRawValue(dataIndex);
|
|
var formattedValue = zrUtil.isArray(value)
|
|
? zrUtil.map(value, addCommas).join(', ') : addCommas(value);
|
|
var name = data.getName(dataIndex);
|
|
return this.name + '<br />'
|
|
+ ((name ? encodeHTML(name) + ' : ' : '') + formattedValue);
|
|
},
|
|
|
|
getData: function () {
|
|
return this._data;
|
|
},
|
|
|
|
setData: function (data) {
|
|
this._data = data;
|
|
}
|
|
};
|
|
|
|
zrUtil.defaults(markLineFormatMixin, modelUtil.dataFormatMixin);
|
|
|
|
require('../../echarts').extendComponentView({
|
|
|
|
type: 'markLine',
|
|
|
|
init: function () {
|
|
/**
|
|
* Markline grouped by series
|
|
* @private
|
|
* @type {Object}
|
|
*/
|
|
this._markLineMap = {};
|
|
},
|
|
|
|
render: function (markLineModel, ecModel, api) {
|
|
var lineDrawMap = this._markLineMap;
|
|
for (var name in lineDrawMap) {
|
|
lineDrawMap[name].__keep = false;
|
|
}
|
|
|
|
ecModel.eachSeries(function (seriesModel) {
|
|
var mlModel = seriesModel.markLineModel;
|
|
mlModel && this._renderSeriesML(seriesModel, mlModel, ecModel, api);
|
|
}, this);
|
|
|
|
for (var name in lineDrawMap) {
|
|
if (!lineDrawMap[name].__keep) {
|
|
this.group.remove(lineDrawMap[name].group);
|
|
}
|
|
}
|
|
},
|
|
|
|
updateLayout: function (markLineModel, ecModel, api) {
|
|
ecModel.eachSeries(function (seriesModel) {
|
|
var mlModel = seriesModel.markLineModel;
|
|
if (mlModel) {
|
|
var mlData = mlModel.getData();
|
|
var fromData = mlModel.__from;
|
|
var toData = mlModel.__to;
|
|
// Update visual and layout of from symbol and to symbol
|
|
fromData.each(function (idx) {
|
|
var lineModel = mlData.getItemModel(idx);
|
|
var mlType = lineModel.get('type');
|
|
var valueIndex = lineModel.get('valueIndex');
|
|
updateSingleMarkerEndLayout(fromData, idx, true, mlType, valueIndex, seriesModel, api);
|
|
updateSingleMarkerEndLayout(toData, idx, false, mlType, valueIndex, seriesModel, api);
|
|
});
|
|
// Update layout of line
|
|
mlData.each(function (idx) {
|
|
mlData.setItemLayout(idx, [
|
|
fromData.getItemLayout(idx),
|
|
toData.getItemLayout(idx)
|
|
]);
|
|
});
|
|
|
|
this._markLineMap[seriesModel.name].updateLayout();
|
|
}
|
|
}, this);
|
|
},
|
|
|
|
_renderSeriesML: function (seriesModel, mlModel, ecModel, api) {
|
|
var coordSys = seriesModel.coordinateSystem;
|
|
var seriesName = seriesModel.name;
|
|
var seriesData = seriesModel.getData();
|
|
|
|
var lineDrawMap = this._markLineMap;
|
|
var lineDraw = lineDrawMap[seriesName];
|
|
if (!lineDraw) {
|
|
lineDraw = lineDrawMap[seriesName] = new LineDraw();
|
|
}
|
|
this.group.add(lineDraw.group);
|
|
|
|
var mlData = createList(coordSys, seriesModel, mlModel);
|
|
|
|
var fromData = mlData.from;
|
|
var toData = mlData.to;
|
|
var lineData = mlData.line;
|
|
|
|
mlModel.__from = fromData;
|
|
mlModel.__to = toData;
|
|
// Line data for tooltip and formatter
|
|
zrUtil.extend(mlModel, markLineFormatMixin);
|
|
mlModel.setData(lineData);
|
|
|
|
var symbolType = mlModel.get('symbol');
|
|
var symbolSize = mlModel.get('symbolSize');
|
|
if (!zrUtil.isArray(symbolType)) {
|
|
symbolType = [symbolType, symbolType];
|
|
}
|
|
if (typeof symbolSize === 'number') {
|
|
symbolSize = [symbolSize, symbolSize];
|
|
}
|
|
|
|
// Update visual and layout of from symbol and to symbol
|
|
mlData.from.each(function (idx) {
|
|
var lineModel = lineData.getItemModel(idx);
|
|
var mlType = lineModel.get('type');
|
|
var valueIndex = lineModel.get('valueIndex');
|
|
updateDataVisualAndLayout(fromData, idx, true, mlType, valueIndex);
|
|
updateDataVisualAndLayout(toData, idx, false, mlType, valueIndex);
|
|
});
|
|
|
|
// Update visual and layout of line
|
|
lineData.each(function (idx) {
|
|
var lineColor = lineData.getItemModel(idx).get('lineStyle.normal.color');
|
|
lineData.setItemVisual(idx, {
|
|
color: lineColor || fromData.getItemVisual(idx, 'color')
|
|
});
|
|
lineData.setItemLayout(idx, [
|
|
fromData.getItemLayout(idx),
|
|
toData.getItemLayout(idx)
|
|
]);
|
|
|
|
lineData.setItemVisual(idx, {
|
|
'fromSymbolSize': fromData.getItemVisual(idx, 'symbolSize'),
|
|
'fromSymbol': fromData.getItemVisual(idx, 'symbol'),
|
|
'toSymbolSize': toData.getItemVisual(idx, 'symbolSize'),
|
|
'toSymbol': toData.getItemVisual(idx, 'symbol')
|
|
});
|
|
});
|
|
|
|
lineDraw.updateData(lineData);
|
|
|
|
// Set host model for tooltip
|
|
// FIXME
|
|
mlData.line.eachItemGraphicEl(function (el, idx) {
|
|
el.traverse(function (child) {
|
|
child.dataModel = mlModel;
|
|
});
|
|
});
|
|
|
|
function updateDataVisualAndLayout(data, idx, isFrom, mlType, valueIndex) {
|
|
var itemModel = data.getItemModel(idx);
|
|
|
|
updateSingleMarkerEndLayout(
|
|
data, idx, isFrom, mlType, valueIndex, seriesModel, api
|
|
);
|
|
|
|
data.setItemVisual(idx, {
|
|
symbolSize: itemModel.get('symbolSize') || symbolSize[isFrom ? 0 : 1],
|
|
symbol: itemModel.get('symbol', true) || symbolType[isFrom ? 0 : 1],
|
|
color: itemModel.get('itemStyle.normal.color') || seriesData.getVisual('color')
|
|
});
|
|
}
|
|
|
|
lineDraw.__keep = true;
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @inner
|
|
* @param {module:echarts/coord/*} coordSys
|
|
* @param {module:echarts/model/Series} seriesModel
|
|
* @param {module:echarts/model/Model} mpModel
|
|
*/
|
|
function createList(coordSys, seriesModel, mlModel) {
|
|
|
|
var coordDimsInfos;
|
|
if (coordSys) {
|
|
coordDimsInfos = zrUtil.map(coordSys && coordSys.dimensions, function (coordDim) {
|
|
var info = seriesModel.getData().getDimensionInfo(
|
|
seriesModel.coordDimToDataDim(coordDim)[0]
|
|
) || {}; // In map series data don't have lng and lat dimension. Fallback to same with coordSys
|
|
info.name = coordDim;
|
|
return info;
|
|
});
|
|
}
|
|
else {
|
|
coordDimsInfos =[{
|
|
name: 'value',
|
|
type: 'float'
|
|
}];
|
|
}
|
|
|
|
var fromData = new List(coordDimsInfos, mlModel);
|
|
var toData = new List(coordDimsInfos, mlModel);
|
|
// No dimensions
|
|
var lineData = new List([], mlModel);
|
|
|
|
var optData = zrUtil.map(mlModel.get('data'), zrUtil.curry(
|
|
markLineTransform, seriesModel, coordSys, mlModel
|
|
));
|
|
if (coordSys) {
|
|
optData = zrUtil.filter(
|
|
optData, zrUtil.curry(markLineFilter, coordSys)
|
|
);
|
|
}
|
|
var dimValueGetter = coordSys ? markerHelper.dimValueGetter : function (item) {
|
|
return item.value;
|
|
};
|
|
fromData.initData(
|
|
zrUtil.map(optData, function (item) { return item[0]; }),
|
|
null, dimValueGetter
|
|
);
|
|
toData.initData(
|
|
zrUtil.map(optData, function (item) { return item[1]; }),
|
|
null, dimValueGetter
|
|
);
|
|
lineData.initData(
|
|
zrUtil.map(optData, function (item) { return item[2]; })
|
|
);
|
|
return {
|
|
from: fromData,
|
|
to: toData,
|
|
line: lineData
|
|
};
|
|
}
|
|
}); |