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
};
}
});
|