| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543 | 
/** 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 echarts = require("../../echarts");var zrUtil = require("zrender/lib/core/util");var env = require("zrender/lib/core/env");var visualDefault = require("../../visual/visualDefault");var VisualMapping = require("../../visual/VisualMapping");var visualSolution = require("../../visual/visualSolution");var modelUtil = require("../../util/model");var numberUtil = require("../../util/number");/** 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 mapVisual = VisualMapping.mapVisual;var eachVisual = VisualMapping.eachVisual;var isArray = zrUtil.isArray;var each = zrUtil.each;var asc = numberUtil.asc;var linearMap = numberUtil.linearMap;var noop = zrUtil.noop;var VisualMapModel = echarts.extendComponentModel({  type: 'visualMap',  dependencies: ['series'],  /**   * @readOnly   * @type {Array.<string>}   */  stateList: ['inRange', 'outOfRange'],  /**   * @readOnly   * @type {Array.<string>}   */  replacableOptionKeys: ['inRange', 'outOfRange', 'target', 'controller', 'color'],  /**   * [lowerBound, upperBound]   *   * @readOnly   * @type {Array.<number>}   */  dataBound: [-Infinity, Infinity],  /**   * @readOnly   * @type {string|Object}   */  layoutMode: {    type: 'box',    ignoreSize: true  },  /**   * @protected   */  defaultOption: {    show: true,    zlevel: 0,    z: 4,    seriesIndex: 'all',    // 'all' or null/undefined: all series.    // A number or an array of number: the specified series.    // set min: 0, max: 200, only for campatible with ec2.    // In fact min max should not have default value.    min: 0,    // min value, must specified if pieces is not specified.    max: 200,    // max value, must specified if pieces is not specified.    dimension: null,    inRange: null,    // 'color', 'colorHue', 'colorSaturation', 'colorLightness', 'colorAlpha',    // 'symbol', 'symbolSize'    outOfRange: null,    // 'color', 'colorHue', 'colorSaturation',    // 'colorLightness', 'colorAlpha',    // 'symbol', 'symbolSize'    left: 0,    // 'center' ¦ 'left' ¦ 'right' ¦ {number} (px)    right: null,    // The same as left.    top: null,    // 'top' ¦ 'bottom' ¦ 'center' ¦ {number} (px)    bottom: 0,    // The same as top.    itemWidth: null,    itemHeight: null,    inverse: false,    orient: 'vertical',    // 'horizontal' ¦ 'vertical'    backgroundColor: 'rgba(0,0,0,0)',    borderColor: '#ccc',    // 值域边框颜色    contentColor: '#5793f3',    inactiveColor: '#aaa',    borderWidth: 0,    // 值域边框线宽,单位px,默认为0(无边框)    padding: 5,    // 值域内边距,单位px,默认各方向内边距为5,    // 接受数组分别设定上右下左边距,同css    textGap: 10,    //    precision: 0,    // 小数精度,默认为0,无小数点    color: null,    //颜色(deprecated,兼容ec2,顺序同pieces,不同于inRange/outOfRange)    formatter: null,    text: null,    // 文本,如['高', '低'],兼容ec2,text[0]对应高值,text[1]对应低值    textStyle: {      color: '#333' // 值域文字颜色    }  },  /**   * @protected   */  init: function (option, parentModel, ecModel) {    /**     * @private     * @type {Array.<number>}     */    this._dataExtent;    /**     * @readOnly     */    this.targetVisuals = {};    /**     * @readOnly     */    this.controllerVisuals = {};    /**     * @readOnly     */    this.textStyleModel;    /**     * [width, height]     * @readOnly     * @type {Array.<number>}     */    this.itemSize;    this.mergeDefaultAndTheme(option, ecModel);  },  /**   * @protected   */  optionUpdated: function (newOption, isInit) {    var thisOption = this.option; // FIXME    // necessary?    // Disable realtime view update if canvas is not supported.    if (!env.canvasSupported) {      thisOption.realtime = false;    }    !isInit && visualSolution.replaceVisualOption(thisOption, newOption, this.replacableOptionKeys);    this.textStyleModel = this.getModel('textStyle');    this.resetItemSize();    this.completeVisualOption();  },  /**   * @protected   */  resetVisual: function (supplementVisualOption) {    var stateList = this.stateList;    supplementVisualOption = zrUtil.bind(supplementVisualOption, this);    this.controllerVisuals = visualSolution.createVisualMappings(this.option.controller, stateList, supplementVisualOption);    this.targetVisuals = visualSolution.createVisualMappings(this.option.target, stateList, supplementVisualOption);  },  /**   * @protected   * @return {Array.<number>} An array of series indices.   */  getTargetSeriesIndices: function () {    var optionSeriesIndex = this.option.seriesIndex;    var seriesIndices = [];    if (optionSeriesIndex == null || optionSeriesIndex === 'all') {      this.ecModel.eachSeries(function (seriesModel, index) {        seriesIndices.push(index);      });    } else {      seriesIndices = modelUtil.normalizeToArray(optionSeriesIndex);    }    return seriesIndices;  },  /**   * @public   */  eachTargetSeries: function (callback, context) {    zrUtil.each(this.getTargetSeriesIndices(), function (seriesIndex) {      callback.call(context, this.ecModel.getSeriesByIndex(seriesIndex));    }, this);  },  /**   * @pubilc   */  isTargetSeries: function (seriesModel) {    var is = false;    this.eachTargetSeries(function (model) {      model === seriesModel && (is = true);    });    return is;  },  /**   * @example   * this.formatValueText(someVal); // format single numeric value to text.   * this.formatValueText(someVal, true); // format single category value to text.   * this.formatValueText([min, max]); // format numeric min-max to text.   * this.formatValueText([this.dataBound[0], max]); // using data lower bound.   * this.formatValueText([min, this.dataBound[1]]); // using data upper bound.   *   * @param {number|Array.<number>} value Real value, or this.dataBound[0 or 1].   * @param {boolean} [isCategory=false] Only available when value is number.   * @param {Array.<string>} edgeSymbols Open-close symbol when value is interval.   * @return {string}   * @protected   */  formatValueText: function (value, isCategory, edgeSymbols) {    var option = this.option;    var precision = option.precision;    var dataBound = this.dataBound;    var formatter = option.formatter;    var isMinMax;    var textValue;    edgeSymbols = edgeSymbols || ['<', '>'];    if (zrUtil.isArray(value)) {      value = value.slice();      isMinMax = true;    }    textValue = isCategory ? value : isMinMax ? [toFixed(value[0]), toFixed(value[1])] : toFixed(value);    if (zrUtil.isString(formatter)) {      return formatter.replace('{value}', isMinMax ? textValue[0] : textValue).replace('{value2}', isMinMax ? textValue[1] : textValue);    } else if (zrUtil.isFunction(formatter)) {      return isMinMax ? formatter(value[0], value[1]) : formatter(value);    }    if (isMinMax) {      if (value[0] === dataBound[0]) {        return edgeSymbols[0] + ' ' + textValue[1];      } else if (value[1] === dataBound[1]) {        return edgeSymbols[1] + ' ' + textValue[0];      } else {        return textValue[0] + ' - ' + textValue[1];      }    } else {      // Format single value (includes category case).      return textValue;    }    function toFixed(val) {      return val === dataBound[0] ? 'min' : val === dataBound[1] ? 'max' : (+val).toFixed(Math.min(precision, 20));    }  },  /**   * @protected   */  resetExtent: function () {    var thisOption = this.option; // Can not calculate data extent by data here.    // Because series and data may be modified in processing stage.    // So we do not support the feature "auto min/max".    var extent = asc([thisOption.min, thisOption.max]);    this._dataExtent = extent;  },  /**   * @public   * @param {module:echarts/data/List} list   * @return {string} Concrete dimention. If return null/undefined,   *                  no dimension used.   */  getDataDimension: function (list) {    var optDim = this.option.dimension;    var listDimensions = list.dimensions;    if (optDim == null && !listDimensions.length) {      return;    }    if (optDim != null) {      return list.getDimension(optDim);    }    var dimNames = list.dimensions;    for (var i = dimNames.length - 1; i >= 0; i--) {      var dimName = dimNames[i];      var dimInfo = list.getDimensionInfo(dimName);      if (!dimInfo.isCalculationCoord) {        return dimName;      }    }  },  /**   * @public   * @override   */  getExtent: function () {    return this._dataExtent.slice();  },  /**   * @protected   */  completeVisualOption: function () {    var ecModel = this.ecModel;    var thisOption = this.option;    var base = {      inRange: thisOption.inRange,      outOfRange: thisOption.outOfRange    };    var target = thisOption.target || (thisOption.target = {});    var controller = thisOption.controller || (thisOption.controller = {});    zrUtil.merge(target, base); // Do not override    zrUtil.merge(controller, base); // Do not override    var isCategory = this.isCategory();    completeSingle.call(this, target);    completeSingle.call(this, controller);    completeInactive.call(this, target, 'inRange', 'outOfRange'); // completeInactive.call(this, target, 'outOfRange', 'inRange');    completeController.call(this, controller);    function completeSingle(base) {      // Compatible with ec2 dataRange.color.      // The mapping order of dataRange.color is: [high value, ..., low value]      // whereas inRange.color and outOfRange.color is [low value, ..., high value]      // Notice: ec2 has no inverse.      if (isArray(thisOption.color) // If there has been inRange: {symbol: ...}, adding color is a mistake.      // So adding color only when no inRange defined.      && !base.inRange) {        base.inRange = {          color: thisOption.color.slice().reverse()        };      } // Compatible with previous logic, always give a defautl color, otherwise      // simple config with no inRange and outOfRange will not work.      // Originally we use visualMap.color as the default color, but setOption at      // the second time the default color will be erased. So we change to use      // constant DEFAULT_COLOR.      // If user do not want the default color, set inRange: {color: null}.      base.inRange = base.inRange || {        color: ecModel.get('gradientColor')      }; // If using shortcut like: {inRange: 'symbol'}, complete default value.      each(this.stateList, function (state) {        var visualType = base[state];        if (zrUtil.isString(visualType)) {          var defa = visualDefault.get(visualType, 'active', isCategory);          if (defa) {            base[state] = {};            base[state][visualType] = defa;          } else {            // Mark as not specified.            delete base[state];          }        }      }, this);    }    function completeInactive(base, stateExist, stateAbsent) {      var optExist = base[stateExist];      var optAbsent = base[stateAbsent];      if (optExist && !optAbsent) {        optAbsent = base[stateAbsent] = {};        each(optExist, function (visualData, visualType) {          if (!VisualMapping.isValidType(visualType)) {            return;          }          var defa = visualDefault.get(visualType, 'inactive', isCategory);          if (defa != null) {            optAbsent[visualType] = defa; // Compatibable with ec2:            // Only inactive color to rgba(0,0,0,0) can not            // make label transparent, so use opacity also.            if (visualType === 'color' && !optAbsent.hasOwnProperty('opacity') && !optAbsent.hasOwnProperty('colorAlpha')) {              optAbsent.opacity = [0, 0];            }          }        });      }    }    function completeController(controller) {      var symbolExists = (controller.inRange || {}).symbol || (controller.outOfRange || {}).symbol;      var symbolSizeExists = (controller.inRange || {}).symbolSize || (controller.outOfRange || {}).symbolSize;      var inactiveColor = this.get('inactiveColor');      each(this.stateList, function (state) {        var itemSize = this.itemSize;        var visuals = controller[state]; // Set inactive color for controller if no other color        // attr (like colorAlpha) specified.        if (!visuals) {          visuals = controller[state] = {            color: isCategory ? inactiveColor : [inactiveColor]          };        } // Consistent symbol and symbolSize if not specified.        if (visuals.symbol == null) {          visuals.symbol = symbolExists && zrUtil.clone(symbolExists) || (isCategory ? 'roundRect' : ['roundRect']);        }        if (visuals.symbolSize == null) {          visuals.symbolSize = symbolSizeExists && zrUtil.clone(symbolSizeExists) || (isCategory ? itemSize[0] : [itemSize[0], itemSize[0]]);        } // Filter square and none.        visuals.symbol = mapVisual(visuals.symbol, function (symbol) {          return symbol === 'none' || symbol === 'square' ? 'roundRect' : symbol;        }); // Normalize symbolSize        var symbolSize = visuals.symbolSize;        if (symbolSize != null) {          var max = -Infinity; // symbolSize can be object when categories defined.          eachVisual(symbolSize, function (value) {            value > max && (max = value);          });          visuals.symbolSize = mapVisual(symbolSize, function (value) {            return linearMap(value, [0, max], [0, itemSize[0]], true);          });        }      }, this);    }  },  /**   * @protected   */  resetItemSize: function () {    this.itemSize = [parseFloat(this.get('itemWidth')), parseFloat(this.get('itemHeight'))];  },  /**   * @public   */  isCategory: function () {    return !!this.option.categories;  },  /**   * @public   * @abstract   */  setSelected: noop,  /**   * @public   * @abstract   * @param {*|module:echarts/data/List} valueOrData   * @param {number} dataIndex   * @return {string} state See this.stateList   */  getValueState: noop,  /**   * FIXME   * Do not publish to thirt-part-dev temporarily   * util the interface is stable. (Should it return   * a function but not visual meta?)   *   * @pubilc   * @abstract   * @param {Function} getColorVisual   *        params: value, valueState   *        return: color   * @return {Object} visualMeta   *        should includes {stops, outerColors}   *        outerColor means [colorBeyondMinValue, colorBeyondMaxValue]   */  getVisualMeta: noop});var _default = VisualMapModel;module.exports = _default;
 |