PiecewiseModel.js 12.3 KB
Newer Older
P
pah100 已提交
1 2
define(function(require) {

P
pah100 已提交
3
    var VisualMapModel = require('./VisualMapModel');
P
pah100 已提交
4
    var zrUtil = require('zrender/core/util');
P
pah100 已提交
5
    var VisualMapping = require('../../visual/VisualMapping');
P
pah100 已提交
6

P
pah100 已提交
7
    var PiecewiseModel = VisualMapModel.extend({
P
pah100 已提交
8

9
        type: 'visualMap.piecewise',
P
pah100 已提交
10

P
pah100 已提交
11 12 13
        /**
         * Order Rule:
         *
P
pah100 已提交
14
         * option.categories / option.pieces / option.text / option.selected:
P
pah100 已提交
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
         *     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.
         */

P
pah100 已提交
31 32 33 34
        /**
         * @protected
         */
        defaultOption: {
P
pah100 已提交
35 36 37
            selected: null,             // Object. If not specified, means selected.
                                        // When pieces and splitNumber: {'0': true, '5': true}
                                        // When categories: {'cate1': false, 'cate3': true}
P
pah100 已提交
38
                                        // When selected === false, means all unselected.
P
pah100 已提交
39
            align: 'auto',              // 'auto', 'left', 'right'
P
pah100 已提交
40 41 42 43
            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.
P
pah100 已提交
44
            itemSymbol: 'roundRect',
P
pah100 已提交
45 46 47 48 49 50 51 52 53 54 55 56 57
            pieceList: null,            // Each item is Object, with some of those attrs:
                                        // {min, max, 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.
P
pah100 已提交
58 59 60 61 62
        },

        /**
         * @override
         */
P
pah100 已提交
63 64
        optionUpdated: function (newOption, isInit) {
            PiecewiseModel.superApply(this, 'optionUpdated', arguments);
P
pah100 已提交
65 66

            /**
P
pah100 已提交
67
             * The order is always [low, ..., high].
P
pah100 已提交
68 69 70 71 72 73
             * [{text: string, interval: Array.<number>}, ...]
             * @private
             * @type {Array.<Object>}
             */
            this._pieceList = [];

P
pah100 已提交
74
            this.resetTargetSeries();
P
pah100 已提交
75 76
            this.resetExtent();

P
pah100 已提交
77 78 79 80
            /**
             * 'pieces', 'categories', 'splitNumber'
             * @type {string}
             */
P
pah100 已提交
81
            var mode = this._mode = this._determineMode();
P
pah100 已提交
82

P
pah100 已提交
83
            resetMethods[this._mode].call(this);
P
pah100 已提交
84

P
pah100 已提交
85
            this._resetSelected(newOption, isInit);
P
pah100 已提交
86

P
pah100 已提交
87
            var categories = this.option.categories;
P
pah100 已提交
88

P
pah100 已提交
89
            this.resetVisual(function (mappingOption, state) {
P
pah100 已提交
90 91 92 93 94
                if (mode === 'categories') {
                    mappingOption.mappingMethod = 'category';
                    mappingOption.categories = zrUtil.clone(categories);
                }
                else {
P
pah100 已提交
95
                    mappingOption.dataExtent = this.getExtent();
P
pah100 已提交
96 97 98 99 100 101 102 103 104
                    mappingOption.mappingMethod = 'piecewise';
                    mappingOption.pieceList = zrUtil.map(this._pieceList, function (piece) {
                        var piece = zrUtil.clone(piece);
                        if (state !== 'inRange') {
                            piece.visual = null;
                        }
                        return piece;
                    });
                }
P
pah100 已提交
105
            });
P
pah100 已提交
106 107
        },

P
pah100 已提交
108
        _resetSelected: function (newOption, isInit) {
P
pah100 已提交
109 110 111
            var thisOption = this.option;
            var pieceList = this._pieceList;

P
pah100 已提交
112 113 114 115 116 117 118 119 120
            // 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 (!(key in selected)) {
                    selected[key] = true;
P
pah100 已提交
121
                }
P
pah100 已提交
122
            }, this);
P
pah100 已提交
123

P
pah100 已提交
124
            if (thisOption.selectedMode === 'single') {
P
pah100 已提交
125 126
                // Ensure there is only one selected.
                var hasSel = false;
P
pah100 已提交
127

P
pah100 已提交
128
                zrUtil.each(pieceList, function (piece, index) {
P
pah100 已提交
129 130
                    var key = this.getSelectedMapKey(piece);
                    if (selected[key]) {
P
pah100 已提交
131
                        hasSel
P
pah100 已提交
132
                            ? (selected[key] = false)
P
pah100 已提交
133 134
                            : (hasSel = true);
                    }
P
pah100 已提交
135
                }, this);
P
pah100 已提交
136
            }
P
pah100 已提交
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
            // 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}
         */
P
pah100 已提交
159
        _determineMode: function () {
P
pah100 已提交
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
            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);
P
pah100 已提交
175 176
        },

P
pah100 已提交
177 178 179 180 181
        /**
         * @public
         * @override
         */
        getValueState: function (value) {
P
pah100 已提交
182
            var index = VisualMapping.findPieceIndex(value, this._pieceList);
P
pah100 已提交
183 184

            return index != null
P
pah100 已提交
185
                ? (this.option.selected[this.getSelectedMapKey(this._pieceList[index])]
P
pah100 已提交
186 187 188
                    ? 'inRange' : 'outOfRange'
                )
                : 'outOfRange';
P
pah100 已提交
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
        },

        /**
         * @public
         * @params {number} pieceIndex piece index in visualMapModel.getPieceList()
         * @return {Array.<Object>} [{seriesId, dataIndices: <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);
                }, true, this);

P
pah100 已提交
209
                result.push({seriesId: seriesModel.id, dataIndex: dataIndices});
P
pah100 已提交
210 211
            }, this);

P
pah100 已提交
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
            return result;
        },

        getStops: function (seriesModel) {
            var result = [];
            if (this.isTargetSeries(seriesModel)) {
                zrUtil.each(this._pieceList, function (piece) {
                    // Do not support category yet.
                    var interval = piece.interval;
                    if (interval) {
                        result.push({
                            interval: interval.slice(),
                            valueState: this.getValueState((interval[0] + interval[1]) / 2)
                        });
                    }
                }, this);
            }
P
pah100 已提交
229
            return result;
P
pah100 已提交
230 231 232 233 234 235 236 237 238 239 240 241
        }

    });

    /**
     * Key is this._mode
     * @type {Object}
     * @this {module:echarts/component/viusalMap/PiecewiseMode}
     */
    var resetMethods = {

        splitNumber: function () {
P
pah100 已提交
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
            var thisOption = this.option;
            var precision = thisOption.precision;
            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);

            for (var i = 0, curr = dataExtent[0]; i < splitNumber; i++, curr += splitStep) {
                var max = i === splitNumber - 1 ? dataExtent[1] : (curr + splitStep);

                this._pieceList.push({
P
pah100 已提交
261
                    text: this.formatValueText([curr, max]),
P
pah100 已提交
262
                    index: i,
P
pah100 已提交
263 264 265 266 267
                    interval: [curr, max]
                });
            }
        },

P
pah100 已提交
268
        categories: function () {
P
pah100 已提交
269 270 271 272
            var thisOption = this.option;
            zrUtil.each(thisOption.categories, function (cate) {
                // FIXME category模式也使用pieceList,但在visualMapping中不是使用pieceList。
                // 是否改一致。
P
pah100 已提交
273
                this._pieceList.push({
P
pah100 已提交
274
                    text: this.formatValueText(cate, true),
P
pah100 已提交
275 276 277
                    value: cate
                });
            }, this);
P
pah100 已提交
278 279

            // See "Order Rule".
P
pah100 已提交
280
            normalizeReverse(thisOption, this._pieceList);
P
pah100 已提交
281 282
        },

P
pah100 已提交
283
        pieces: function () {
P
pah100 已提交
284
            var thisOption = this.option;
P
pah100 已提交
285
            zrUtil.each(thisOption.pieces, function (pieceListItem, index) {
P
pah100 已提交
286

P
pah100 已提交
287 288
                if (!zrUtil.isObject(pieceListItem)) {
                    pieceListItem = {value: pieceListItem};
P
pah100 已提交
289 290
                }

P
pah100 已提交
291
                var item = {text: '', index: index};
P
pah100 已提交
292
                var hasLabel;
P
pah100 已提交
293

P
pah100 已提交
294 295
                if (pieceListItem.label != null) {
                    item.text = pieceListItem.label;
P
pah100 已提交
296
                    hasLabel = true;
P
pah100 已提交
297
                }
P
pah100 已提交
298

P
pah100 已提交
299 300
                if (pieceListItem.hasOwnProperty('value')) {
                    item.value = pieceListItem.value;
P
pah100 已提交
301 302 303 304

                    if (!hasLabel) {
                        item.text = this.formatValueText(item.value);
                    }
P
pah100 已提交
305 306
                }
                else {
P
pah100 已提交
307 308
                    var min = pieceListItem.min;
                    var max = pieceListItem.max;
P
pah100 已提交
309 310
                    min == null && (min = -Infinity);
                    max == null && (max = Infinity);
P
pah100 已提交
311 312 313 314 315
                    if (min === max) {
                        // Consider: [{min: 5, max: 5, visual: {...}}, {min: 0, max: 5}],
                        // we use value to lift the priority when min === max
                        item.value = min;
                    }
P
pah100 已提交
316 317 318
                    item.interval = [min, max];

                    if (!hasLabel) {
P
pah100 已提交
319
                        item.text = this.formatValueText([min, max]);
P
pah100 已提交
320
                    }
P
pah100 已提交
321 322
                }

P
pah100 已提交
323
                item.visual = VisualMapping.retrieveVisuals(pieceListItem);
P
pah100 已提交
324 325

                this._pieceList.push(item);
P
pah100 已提交
326

P
pah100 已提交
327 328 329
            }, this);

            // See "Order Rule".
P
pah100 已提交
330
            normalizeReverse(thisOption, this._pieceList);
P
pah100 已提交
331
        }
P
pah100 已提交
332
    };
P
pah100 已提交
333

P
pah100 已提交
334 335 336 337 338 339
    function normalizeReverse(thisOption, arr) {
        var inverse = thisOption.inverse;
        if (thisOption.orient === 'vertical' ? !inverse : inverse) {
             arr.reverse();
        }
    }
P
pah100 已提交
340

P
pah100 已提交
341
    return PiecewiseModel;
P
pah100 已提交
342
});