blob: 7cc6006c05668176f54b926e61099cc886bb07bd [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import * as zrUtil from 'zrender/src/core/util';
import SeriesModel from '../../model/Series';
import Tree from '../../data/Tree';
import Model from '../../model/Model';
import { encodeHTML, addCommas } from '../../util/format';
import { wrapTreePathInfo } from '../helper/treeHelper';
export default SeriesModel.extend({
type: 'series.treemap',
layoutMode: 'box',
dependencies: ['grid', 'polar'],
preventUsingHoverLayer: true,
/**
* @type {module:echarts/data/Tree~Node}
*/
_viewRoot: null,
defaultOption: {
// Disable progressive rendering
progressive: 0,
// center: ['50%', '50%'], // not supported in ec3.
// size: ['80%', '80%'], // deprecated, compatible with ec2.
left: 'center',
top: 'middle',
right: null,
bottom: null,
width: '80%',
height: '80%',
sort: true,
// Can be null or false or true
// (order by desc default, asc not supported yet (strange effect))
clipWindow: 'origin',
// Size of clipped window when zooming. 'origin' or 'fullscreen'
squareRatio: 0.5 * (1 + Math.sqrt(5)),
// golden ratio
leafDepth: null,
// Nodes on depth from root are regarded as leaves.
// Count from zero (zero represents only view root).
drillDownIcon: '▶',
// Use html character temporarily because it is complicated
// to align specialized icon. ▷▶❒❐▼✚
zoomToNodeRatio: 0.32 * 0.32,
// Be effective when using zoomToNode. Specify the proportion of the
// target node area in the view area.
roam: true,
// true, false, 'scale' or 'zoom', 'move'.
nodeClick: 'zoomToNode',
// Leaf node click behaviour: 'zoomToNode', 'link', false.
// If leafDepth is set and clicking a node which has children but
// be on left depth, the behaviour would be changing root. Otherwise
// use behavious defined above.
animation: true,
animationDurationUpdate: 900,
animationEasing: 'quinticInOut',
breadcrumb: {
show: true,
height: 22,
left: 'center',
top: 'bottom',
// right
// bottom
emptyItemWidth: 25,
// Width of empty node.
itemStyle: {
color: 'rgba(0,0,0,0.7)',
//'#5793f3',
borderColor: 'rgba(255,255,255,0.7)',
borderWidth: 1,
shadowColor: 'rgba(150,150,150,1)',
shadowBlur: 3,
shadowOffsetX: 0,
shadowOffsetY: 0,
textStyle: {
color: '#fff'
}
},
emphasis: {
textStyle: {}
}
},
label: {
show: true,
// Do not use textDistance, for ellipsis rect just the same as treemap node rect.
distance: 0,
padding: 5,
position: 'inside',
// Can be [5, '5%'] or position stirng like 'insideTopLeft', ...
// formatter: null,
color: '#fff',
ellipsis: true // align
// verticalAlign
},
upperLabel: {
// Label when node is parent.
show: false,
position: [0, '50%'],
height: 20,
// formatter: null,
color: '#fff',
ellipsis: true,
// align: null,
verticalAlign: 'middle'
},
itemStyle: {
color: null,
// Can be 'none' if not necessary.
colorAlpha: null,
// Can be 'none' if not necessary.
colorSaturation: null,
// Can be 'none' if not necessary.
borderWidth: 0,
gapWidth: 0,
borderColor: '#fff',
borderColorSaturation: null // If specified, borderColor will be ineffective, and the
// border color is evaluated by color of current node and
// borderColorSaturation.
},
emphasis: {
upperLabel: {
show: true,
position: [0, '50%'],
color: '#fff',
ellipsis: true,
verticalAlign: 'middle'
}
},
visualDimension: 0,
// Can be 0, 1, 2, 3.
visualMin: null,
visualMax: null,
color: [],
// + treemapSeries.color should not be modified. Please only modified
// level[n].color (if necessary).
// + Specify color list of each level. level[0].color would be global
// color list if not specified. (see method `setDefault`).
// + But set as a empty array to forbid fetch color from global palette
// when using nodeModel.get('color'), otherwise nodes on deep level
// will always has color palette set and are not able to inherit color
// from parent node.
// + TreemapSeries.color can not be set as 'none', otherwise effect
// legend color fetching (see seriesColor.js).
colorAlpha: null,
// Array. Specify color alpha range of each level, like [0.2, 0.8]
colorSaturation: null,
// Array. Specify color saturation of each level, like [0.2, 0.5]
colorMappingBy: 'index',
// 'value' or 'index' or 'id'.
visibleMin: 10,
// If area less than this threshold (unit: pixel^2), node will not
// be rendered. Only works when sort is 'asc' or 'desc'.
childrenVisibleMin: null,
// If area of a node less than this threshold (unit: pixel^2),
// grandchildren will not show.
// Why grandchildren? If not grandchildren but children,
// some siblings show children and some not,
// the appearance may be mess and not consistent,
levels: [] // Each item: {
// visibleMin, itemStyle, visualDimension, label
// }
// data: {
// value: [],
// children: [],
// link: 'http://xxx.xxx.xxx',
// target: 'blank' or 'self'
// }
},
/**
* @override
*/
getInitialData: function (option, ecModel) {
// Create a virtual root.
var root = {
name: option.name,
children: option.data
};
completeTreeValue(root);
var levels = option.levels || [];
levels = option.levels = setDefault(levels, ecModel);
var treeOption = {};
treeOption.levels = levels; // Make sure always a new tree is created when setOption,
// in TreemapView, we check whether oldTree === newTree
// to choose mappings approach among old shapes and new shapes.
return Tree.createTree(root, this, treeOption).data;
},
optionUpdated: function () {
this.resetViewRoot();
},
/**
* @override
* @param {number} dataIndex
* @param {boolean} [mutipleSeries=false]
*/
formatTooltip: function (dataIndex) {
var data = this.getData();
var value = this.getRawValue(dataIndex);
var formattedValue = zrUtil.isArray(value) ? addCommas(value[0]) : addCommas(value);
var name = data.getName(dataIndex);
return encodeHTML(name + ': ' + formattedValue);
},
/**
* Add tree path to tooltip param
*
* @override
* @param {number} dataIndex
* @return {Object}
*/
getDataParams: function (dataIndex) {
var params = SeriesModel.prototype.getDataParams.apply(this, arguments);
var node = this.getData().tree.getNodeByDataIndex(dataIndex);
params.treePathInfo = wrapTreePathInfo(node, this);
return params;
},
/**
* @public
* @param {Object} layoutInfo {
* x: containerGroup x
* y: containerGroup y
* width: containerGroup width
* height: containerGroup height
* }
*/
setLayoutInfo: function (layoutInfo) {
/**
* @readOnly
* @type {Object}
*/
this.layoutInfo = this.layoutInfo || {};
zrUtil.extend(this.layoutInfo, layoutInfo);
},
/**
* @param {string} id
* @return {number} index
*/
mapIdToIndex: function (id) {
// A feature is implemented:
// index is monotone increasing with the sequence of
// input id at the first time.
// This feature can make sure that each data item and its
// mapped color have the same index between data list and
// color list at the beginning, which is useful for user
// to adjust data-color mapping.
/**
* @private
* @type {Object}
*/
var idIndexMap = this._idIndexMap;
if (!idIndexMap) {
idIndexMap = this._idIndexMap = zrUtil.createHashMap();
/**
* @private
* @type {number}
*/
this._idIndexMapCount = 0;
}
var index = idIndexMap.get(id);
if (index == null) {
idIndexMap.set(id, index = this._idIndexMapCount++);
}
return index;
},
getViewRoot: function () {
return this._viewRoot;
},
/**
* @param {module:echarts/data/Tree~Node} [viewRoot]
*/
resetViewRoot: function (viewRoot) {
viewRoot ? this._viewRoot = viewRoot : viewRoot = this._viewRoot;
var root = this.getRawData().tree.root;
if (!viewRoot || viewRoot !== root && !root.contains(viewRoot)) {
this._viewRoot = root;
}
}
});
/**
* @param {Object} dataNode
*/
function completeTreeValue(dataNode) {
// Postorder travel tree.
// If value of none-leaf node is not set,
// calculate it by suming up the value of all children.
var sum = 0;
zrUtil.each(dataNode.children, function (child) {
completeTreeValue(child);
var childValue = child.value;
zrUtil.isArray(childValue) && (childValue = childValue[0]);
sum += childValue;
});
var thisValue = dataNode.value;
if (zrUtil.isArray(thisValue)) {
thisValue = thisValue[0];
}
if (thisValue == null || isNaN(thisValue)) {
thisValue = sum;
} // Value should not less than 0.
if (thisValue < 0) {
thisValue = 0;
}
zrUtil.isArray(dataNode.value) ? dataNode.value[0] = thisValue : dataNode.value = thisValue;
}
/**
* set default to level configuration
*/
function setDefault(levels, ecModel) {
var globalColorList = ecModel.get('color');
if (!globalColorList) {
return;
}
levels = levels || [];
var hasColorDefine;
zrUtil.each(levels, function (levelDefine) {
var model = new Model(levelDefine);
var modelColor = model.get('color');
if (model.get('itemStyle.color') || modelColor && modelColor !== 'none') {
hasColorDefine = true;
}
});
if (!hasColorDefine) {
var level0 = levels[0] || (levels[0] = {});
level0.color = globalColorList.slice();
}
return levels;
}