MapDraw.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. var zrUtil = require("zrender/lib/core/util");
  2. var RoamController = require("./RoamController");
  3. var roamHelper = require("../../component/helper/roamHelper");
  4. var _cursorHelper = require("../../component/helper/cursorHelper");
  5. var onIrrelevantElement = _cursorHelper.onIrrelevantElement;
  6. var graphic = require("../../util/graphic");
  7. function getFixedItemStyle(model, scale) {
  8. var itemStyle = model.getItemStyle();
  9. var areaColor = model.get('areaColor'); // If user want the color not to be changed when hover,
  10. // they should both set areaColor and color to be null.
  11. if (areaColor != null) {
  12. itemStyle.fill = areaColor;
  13. }
  14. return itemStyle;
  15. }
  16. function updateMapSelectHandler(mapDraw, mapOrGeoModel, group, api, fromView) {
  17. group.off('click');
  18. group.off('mousedown');
  19. if (mapOrGeoModel.get('selectedMode')) {
  20. group.on('mousedown', function () {
  21. mapDraw._mouseDownFlag = true;
  22. });
  23. group.on('click', function (e) {
  24. if (!mapDraw._mouseDownFlag) {
  25. return;
  26. }
  27. mapDraw._mouseDownFlag = false;
  28. var el = e.target;
  29. while (!el.__regions) {
  30. el = el.parent;
  31. }
  32. if (!el) {
  33. return;
  34. }
  35. var action = {
  36. type: (mapOrGeoModel.mainType === 'geo' ? 'geo' : 'map') + 'ToggleSelect',
  37. batch: zrUtil.map(el.__regions, function (region) {
  38. return {
  39. name: region.name,
  40. from: fromView.uid
  41. };
  42. })
  43. };
  44. action[mapOrGeoModel.mainType + 'Id'] = mapOrGeoModel.id;
  45. api.dispatchAction(action);
  46. updateMapSelected(mapOrGeoModel, group);
  47. });
  48. }
  49. }
  50. function updateMapSelected(mapOrGeoModel, group) {
  51. // FIXME
  52. group.eachChild(function (otherRegionEl) {
  53. zrUtil.each(otherRegionEl.__regions, function (region) {
  54. otherRegionEl.trigger(mapOrGeoModel.isSelected(region.name) ? 'emphasis' : 'normal');
  55. });
  56. });
  57. }
  58. /**
  59. * @alias module:echarts/component/helper/MapDraw
  60. * @param {module:echarts/ExtensionAPI} api
  61. * @param {boolean} updateGroup
  62. */
  63. function MapDraw(api, updateGroup) {
  64. var group = new graphic.Group();
  65. /**
  66. * @type {module:echarts/component/helper/RoamController}
  67. * @private
  68. */
  69. this._controller = new RoamController(api.getZr());
  70. /**
  71. * @type {Object} {target, zoom, zoomLimit}
  72. * @private
  73. */
  74. this._controllerHost = {
  75. target: updateGroup ? group : null
  76. };
  77. /**
  78. * @type {module:zrender/container/Group}
  79. * @readOnly
  80. */
  81. this.group = group;
  82. /**
  83. * @type {boolean}
  84. * @private
  85. */
  86. this._updateGroup = updateGroup;
  87. /**
  88. * This flag is used to make sure that only one among
  89. * `pan`, `zoom`, `click` can occurs, otherwise 'selected'
  90. * action may be triggered when `pan`, which is unexpected.
  91. * @type {booelan}
  92. */
  93. this._mouseDownFlag;
  94. }
  95. MapDraw.prototype = {
  96. constructor: MapDraw,
  97. draw: function (mapOrGeoModel, ecModel, api, fromView, payload) {
  98. var isGeo = mapOrGeoModel.mainType === 'geo'; // Map series has data. GEO model that controlled by map series
  99. // will be assigned with map data. Other GEO model has no data.
  100. var data = mapOrGeoModel.getData && mapOrGeoModel.getData();
  101. isGeo && ecModel.eachComponent({
  102. mainType: 'series',
  103. subType: 'map'
  104. }, function (mapSeries) {
  105. if (!data && mapSeries.getHostGeoModel() === mapOrGeoModel) {
  106. data = mapSeries.getData();
  107. }
  108. });
  109. var geo = mapOrGeoModel.coordinateSystem;
  110. var group = this.group;
  111. var scale = geo.scale;
  112. var groupNewProp = {
  113. position: geo.position,
  114. scale: scale
  115. }; // No animation when first draw or in action
  116. if (!group.childAt(0) || payload) {
  117. group.attr(groupNewProp);
  118. } else {
  119. graphic.updateProps(group, groupNewProp, mapOrGeoModel);
  120. }
  121. group.removeAll();
  122. var itemStyleAccessPath = ['itemStyle', 'normal'];
  123. var hoverItemStyleAccessPath = ['itemStyle', 'emphasis'];
  124. var labelAccessPath = ['label', 'normal'];
  125. var hoverLabelAccessPath = ['label', 'emphasis'];
  126. var nameMap = zrUtil.createHashMap();
  127. zrUtil.each(geo.regions, function (region) {
  128. // Consider in GeoJson properties.name may be duplicated, for example,
  129. // there is multiple region named "United Kindom" or "France" (so many
  130. // colonies). And it is not appropriate to merge them in geo, which
  131. // will make them share the same label and bring trouble in label
  132. // location calculation.
  133. var regionGroup = nameMap.get(region.name) || nameMap.set(region.name, new graphic.Group());
  134. var compoundPath = new graphic.CompoundPath({
  135. shape: {
  136. paths: []
  137. }
  138. });
  139. regionGroup.add(compoundPath);
  140. var regionModel = mapOrGeoModel.getRegionModel(region.name) || mapOrGeoModel;
  141. var itemStyleModel = regionModel.getModel(itemStyleAccessPath);
  142. var hoverItemStyleModel = regionModel.getModel(hoverItemStyleAccessPath);
  143. var itemStyle = getFixedItemStyle(itemStyleModel, scale);
  144. var hoverItemStyle = getFixedItemStyle(hoverItemStyleModel, scale);
  145. var labelModel = regionModel.getModel(labelAccessPath);
  146. var hoverLabelModel = regionModel.getModel(hoverLabelAccessPath);
  147. var dataIdx; // Use the itemStyle in data if has data
  148. if (data) {
  149. dataIdx = data.indexOfName(region.name); // Only visual color of each item will be used. It can be encoded by dataRange
  150. // But visual color of series is used in symbol drawing
  151. //
  152. // Visual color for each series is for the symbol draw
  153. var visualColor = data.getItemVisual(dataIdx, 'color', true);
  154. if (visualColor) {
  155. itemStyle.fill = visualColor;
  156. }
  157. }
  158. zrUtil.each(region.geometries, function (geometry) {
  159. if (geometry.type !== 'polygon') {
  160. return;
  161. }
  162. compoundPath.shape.paths.push(new graphic.Polygon({
  163. shape: {
  164. points: geometry.exterior
  165. }
  166. }));
  167. for (var i = 0; i < (geometry.interiors ? geometry.interiors.length : 0); i++) {
  168. compoundPath.shape.paths.push(new graphic.Polygon({
  169. shape: {
  170. points: geometry.interiors[i]
  171. }
  172. }));
  173. }
  174. });
  175. compoundPath.setStyle(itemStyle);
  176. compoundPath.style.strokeNoScale = true;
  177. compoundPath.culling = true; // Label
  178. var showLabel = labelModel.get('show');
  179. var hoverShowLabel = hoverLabelModel.get('show');
  180. var isDataNaN = data && isNaN(data.get('value', dataIdx));
  181. var itemLayout = data && data.getItemLayout(dataIdx); // In the following cases label will be drawn
  182. // 1. In map series and data value is NaN
  183. // 2. In geo component
  184. // 4. Region has no series legendSymbol, which will be add a showLabel flag in mapSymbolLayout
  185. if (isGeo || isDataNaN && (showLabel || hoverShowLabel) || itemLayout && itemLayout.showLabel) {
  186. var query = !isGeo ? dataIdx : region.name;
  187. var labelFetcher; // Consider dataIdx not found.
  188. if (!data || dataIdx >= 0) {
  189. labelFetcher = mapOrGeoModel;
  190. }
  191. var textEl = new graphic.Text({
  192. position: region.center.slice(),
  193. scale: [1 / scale[0], 1 / scale[1]],
  194. z2: 10,
  195. silent: true
  196. });
  197. graphic.setLabelStyle(textEl.style, textEl.hoverStyle = {}, labelModel, hoverLabelModel, {
  198. labelFetcher: labelFetcher,
  199. labelDataIndex: query,
  200. defaultText: region.name,
  201. useInsideStyle: false
  202. }, {
  203. textAlign: 'center',
  204. textVerticalAlign: 'middle'
  205. });
  206. regionGroup.add(textEl);
  207. } // setItemGraphicEl, setHoverStyle after all polygons and labels
  208. // are added to the rigionGroup
  209. if (data) {
  210. data.setItemGraphicEl(dataIdx, regionGroup);
  211. } else {
  212. var regionModel = mapOrGeoModel.getRegionModel(region.name); // Package custom mouse event for geo component
  213. compoundPath.eventData = {
  214. componentType: 'geo',
  215. geoIndex: mapOrGeoModel.componentIndex,
  216. name: region.name,
  217. region: regionModel && regionModel.option || {}
  218. };
  219. }
  220. var groupRegions = regionGroup.__regions || (regionGroup.__regions = []);
  221. groupRegions.push(region);
  222. graphic.setHoverStyle(regionGroup, hoverItemStyle, {
  223. hoverSilentOnTouch: !!mapOrGeoModel.get('selectedMode')
  224. });
  225. group.add(regionGroup);
  226. });
  227. this._updateController(mapOrGeoModel, ecModel, api);
  228. updateMapSelectHandler(this, mapOrGeoModel, group, api, fromView);
  229. updateMapSelected(mapOrGeoModel, group);
  230. },
  231. remove: function () {
  232. this.group.removeAll();
  233. this._controller.dispose();
  234. this._controllerHost = {};
  235. },
  236. _updateController: function (mapOrGeoModel, ecModel, api) {
  237. var geo = mapOrGeoModel.coordinateSystem;
  238. var controller = this._controller;
  239. var controllerHost = this._controllerHost;
  240. controllerHost.zoomLimit = mapOrGeoModel.get('scaleLimit');
  241. controllerHost.zoom = geo.getZoom(); // roamType is will be set default true if it is null
  242. controller.enable(mapOrGeoModel.get('roam') || false);
  243. var mainType = mapOrGeoModel.mainType;
  244. function makeActionBase() {
  245. var action = {
  246. type: 'geoRoam',
  247. componentType: mainType
  248. };
  249. action[mainType + 'Id'] = mapOrGeoModel.id;
  250. return action;
  251. }
  252. controller.off('pan').on('pan', function (dx, dy) {
  253. this._mouseDownFlag = false;
  254. roamHelper.updateViewOnPan(controllerHost, dx, dy);
  255. api.dispatchAction(zrUtil.extend(makeActionBase(), {
  256. dx: dx,
  257. dy: dy
  258. }));
  259. }, this);
  260. controller.off('zoom').on('zoom', function (zoom, mouseX, mouseY) {
  261. this._mouseDownFlag = false;
  262. roamHelper.updateViewOnZoom(controllerHost, zoom, mouseX, mouseY);
  263. api.dispatchAction(zrUtil.extend(makeActionBase(), {
  264. zoom: zoom,
  265. originX: mouseX,
  266. originY: mouseY
  267. }));
  268. if (this._updateGroup) {
  269. var group = this.group;
  270. var scale = group.scale;
  271. group.traverse(function (el) {
  272. if (el.type === 'text') {
  273. el.attr('scale', [1 / scale[0], 1 / scale[1]]);
  274. }
  275. });
  276. }
  277. }, this);
  278. controller.setPointerChecker(function (e, x, y) {
  279. return geo.getViewRectAfterRoam().contain(x, y) && !onIrrelevantElement(e, api, mapOrGeoModel);
  280. });
  281. }
  282. };
  283. var _default = MapDraw;
  284. module.exports = _default;