123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540 |
- /*
- * 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.
- */
- var _config = require("../../config");
- var __DEV__ = _config.__DEV__;
- var zrUtil = require("zrender/lib/core/util");
- var VisualMapModel = require("./VisualMapModel");
- var VisualMapping = require("../../visual/VisualMapping");
- var visualDefault = require("../../visual/visualDefault");
- var _number = require("../../util/number");
- var reformIntervals = _number.reformIntervals;
- /*
- * 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.
- */
- var PiecewiseModel = VisualMapModel.extend({
- type: 'visualMap.piecewise',
- /**
- * Order Rule:
- *
- * option.categories / option.pieces / option.text / option.selected:
- * If !option.inverse,
- * Order when vertical: ['top', ..., 'bottom'].
- * Order when horizontal: ['left', ..., 'right'].
- * If option.inverse, the meaning of
- * the order should be reversed.
- *
- * this._pieceList:
- * The order is always [low, ..., high].
- *
- * Mapping from location to low-high:
- * If !option.inverse
- * When vertical, top is high.
- * When horizontal, right is high.
- * If option.inverse, reverse.
- */
- /**
- * @protected
- */
- defaultOption: {
- selected: null,
- // Object. If not specified, means selected.
- // When pieces and splitNumber: {'0': true, '5': true}
- // When categories: {'cate1': false, 'cate3': true}
- // When selected === false, means all unselected.
- minOpen: false,
- // Whether include values that smaller than `min`.
- maxOpen: false,
- // Whether include values that bigger than `max`.
- align: 'auto',
- // 'auto', 'left', 'right'
- itemWidth: 20,
- // When put the controller vertically, it is the length of
- // horizontal side of each item. Otherwise, vertical side.
- itemHeight: 14,
- // When put the controller vertically, it is the length of
- // vertical side of each item. Otherwise, horizontal side.
- itemSymbol: 'roundRect',
- pieceList: null,
- // Each item is Object, with some of those attrs:
- // {min, max, lt, gt, lte, gte, value,
- // color, colorSaturation, colorAlpha, opacity,
- // symbol, symbolSize}, which customize the range or visual
- // coding of the certain piece. Besides, see "Order Rule".
- categories: null,
- // category names, like: ['some1', 'some2', 'some3'].
- // Attr min/max are ignored when categories set. See "Order Rule"
- splitNumber: 5,
- // If set to 5, auto split five pieces equally.
- // If set to 0 and component type not set, component type will be
- // determined as "continuous". (It is less reasonable but for ec2
- // compatibility, see echarts/component/visualMap/typeDefaulter)
- selectedMode: 'multiple',
- // Can be 'multiple' or 'single'.
- itemGap: 10,
- // The gap between two items, in px.
- hoverLink: true,
- // Enable hover highlight.
- showLabel: null // By default, when text is used, label will hide (the logic
- // is remained for compatibility reason)
- },
- /**
- * @override
- */
- optionUpdated: function (newOption, isInit) {
- PiecewiseModel.superApply(this, 'optionUpdated', arguments);
- /**
- * The order is always [low, ..., high].
- * [{text: string, interval: Array.<number>}, ...]
- * @private
- * @type {Array.<Object>}
- */
- this._pieceList = [];
- this.resetExtent();
- /**
- * 'pieces', 'categories', 'splitNumber'
- * @type {string}
- */
- var mode = this._mode = this._determineMode();
- resetMethods[this._mode].call(this);
- this._resetSelected(newOption, isInit);
- var categories = this.option.categories;
- this.resetVisual(function (mappingOption, state) {
- if (mode === 'categories') {
- mappingOption.mappingMethod = 'category';
- mappingOption.categories = zrUtil.clone(categories);
- } else {
- mappingOption.dataExtent = this.getExtent();
- mappingOption.mappingMethod = 'piecewise';
- mappingOption.pieceList = zrUtil.map(this._pieceList, function (piece) {
- var piece = zrUtil.clone(piece);
- if (state !== 'inRange') {
- // FIXME
- // outOfRange do not support special visual in pieces.
- piece.visual = null;
- }
- return piece;
- });
- }
- });
- },
- /**
- * @protected
- * @override
- */
- completeVisualOption: function () {
- // Consider this case:
- // visualMap: {
- // pieces: [{symbol: 'circle', lt: 0}, {symbol: 'rect', gte: 0}]
- // }
- // where no inRange/outOfRange set but only pieces. So we should make
- // default inRange/outOfRange for this case, otherwise visuals that only
- // appear in `pieces` will not be taken into account in visual encoding.
- var option = this.option;
- var visualTypesInPieces = {};
- var visualTypes = VisualMapping.listVisualTypes();
- var isCategory = this.isCategory();
- zrUtil.each(option.pieces, function (piece) {
- zrUtil.each(visualTypes, function (visualType) {
- if (piece.hasOwnProperty(visualType)) {
- visualTypesInPieces[visualType] = 1;
- }
- });
- });
- zrUtil.each(visualTypesInPieces, function (v, visualType) {
- var exists = 0;
- zrUtil.each(this.stateList, function (state) {
- exists |= has(option, state, visualType) || has(option.target, state, visualType);
- }, this);
- !exists && zrUtil.each(this.stateList, function (state) {
- (option[state] || (option[state] = {}))[visualType] = visualDefault.get(visualType, state === 'inRange' ? 'active' : 'inactive', isCategory);
- });
- }, this);
- function has(obj, state, visualType) {
- return obj && obj[state] && (zrUtil.isObject(obj[state]) ? obj[state].hasOwnProperty(visualType) : obj[state] === visualType // e.g., inRange: 'symbol'
- );
- }
- VisualMapModel.prototype.completeVisualOption.apply(this, arguments);
- },
- _resetSelected: function (newOption, isInit) {
- var thisOption = this.option;
- var pieceList = this._pieceList; // Selected do not merge but all override.
- var selected = (isInit ? thisOption : newOption).selected || {};
- thisOption.selected = selected; // Consider 'not specified' means true.
- zrUtil.each(pieceList, function (piece, index) {
- var key = this.getSelectedMapKey(piece);
- if (!selected.hasOwnProperty(key)) {
- selected[key] = true;
- }
- }, this);
- if (thisOption.selectedMode === 'single') {
- // Ensure there is only one selected.
- var hasSel = false;
- zrUtil.each(pieceList, function (piece, index) {
- var key = this.getSelectedMapKey(piece);
- if (selected[key]) {
- hasSel ? selected[key] = false : hasSel = true;
- }
- }, this);
- } // thisOption.selectedMode === 'multiple', default: all selected.
- },
- /**
- * @public
- */
- getSelectedMapKey: function (piece) {
- return this._mode === 'categories' ? piece.value + '' : piece.index + '';
- },
- /**
- * @public
- */
- getPieceList: function () {
- return this._pieceList;
- },
- /**
- * @private
- * @return {string}
- */
- _determineMode: function () {
- var option = this.option;
- return option.pieces && option.pieces.length > 0 ? 'pieces' : this.option.categories ? 'categories' : 'splitNumber';
- },
- /**
- * @public
- * @override
- */
- setSelected: function (selected) {
- this.option.selected = zrUtil.clone(selected);
- },
- /**
- * @public
- * @override
- */
- getValueState: function (value) {
- var index = VisualMapping.findPieceIndex(value, this._pieceList);
- return index != null ? this.option.selected[this.getSelectedMapKey(this._pieceList[index])] ? 'inRange' : 'outOfRange' : 'outOfRange';
- },
- /**
- * @public
- * @params {number} pieceIndex piece index in visualMapModel.getPieceList()
- * @return {Array.<Object>} [{seriesId, dataIndex: <Array.<number>>}, ...]
- */
- findTargetDataIndices: function (pieceIndex) {
- var result = [];
- this.eachTargetSeries(function (seriesModel) {
- var dataIndices = [];
- var data = seriesModel.getData();
- data.each(this.getDataDimension(data), function (value, dataIndex) {
- // Should always base on model pieceList, because it is order sensitive.
- var pIdx = VisualMapping.findPieceIndex(value, this._pieceList);
- pIdx === pieceIndex && dataIndices.push(dataIndex);
- }, this);
- result.push({
- seriesId: seriesModel.id,
- dataIndex: dataIndices
- });
- }, this);
- return result;
- },
- /**
- * @private
- * @param {Object} piece piece.value or piece.interval is required.
- * @return {number} Can be Infinity or -Infinity
- */
- getRepresentValue: function (piece) {
- var representValue;
- if (this.isCategory()) {
- representValue = piece.value;
- } else {
- if (piece.value != null) {
- representValue = piece.value;
- } else {
- var pieceInterval = piece.interval || [];
- representValue = pieceInterval[0] === -Infinity && pieceInterval[1] === Infinity ? 0 : (pieceInterval[0] + pieceInterval[1]) / 2;
- }
- }
- return representValue;
- },
- getVisualMeta: function (getColorVisual) {
- // Do not support category. (category axis is ordinal, numerical)
- if (this.isCategory()) {
- return;
- }
- var stops = [];
- var outerColors = [];
- var visualMapModel = this;
- function setStop(interval, valueState) {
- var representValue = visualMapModel.getRepresentValue({
- interval: interval
- });
- if (!valueState) {
- valueState = visualMapModel.getValueState(representValue);
- }
- var color = getColorVisual(representValue, valueState);
- if (interval[0] === -Infinity) {
- outerColors[0] = color;
- } else if (interval[1] === Infinity) {
- outerColors[1] = color;
- } else {
- stops.push({
- value: interval[0],
- color: color
- }, {
- value: interval[1],
- color: color
- });
- }
- } // Suplement
- var pieceList = this._pieceList.slice();
- if (!pieceList.length) {
- pieceList.push({
- interval: [-Infinity, Infinity]
- });
- } else {
- var edge = pieceList[0].interval[0];
- edge !== -Infinity && pieceList.unshift({
- interval: [-Infinity, edge]
- });
- edge = pieceList[pieceList.length - 1].interval[1];
- edge !== Infinity && pieceList.push({
- interval: [edge, Infinity]
- });
- }
- var curr = -Infinity;
- zrUtil.each(pieceList, function (piece) {
- var interval = piece.interval;
- if (interval) {
- // Fulfill gap.
- interval[0] > curr && setStop([curr, interval[0]], 'outOfRange');
- setStop(interval.slice());
- curr = interval[1];
- }
- }, this);
- return {
- stops: stops,
- outerColors: outerColors
- };
- }
- });
- /**
- * Key is this._mode
- * @type {Object}
- * @this {module:echarts/component/viusalMap/PiecewiseMode}
- */
- var resetMethods = {
- splitNumber: function () {
- var thisOption = this.option;
- var pieceList = this._pieceList;
- var precision = Math.min(thisOption.precision, 20);
- var dataExtent = this.getExtent();
- var splitNumber = thisOption.splitNumber;
- splitNumber = Math.max(parseInt(splitNumber, 10), 1);
- thisOption.splitNumber = splitNumber;
- var splitStep = (dataExtent[1] - dataExtent[0]) / splitNumber; // Precision auto-adaption
- while (+splitStep.toFixed(precision) !== splitStep && precision < 5) {
- precision++;
- }
- thisOption.precision = precision;
- splitStep = +splitStep.toFixed(precision);
- if (thisOption.minOpen) {
- pieceList.push({
- interval: [-Infinity, dataExtent[0]],
- close: [0, 0]
- });
- }
- for (var index = 0, curr = dataExtent[0]; index < splitNumber; curr += splitStep, index++) {
- var max = index === splitNumber - 1 ? dataExtent[1] : curr + splitStep;
- pieceList.push({
- interval: [curr, max],
- close: [1, 1]
- });
- }
- if (thisOption.maxOpen) {
- pieceList.push({
- interval: [dataExtent[1], Infinity],
- close: [0, 0]
- });
- }
- reformIntervals(pieceList);
- zrUtil.each(pieceList, function (piece, index) {
- piece.index = index;
- piece.text = this.formatValueText(piece.interval);
- }, this);
- },
- categories: function () {
- var thisOption = this.option;
- zrUtil.each(thisOption.categories, function (cate) {
- // FIXME category模式也使用pieceList,但在visualMapping中不是使用pieceList。
- // 是否改一致。
- this._pieceList.push({
- text: this.formatValueText(cate, true),
- value: cate
- });
- }, this); // See "Order Rule".
- normalizeReverse(thisOption, this._pieceList);
- },
- pieces: function () {
- var thisOption = this.option;
- var pieceList = this._pieceList;
- zrUtil.each(thisOption.pieces, function (pieceListItem, index) {
- if (!zrUtil.isObject(pieceListItem)) {
- pieceListItem = {
- value: pieceListItem
- };
- }
- var item = {
- text: '',
- index: index
- };
- if (pieceListItem.label != null) {
- item.text = pieceListItem.label;
- }
- if (pieceListItem.hasOwnProperty('value')) {
- var value = item.value = pieceListItem.value;
- item.interval = [value, value];
- item.close = [1, 1];
- } else {
- // `min` `max` is legacy option.
- // `lt` `gt` `lte` `gte` is recommanded.
- var interval = item.interval = [];
- var close = item.close = [0, 0];
- var closeList = [1, 0, 1];
- var infinityList = [-Infinity, Infinity];
- var useMinMax = [];
- for (var lg = 0; lg < 2; lg++) {
- var names = [['gte', 'gt', 'min'], ['lte', 'lt', 'max']][lg];
- for (var i = 0; i < 3 && interval[lg] == null; i++) {
- interval[lg] = pieceListItem[names[i]];
- close[lg] = closeList[i];
- useMinMax[lg] = i === 2;
- }
- interval[lg] == null && (interval[lg] = infinityList[lg]);
- }
- useMinMax[0] && interval[1] === Infinity && (close[0] = 0);
- useMinMax[1] && interval[0] === -Infinity && (close[1] = 0);
- if (interval[0] === interval[1] && close[0] && close[1]) {
- // Consider: [{min: 5, max: 5, visual: {...}}, {min: 0, max: 5}],
- // we use value to lift the priority when min === max
- item.value = interval[0];
- }
- }
- item.visual = VisualMapping.retrieveVisuals(pieceListItem);
- pieceList.push(item);
- }, this); // See "Order Rule".
- normalizeReverse(thisOption, pieceList); // Only pieces
- reformIntervals(pieceList);
- zrUtil.each(pieceList, function (piece) {
- var close = piece.close;
- var edgeSymbols = [['<', '≤'][close[1]], ['>', '≥'][close[0]]];
- piece.text = piece.text || this.formatValueText(piece.value != null ? piece.value : piece.interval, false, edgeSymbols);
- }, this);
- }
- };
- function normalizeReverse(thisOption, pieceList) {
- var inverse = thisOption.inverse;
- if (thisOption.orient === 'vertical' ? !inverse : inverse) {
- pieceList.reverse();
- }
- }
- var _default = PiecewiseModel;
- module.exports = _default;
|