| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467 | 
/** 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 zrUtil = require("zrender/lib/core/util");var RoamController = require("./RoamController");var roamHelper = require("../../component/helper/roamHelper");var _cursorHelper = require("../../component/helper/cursorHelper");var onIrrelevantElement = _cursorHelper.onIrrelevantElement;var graphic = require("../../util/graphic");var geoSourceManager = require("../../coord/geo/geoSourceManager");var _component = require("../../util/component");var getUID = _component.getUID;var Transformable = require("zrender/lib/mixin/Transformable");/** 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.*/function getFixedItemStyle(model) {  var itemStyle = model.getItemStyle();  var areaColor = model.get('areaColor'); // If user want the color not to be changed when hover,  // they should both set areaColor and color to be null.  if (areaColor != null) {    itemStyle.fill = areaColor;  }  return itemStyle;}function updateMapSelectHandler(mapDraw, mapOrGeoModel, regionsGroup, api, fromView) {  regionsGroup.off('click');  regionsGroup.off('mousedown');  if (mapOrGeoModel.get('selectedMode')) {    regionsGroup.on('mousedown', function () {      mapDraw._mouseDownFlag = true;    });    regionsGroup.on('click', function (e) {      if (!mapDraw._mouseDownFlag) {        return;      }      mapDraw._mouseDownFlag = false;      var el = e.target;      while (!el.__regions) {        el = el.parent;      }      if (!el) {        return;      }      var action = {        type: (mapOrGeoModel.mainType === 'geo' ? 'geo' : 'map') + 'ToggleSelect',        batch: zrUtil.map(el.__regions, function (region) {          return {            name: region.name,            from: fromView.uid          };        })      };      action[mapOrGeoModel.mainType + 'Id'] = mapOrGeoModel.id;      api.dispatchAction(action);      updateMapSelected(mapOrGeoModel, regionsGroup);    });  }}function updateMapSelected(mapOrGeoModel, regionsGroup) {  // FIXME  regionsGroup.eachChild(function (otherRegionEl) {    zrUtil.each(otherRegionEl.__regions, function (region) {      otherRegionEl.trigger(mapOrGeoModel.isSelected(region.name) ? 'emphasis' : 'normal');    });  });}/** * @alias module:echarts/component/helper/MapDraw * @param {module:echarts/ExtensionAPI} api * @param {boolean} updateGroup */function MapDraw(api, updateGroup) {  var group = new graphic.Group();  /**   * @type {string}   * @private   */  this.uid = getUID('ec_map_draw');  /**   * @type {module:echarts/component/helper/RoamController}   * @private   */  this._controller = new RoamController(api.getZr());  /**   * @type {Object} {target, zoom, zoomLimit}   * @private   */  this._controllerHost = {    target: updateGroup ? group : null  };  /**   * @type {module:zrender/container/Group}   * @readOnly   */  this.group = group;  /**   * @type {boolean}   * @private   */  this._updateGroup = updateGroup;  /**   * This flag is used to make sure that only one among   * `pan`, `zoom`, `click` can occurs, otherwise 'selected'   * action may be triggered when `pan`, which is unexpected.   * @type {booelan}   */  this._mouseDownFlag;  /**   * @type {string}   */  this._mapName;  /**   * @type {boolean}   */  this._initialized;  /**   * @type {module:zrender/container/Group}   */  group.add(this._regionsGroup = new graphic.Group());  /**   * @type {module:zrender/container/Group}   */  group.add(this._backgroundGroup = new graphic.Group());}MapDraw.prototype = {  constructor: MapDraw,  draw: function (mapOrGeoModel, ecModel, api, fromView, payload) {    var isGeo = mapOrGeoModel.mainType === 'geo'; // Map series has data. GEO model that controlled by map series    // will be assigned with map data. Other GEO model has no data.    var data = mapOrGeoModel.getData && mapOrGeoModel.getData();    isGeo && ecModel.eachComponent({      mainType: 'series',      subType: 'map'    }, function (mapSeries) {      if (!data && mapSeries.getHostGeoModel() === mapOrGeoModel) {        data = mapSeries.getData();      }    });    var geo = mapOrGeoModel.coordinateSystem;    this._updateBackground(geo);    var regionsGroup = this._regionsGroup;    var group = this.group;    var transformInfo = geo.getTransformInfo(); // No animation when first draw or in action    var isFirstDraw = !regionsGroup.childAt(0) || payload;    var targetScale;    if (isFirstDraw) {      group.transform = transformInfo.roamTransform;      group.decomposeTransform();      group.dirty();    } else {      var target = new Transformable();      target.transform = transformInfo.roamTransform;      target.decomposeTransform();      var props = {        scale: target.scale,        position: target.position      };      targetScale = target.scale;      graphic.updateProps(group, props, mapOrGeoModel);    }    var scale = transformInfo.rawScale;    var position = transformInfo.rawPosition;    regionsGroup.removeAll();    var itemStyleAccessPath = ['itemStyle'];    var hoverItemStyleAccessPath = ['emphasis', 'itemStyle'];    var labelAccessPath = ['label'];    var hoverLabelAccessPath = ['emphasis', 'label'];    var nameMap = zrUtil.createHashMap();    zrUtil.each(geo.regions, function (region) {      // Consider in GeoJson properties.name may be duplicated, for example,      // there is multiple region named "United Kindom" or "France" (so many      // colonies). And it is not appropriate to merge them in geo, which      // will make them share the same label and bring trouble in label      // location calculation.      var regionGroup = nameMap.get(region.name) || nameMap.set(region.name, new graphic.Group());      var compoundPath = new graphic.CompoundPath({        segmentIgnoreThreshold: 1,        shape: {          paths: []        }      });      regionGroup.add(compoundPath);      var regionModel = mapOrGeoModel.getRegionModel(region.name) || mapOrGeoModel;      var itemStyleModel = regionModel.getModel(itemStyleAccessPath);      var hoverItemStyleModel = regionModel.getModel(hoverItemStyleAccessPath);      var itemStyle = getFixedItemStyle(itemStyleModel);      var hoverItemStyle = getFixedItemStyle(hoverItemStyleModel);      var labelModel = regionModel.getModel(labelAccessPath);      var hoverLabelModel = regionModel.getModel(hoverLabelAccessPath);      var dataIdx; // Use the itemStyle in data if has data      if (data) {        dataIdx = data.indexOfName(region.name); // Only visual color of each item will be used. It can be encoded by dataRange        // But visual color of series is used in symbol drawing        //        // Visual color for each series is for the symbol draw        var visualColor = data.getItemVisual(dataIdx, 'color', true);        if (visualColor) {          itemStyle.fill = visualColor;        }      }      var transformPoint = function (point) {        return [point[0] * scale[0] + position[0], point[1] * scale[1] + position[1]];      };      zrUtil.each(region.geometries, function (geometry) {        if (geometry.type !== 'polygon') {          return;        }        var points = [];        for (var i = 0; i < geometry.exterior.length; ++i) {          points.push(transformPoint(geometry.exterior[i]));        }        compoundPath.shape.paths.push(new graphic.Polygon({          segmentIgnoreThreshold: 1,          shape: {            points: points          }        }));        for (var i = 0; i < (geometry.interiors ? geometry.interiors.length : 0); ++i) {          var interior = geometry.interiors[i];          var points = [];          for (var j = 0; j < interior.length; ++j) {            points.push(transformPoint(interior[j]));          }          compoundPath.shape.paths.push(new graphic.Polygon({            segmentIgnoreThreshold: 1,            shape: {              points: points            }          }));        }      });      compoundPath.setStyle(itemStyle);      compoundPath.style.strokeNoScale = true;      compoundPath.culling = true; // Label      var showLabel = labelModel.get('show');      var hoverShowLabel = hoverLabelModel.get('show');      var isDataNaN = data && isNaN(data.get(data.mapDimension('value'), dataIdx));      var itemLayout = data && data.getItemLayout(dataIdx); // In the following cases label will be drawn      // 1. In map series and data value is NaN      // 2. In geo component      // 4. Region has no series legendSymbol, which will be add a showLabel flag in mapSymbolLayout      if (isGeo || isDataNaN && (showLabel || hoverShowLabel) || itemLayout && itemLayout.showLabel) {        var query = !isGeo ? dataIdx : region.name;        var labelFetcher; // Consider dataIdx not found.        if (!data || dataIdx >= 0) {          labelFetcher = mapOrGeoModel;        }        var textEl = new graphic.Text({          position: transformPoint(region.center.slice()),          // FIXME          // label rotation is not support yet in geo or regions of series-map          // that has no data. The rotation will be effected by this `scale`.          // So needed to change to RectText?          scale: [1 / group.scale[0], 1 / group.scale[1]],          z2: 10,          silent: true        });        graphic.setLabelStyle(textEl.style, textEl.hoverStyle = {}, labelModel, hoverLabelModel, {          labelFetcher: labelFetcher,          labelDataIndex: query,          defaultText: region.name,          useInsideStyle: false        }, {          textAlign: 'center',          textVerticalAlign: 'middle'        });        if (!isFirstDraw) {          // Text animation          var textScale = [1 / targetScale[0], 1 / targetScale[1]];          graphic.updateProps(textEl, {            scale: textScale          }, mapOrGeoModel);        }        regionGroup.add(textEl);      } // setItemGraphicEl, setHoverStyle after all polygons and labels      // are added to the rigionGroup      if (data) {        data.setItemGraphicEl(dataIdx, regionGroup);      } else {        var regionModel = mapOrGeoModel.getRegionModel(region.name); // Package custom mouse event for geo component        compoundPath.eventData = {          componentType: 'geo',          componentIndex: mapOrGeoModel.componentIndex,          geoIndex: mapOrGeoModel.componentIndex,          name: region.name,          region: regionModel && regionModel.option || {}        };      }      var groupRegions = regionGroup.__regions || (regionGroup.__regions = []);      groupRegions.push(region);      regionGroup.highDownSilentOnTouch = !!mapOrGeoModel.get('selectedMode');      graphic.setHoverStyle(regionGroup, hoverItemStyle);      regionsGroup.add(regionGroup);    });    this._updateController(mapOrGeoModel, ecModel, api);    updateMapSelectHandler(this, mapOrGeoModel, regionsGroup, api, fromView);    updateMapSelected(mapOrGeoModel, regionsGroup);  },  remove: function () {    this._regionsGroup.removeAll();    this._backgroundGroup.removeAll();    this._controller.dispose();    this._mapName && geoSourceManager.removeGraphic(this._mapName, this.uid);    this._mapName = null;    this._controllerHost = {};  },  _updateBackground: function (geo) {    var mapName = geo.map;    if (this._mapName !== mapName) {      zrUtil.each(geoSourceManager.makeGraphic(mapName, this.uid), function (root) {        this._backgroundGroup.add(root);      }, this);    }    this._mapName = mapName;  },  _updateController: function (mapOrGeoModel, ecModel, api) {    var geo = mapOrGeoModel.coordinateSystem;    var controller = this._controller;    var controllerHost = this._controllerHost;    controllerHost.zoomLimit = mapOrGeoModel.get('scaleLimit');    controllerHost.zoom = geo.getZoom(); // roamType is will be set default true if it is null    controller.enable(mapOrGeoModel.get('roam') || false);    var mainType = mapOrGeoModel.mainType;    function makeActionBase() {      var action = {        type: 'geoRoam',        componentType: mainType      };      action[mainType + 'Id'] = mapOrGeoModel.id;      return action;    }    controller.off('pan').on('pan', function (e) {      this._mouseDownFlag = false;      roamHelper.updateViewOnPan(controllerHost, e.dx, e.dy);      api.dispatchAction(zrUtil.extend(makeActionBase(), {        dx: e.dx,        dy: e.dy      }));    }, this);    controller.off('zoom').on('zoom', function (e) {      this._mouseDownFlag = false;      roamHelper.updateViewOnZoom(controllerHost, e.scale, e.originX, e.originY);      api.dispatchAction(zrUtil.extend(makeActionBase(), {        zoom: e.scale,        originX: e.originX,        originY: e.originY      }));      if (this._updateGroup) {        var scale = this.group.scale;        this._regionsGroup.traverse(function (el) {          if (el.type === 'text') {            el.attr('scale', [1 / scale[0], 1 / scale[1]]);          }        });      }    }, this);    controller.setPointerChecker(function (e, x, y) {      return geo.getViewRectAfterRoam().contain(x, y) && !onIrrelevantElement(e, api, mapOrGeoModel);    });  }};var _default = MapDraw;module.exports = _default;
 |