MapDraw.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. /**
  20. * AUTO-GENERATED FILE. DO NOT MODIFY.
  21. */
  22. /*
  23. * Licensed to the Apache Software Foundation (ASF) under one
  24. * or more contributor license agreements. See the NOTICE file
  25. * distributed with this work for additional information
  26. * regarding copyright ownership. The ASF licenses this file
  27. * to you under the Apache License, Version 2.0 (the
  28. * "License"); you may not use this file except in compliance
  29. * with the License. You may obtain a copy of the License at
  30. *
  31. * http://www.apache.org/licenses/LICENSE-2.0
  32. *
  33. * Unless required by applicable law or agreed to in writing,
  34. * software distributed under the License is distributed on an
  35. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36. * KIND, either express or implied. See the License for the
  37. * specific language governing permissions and limitations
  38. * under the License.
  39. */
  40. import * as zrUtil from 'zrender/lib/core/util.js';
  41. import RoamController from './RoamController.js';
  42. import * as graphic from '../../util/graphic.js';
  43. import { toggleHoverEmphasis, enableComponentHighDownFeatures, setDefaultStateProxy } from '../../util/states.js';
  44. import geoSourceManager from '../../coord/geo/geoSourceManager.js';
  45. import { getUID } from '../../util/component.js';
  46. import { setLabelStyle, getLabelStatesModels } from '../../label/labelStyle.js';
  47. import { getECData } from '../../util/innerStore.js';
  48. import { createOrUpdatePatternFromDecal } from '../../util/decal.js';
  49. import { applyViewCoordSysTransToElement, VIEW_COORD_SYS_TRANS_RAW, VIEW_COORD_SYS_TRANS_ROAM, viewCoordSysCopyTrans, viewCoordSysCopyViewRect } from '../../coord/View.js';
  50. import Displayable from 'zrender/lib/graphic/Displayable.js';
  51. import { makeInner } from '../../util/model.js';
  52. import { updateRoamControllerSimply } from './roamHelper.js';
  53. import { transformableGetLocalTransform } from 'zrender/lib/core/Transformable.js';
  54. import { applyTransform } from 'zrender/lib/core/vector.js';
  55. /**
  56. * Only these tags enable use `itemStyle` if they are named in SVG.
  57. * Other tags like <text> <tspan> <image> might not suitable for `itemStyle`.
  58. * They will not be considered to be styled until some requirements come.
  59. */
  60. var OPTION_STYLE_ENABLED_TAGS = ['rect', 'circle', 'line', 'ellipse', 'polygon', 'polyline', 'path'];
  61. var OPTION_STYLE_ENABLED_TAG_MAP = zrUtil.createHashMap(OPTION_STYLE_ENABLED_TAGS);
  62. var STATE_TRIGGER_TAG_MAP = zrUtil.createHashMap(OPTION_STYLE_ENABLED_TAGS.concat(['g']));
  63. var LABEL_HOST_MAP = zrUtil.createHashMap(OPTION_STYLE_ENABLED_TAGS.concat(['g']));
  64. var mapLabelRaw = makeInner();
  65. function getFixedItemStyle(model) {
  66. var itemStyle = model.getItemStyle();
  67. var areaColor = model.get('areaColor');
  68. // If user want the color not to be changed when hover,
  69. // they should both set areaColor and color to be null.
  70. if (areaColor != null) {
  71. itemStyle.fill = areaColor;
  72. }
  73. return itemStyle;
  74. }
  75. // Only stroke can be used for line.
  76. // Using fill in style if stroke not exits.
  77. // TODO Not sure yet. Perhaps a separate `lineStyle`?
  78. function fixLineStyle(styleHost) {
  79. var style = styleHost.style;
  80. if (style) {
  81. style.stroke = style.stroke || style.fill;
  82. style.fill = null;
  83. }
  84. }
  85. var MapDraw = /** @class */function () {
  86. function MapDraw(api) {
  87. var group = this.group = new graphic.Group();
  88. var transformGroup = this._transformGroup = new graphic.Group();
  89. group.add(transformGroup);
  90. this.uid = getUID('ec_map_draw');
  91. this._controller = new RoamController(api.getZr());
  92. transformGroup.add(this._regionsGroup = new graphic.Group());
  93. transformGroup.add(this._svgGroup = new graphic.Group());
  94. }
  95. MapDraw.prototype.draw = function (mapOrGeoModel, ecModel, api, fromView, payload) {
  96. var mapDraw = this;
  97. // Map series has data. GEO model that controlled by map series
  98. // will be assigned with map data. Other GEO model has no data.
  99. var data = mapOrGeoModel.getData && mapOrGeoModel.getData();
  100. if (isGeoModel(mapOrGeoModel)) {
  101. ecModel.eachComponent({
  102. mainType: 'series',
  103. subType: 'map'
  104. }, function (mapSeries) {
  105. if (!data && mapSeries.getHostGeoModel() === mapOrGeoModel) {
  106. data = mapSeries.getData();
  107. }
  108. });
  109. }
  110. var geo = mapOrGeoModel.coordinateSystem;
  111. var viewCoordSys = geo.view;
  112. var regionsGroup = this._regionsGroup;
  113. var transformGroup = this._transformGroup;
  114. // No animation when first draw or in action
  115. var isFirstDraw = !regionsGroup.childAt(0) || payload;
  116. var clipRect;
  117. if (geo.shouldClip()) {
  118. clipRect = viewCoordSysCopyViewRect(null, viewCoordSys);
  119. this.group.setClipPath(new graphic.Rect({
  120. shape: clipRect.clone()
  121. }));
  122. } else {
  123. this.group.removeClipPath();
  124. }
  125. applyViewCoordSysTransToElement(transformGroup, VIEW_COORD_SYS_TRANS_ROAM, viewCoordSys, isFirstDraw ? null : mapOrGeoModel);
  126. var isVisualEncodedByVisualMap = data && data.getVisual('visualMeta') && data.getVisual('visualMeta').length > 0;
  127. if (geo.resourceType === 'geoJSON') {
  128. this._buildGeoJSON(viewCoordSys, api, geo, mapOrGeoModel, data, isVisualEncodedByVisualMap);
  129. } else if (geo.resourceType === 'geoSVG') {
  130. this._buildSVG(viewCoordSys, api, geo, mapOrGeoModel, data, isVisualEncodedByVisualMap);
  131. }
  132. updateRoamControllerSimply(mapOrGeoModel, api, this._controller, function (e, x, y) {
  133. return mapOrGeoModel.coordinateSystem.containPoint([x, y]);
  134. }, clipRect, function () {
  135. mapDraw._mouseDownFlag = false;
  136. }, false,
  137. // Default roam type.
  138. true);
  139. this._updateMapSelectHandler(mapOrGeoModel, regionsGroup, api, fromView);
  140. };
  141. MapDraw.prototype.__updateOnOwnRoam = function (mapOrGeoModel) {
  142. applyViewCoordSysTransToElement(this._transformGroup, VIEW_COORD_SYS_TRANS_ROAM, mapOrGeoModel.coordinateSystem.view, null);
  143. };
  144. MapDraw.prototype._buildGeoJSON = function (viewCoordSys, api, geo, mapOrGeoModel, data, isVisualEncodedByVisualMap) {
  145. var regionsGroupByName = this._regionsGroupByName = zrUtil.createHashMap();
  146. var regionsInfoByName = zrUtil.createHashMap();
  147. var regionsGroup = this._regionsGroup;
  148. var projection = geo.projection;
  149. var projectionStream = projection && projection.stream;
  150. var transMt = transformableGetLocalTransform(viewCoordSysCopyTrans(null, viewCoordSys, VIEW_COORD_SYS_TRANS_RAW));
  151. function transformPoint(point, project) {
  152. if (project) {
  153. // projection may return null point.
  154. point = project(point);
  155. }
  156. return point && applyTransform([], point, transMt);
  157. }
  158. ;
  159. function transformPolygonPoints(inPoints) {
  160. var outPoints = [];
  161. // If projectionStream is provided. Use it instead of single point project.
  162. var project = !projectionStream && projection && projection.project;
  163. for (var i = 0; i < inPoints.length; ++i) {
  164. var newPt = transformPoint(inPoints[i], project);
  165. newPt && outPoints.push(newPt);
  166. }
  167. return outPoints;
  168. }
  169. function getPolyShape(points) {
  170. return {
  171. shape: {
  172. points: transformPolygonPoints(points)
  173. }
  174. };
  175. }
  176. regionsGroup.removeAll();
  177. // Only when the resource is GeoJSON, there is `geo.regions`.
  178. zrUtil.each(geo.regions, function (region) {
  179. var regionName = region.name;
  180. // Consider in GeoJson properties.name may be duplicated, for example,
  181. // there is multiple region named "United Kindom" or "France" (so many
  182. // colonies). And it is not appropriate to merge them in geo, which
  183. // will make them share the same label and bring trouble in label
  184. // location calculation.
  185. var regionGroup = regionsGroupByName.get(regionName);
  186. var _a = regionsInfoByName.get(regionName) || {},
  187. dataIdx = _a.dataIdx,
  188. regionModel = _a.regionModel;
  189. if (!regionGroup) {
  190. regionGroup = regionsGroupByName.set(regionName, new graphic.Group());
  191. regionsGroup.add(regionGroup);
  192. dataIdx = data ? data.indexOfName(regionName) : null;
  193. regionModel = isGeoModel(mapOrGeoModel) ? mapOrGeoModel.getRegionModel(regionName) : data ? data.getItemModel(dataIdx) : null;
  194. var silent = regionModel.get('silent', true);
  195. silent != null && (regionGroup.silent = silent);
  196. regionsInfoByName.set(regionName, {
  197. dataIdx: dataIdx,
  198. regionModel: regionModel
  199. });
  200. }
  201. var polygonSubpaths = [];
  202. var polylineSubpaths = [];
  203. zrUtil.each(region.geometries, function (geometry) {
  204. // Polygon and MultiPolygon
  205. if (geometry.type === 'polygon') {
  206. var polys = [geometry.exterior].concat(geometry.interiors || []);
  207. if (projectionStream) {
  208. polys = projectPolys(polys, projectionStream);
  209. }
  210. zrUtil.each(polys, function (poly) {
  211. polygonSubpaths.push(new graphic.Polygon(getPolyShape(poly)));
  212. });
  213. }
  214. // LineString and MultiLineString
  215. else {
  216. var points = geometry.points;
  217. if (projectionStream) {
  218. points = projectPolys(points, projectionStream, true);
  219. }
  220. zrUtil.each(points, function (points) {
  221. polylineSubpaths.push(new graphic.Polyline(getPolyShape(points)));
  222. });
  223. }
  224. });
  225. var centerPt = transformPoint(region.getCenter(), projection && projection.project);
  226. function createCompoundPath(subpaths, isLine) {
  227. if (!subpaths.length) {
  228. return;
  229. }
  230. var compoundPath = new graphic.CompoundPath({
  231. culling: true,
  232. segmentIgnoreThreshold: 1,
  233. shape: {
  234. paths: subpaths
  235. }
  236. });
  237. regionGroup.add(compoundPath);
  238. applyOptionStyleForRegion(api, data, isVisualEncodedByVisualMap, compoundPath, dataIdx, regionModel);
  239. resetLabelForRegion(mapOrGeoModel, data, compoundPath, regionName, regionModel, dataIdx, centerPt);
  240. if (isLine) {
  241. fixLineStyle(compoundPath);
  242. zrUtil.each(compoundPath.states, fixLineStyle);
  243. }
  244. }
  245. createCompoundPath(polygonSubpaths);
  246. createCompoundPath(polylineSubpaths, true);
  247. });
  248. // Ensure children have been added to `regionGroup` before calling them.
  249. regionsGroupByName.each(function (regionGroup, regionName) {
  250. var _a = regionsInfoByName.get(regionName),
  251. dataIdx = _a.dataIdx,
  252. regionModel = _a.regionModel;
  253. resetEventTriggerForRegion(mapOrGeoModel, data, regionGroup, regionName, regionModel, dataIdx);
  254. resetTooltipForRegion(mapOrGeoModel, data, regionGroup, regionName, regionModel);
  255. resetStateTriggerForRegion(mapOrGeoModel, regionGroup, regionName, regionModel);
  256. }, this);
  257. };
  258. MapDraw.prototype._buildSVG = function (viewCoordSys, api, geo, mapOrGeoModel, data, isVisualEncodedByVisualMap) {
  259. var mapName = geo.map;
  260. viewCoordSysCopyTrans(this._svgGroup, viewCoordSys, VIEW_COORD_SYS_TRANS_RAW);
  261. if (this._svgResourceChanged(mapName)) {
  262. this._freeSVG();
  263. this._useSVG(mapName);
  264. }
  265. var svgDispatcherMap = this._svgDispatcherMap = zrUtil.createHashMap();
  266. var focusSelf = false;
  267. zrUtil.each(this._svgGraphicRecord.named, function (namedItem) {
  268. // Note that we also allow different elements have the same name.
  269. // For example, a glyph of a city and the label of the city have
  270. // the same name and their tooltip info can be defined in a single
  271. // region option.
  272. var regionName = namedItem.name;
  273. var svgNodeTagLower = namedItem.svgNodeTagLower;
  274. var el = namedItem.el;
  275. var dataIdx = data ? data.indexOfName(regionName) : null;
  276. var regionModel = mapOrGeoModel.getRegionModel(regionName);
  277. if (OPTION_STYLE_ENABLED_TAG_MAP.get(svgNodeTagLower) != null && el instanceof Displayable) {
  278. applyOptionStyleForRegion(api, data, isVisualEncodedByVisualMap, el, dataIdx, regionModel);
  279. }
  280. if (el instanceof Displayable) {
  281. el.culling = true;
  282. }
  283. var silent = regionModel.get('silent', true);
  284. silent != null && (el.silent = silent);
  285. // We do not know how the SVG like so we'd better not to change z2.
  286. // Otherwise it might bring some unexpected result. For example,
  287. // an area hovered that make some inner city can not be clicked.
  288. el.z2EmphasisLift = 0;
  289. // If self named:
  290. if (!namedItem.namedFrom) {
  291. // label should batter to be displayed based on the center of <g>
  292. // if it is named rather than displayed on each child.
  293. if (LABEL_HOST_MAP.get(svgNodeTagLower) != null) {
  294. resetLabelForRegion(mapOrGeoModel, data, el, regionName, regionModel, dataIdx, null);
  295. }
  296. resetEventTriggerForRegion(mapOrGeoModel, data, el, regionName, regionModel, dataIdx);
  297. resetTooltipForRegion(mapOrGeoModel, data, el, regionName, regionModel);
  298. if (STATE_TRIGGER_TAG_MAP.get(svgNodeTagLower) != null) {
  299. var focus_1 = resetStateTriggerForRegion(mapOrGeoModel, el, regionName, regionModel);
  300. if (focus_1 === 'self') {
  301. focusSelf = true;
  302. }
  303. var els = svgDispatcherMap.get(regionName) || svgDispatcherMap.set(regionName, []);
  304. els.push(el);
  305. }
  306. }
  307. }, this);
  308. this._enableBlurEntireSVG(focusSelf, mapOrGeoModel);
  309. };
  310. MapDraw.prototype._enableBlurEntireSVG = function (focusSelf, mapOrGeoModel) {
  311. // It's a little complicated to support blurring the entire geoSVG in series-map.
  312. // So do not support it until some requirements come.
  313. // At present, in series-map, only regions can be blurred.
  314. if (focusSelf && isGeoModel(mapOrGeoModel)) {
  315. var blurStyle = mapOrGeoModel.getModel(['blur', 'itemStyle']).getItemStyle();
  316. // Only support `opacity` here. Because not sure that other props are suitable for
  317. // all of the elements generated by SVG (especially for Text/TSpan/Image/... ).
  318. var opacity_1 = blurStyle.opacity;
  319. this._svgGraphicRecord.root.traverse(function (el) {
  320. if (!el.isGroup) {
  321. // PENDING: clear those settings to SVG elements when `_freeSVG`.
  322. // (Currently it happen not to be needed.)
  323. setDefaultStateProxy(el);
  324. var style = el.ensureState('blur').style || {};
  325. // Do not overwrite the region style that already set from region option.
  326. if (style.opacity == null && opacity_1 != null) {
  327. style.opacity = opacity_1;
  328. }
  329. // If `ensureState('blur').style = {}`, there will be default opacity.
  330. // Enable `stateTransition` (animation).
  331. el.ensureState('emphasis');
  332. }
  333. });
  334. }
  335. };
  336. MapDraw.prototype.remove = function () {
  337. this._regionsGroup.removeAll();
  338. this._regionsGroupByName = null;
  339. this._svgGroup.removeAll();
  340. this._freeSVG();
  341. this._controller.disable();
  342. };
  343. MapDraw.prototype.findHighDownDispatchers = function (name, geoModel) {
  344. if (name == null) {
  345. return [];
  346. }
  347. var geo = geoModel.coordinateSystem;
  348. if (geo.resourceType === 'geoJSON') {
  349. var regionsGroupByName = this._regionsGroupByName;
  350. if (regionsGroupByName) {
  351. var regionGroup = regionsGroupByName.get(name);
  352. return regionGroup ? [regionGroup] : [];
  353. }
  354. } else if (geo.resourceType === 'geoSVG') {
  355. return this._svgDispatcherMap && this._svgDispatcherMap.get(name) || [];
  356. }
  357. };
  358. MapDraw.prototype._svgResourceChanged = function (mapName) {
  359. return this._svgMapName !== mapName;
  360. };
  361. MapDraw.prototype._useSVG = function (mapName) {
  362. var resource = geoSourceManager.getGeoResource(mapName);
  363. if (resource && resource.type === 'geoSVG') {
  364. var svgGraphic = resource.useGraphic(this.uid);
  365. this._svgGroup.add(svgGraphic.root);
  366. this._svgGraphicRecord = svgGraphic;
  367. this._svgMapName = mapName;
  368. }
  369. };
  370. MapDraw.prototype._freeSVG = function () {
  371. var mapName = this._svgMapName;
  372. if (mapName == null) {
  373. return;
  374. }
  375. var resource = geoSourceManager.getGeoResource(mapName);
  376. if (resource && resource.type === 'geoSVG') {
  377. resource.freeGraphic(this.uid);
  378. }
  379. this._svgGraphicRecord = null;
  380. this._svgDispatcherMap = null;
  381. this._svgGroup.removeAll();
  382. this._svgMapName = null;
  383. };
  384. /**
  385. * FIXME: this is a temporarily workaround.
  386. * When `geoRoam` the elements need to be reset in `MapView['render']`, because the props like
  387. * `ignore` might have been modified by `LabelManager`, and `LabelManager#addLabelsOfSeries`
  388. * will subsequently cache `defaultAttr` like `ignore`. If do not do this reset, the modified
  389. * props will have no chance to be restored.
  390. * Note: This reset should be after `clearStates` in `renderSeries` because `useStates` in
  391. * `renderSeries` will cache the modified `ignore` to `el._normalState`.
  392. * TODO:
  393. * Use clone/immutable in `LabelManager`?
  394. */
  395. MapDraw.prototype.resetForLabelLayout = function () {
  396. this.group.traverse(function (el) {
  397. var label = el.getTextContent();
  398. if (label) {
  399. label.ignore = mapLabelRaw(label).ignore;
  400. }
  401. });
  402. };
  403. MapDraw.prototype._updateMapSelectHandler = function (mapOrGeoModel, regionsGroup, api, fromView) {
  404. var mapDraw = this;
  405. regionsGroup.off('mousedown');
  406. regionsGroup.off('click');
  407. if (mapOrGeoModel.get('selectedMode')) {
  408. regionsGroup.on('mousedown', function () {
  409. mapDraw._mouseDownFlag = true;
  410. });
  411. regionsGroup.on('click', function (e) {
  412. if (!mapDraw._mouseDownFlag) {
  413. return;
  414. }
  415. mapDraw._mouseDownFlag = false;
  416. });
  417. }
  418. };
  419. return MapDraw;
  420. }();
  421. ;
  422. function applyOptionStyleForRegion(api, data, isVisualEncodedByVisualMap, el, dataIndex, regionModel) {
  423. // All of the path are using `itemStyle`, because
  424. // (1) Some SVG also use fill on polyline (The different between
  425. // polyline and polygon is "open" or "close" but not fill or not).
  426. // (2) For the common props like opacity, if some use itemStyle
  427. // and some use `lineStyle`, it might confuse users.
  428. // (3) Most SVG use <path>, where can not detect whether to draw a "line"
  429. // or a filled shape, so use `itemStyle` for <path>.
  430. var normalStyleModel = regionModel.getModel('itemStyle');
  431. var emphasisStyleModel = regionModel.getModel(['emphasis', 'itemStyle']);
  432. var blurStyleModel = regionModel.getModel(['blur', 'itemStyle']);
  433. var selectStyleModel = regionModel.getModel(['select', 'itemStyle']);
  434. // NOTE: DON'T use 'style' in visual when drawing map.
  435. // This component is used for drawing underlying map for both geo component and map series.
  436. var normalStyle = getFixedItemStyle(normalStyleModel);
  437. var emphasisStyle = getFixedItemStyle(emphasisStyleModel);
  438. var selectStyle = getFixedItemStyle(selectStyleModel);
  439. var blurStyle = getFixedItemStyle(blurStyleModel);
  440. // Update the itemStyle if has data visual
  441. if (data) {
  442. // Only visual color of each item will be used. It can be encoded by visualMap
  443. // But visual color of series is used in symbol drawing
  444. // Visual color for each series is for the symbol draw
  445. var style = data.getItemVisual(dataIndex, 'style');
  446. var decal = data.getItemVisual(dataIndex, 'decal');
  447. if (isVisualEncodedByVisualMap && style.fill) {
  448. normalStyle.fill = style.fill;
  449. }
  450. if (decal) {
  451. normalStyle.decal = createOrUpdatePatternFromDecal(decal, api);
  452. }
  453. }
  454. // SVG text, tspan and image can be named but not supporeted
  455. // to be styled by region option yet.
  456. el.setStyle(normalStyle);
  457. el.style.strokeNoScale = true;
  458. el.ensureState('emphasis').style = emphasisStyle;
  459. el.ensureState('select').style = selectStyle;
  460. el.ensureState('blur').style = blurStyle;
  461. // Enable blur
  462. setDefaultStateProxy(el);
  463. }
  464. function resetLabelForRegion(mapOrGeoModel, data, el, regionName, regionModel,
  465. // Exist only if `viewBuildCtx.data` exists.
  466. dataIdx,
  467. // If labelXY not provided, use `textConfig.position: 'inside'`
  468. labelXY) {
  469. var isDataNaN = data && isNaN(data.get(data.mapDimension('value'), dataIdx));
  470. var itemLayout = data && data.getItemLayout(dataIdx);
  471. // In the following cases label will be drawn
  472. // 1. In map series and data value is NaN
  473. // 2. In geo component
  474. // 3. Region has no series legendIcon, which will be add a showLabel flag in mapSymbolLayout
  475. if (isGeoModel(mapOrGeoModel) || isDataNaN || itemLayout && itemLayout.showLabel) {
  476. var query = !isGeoModel(mapOrGeoModel) ? dataIdx : regionName;
  477. var labelFetcher = void 0;
  478. // Consider dataIdx not found.
  479. if (!data || dataIdx >= 0) {
  480. labelFetcher = mapOrGeoModel;
  481. }
  482. var specifiedTextOpt = labelXY ? {
  483. normal: {
  484. align: 'center',
  485. verticalAlign: 'middle'
  486. }
  487. } : null;
  488. // Caveat: must be called after `setDefaultStateProxy(el);` called.
  489. // because textContent will be assign with `el.stateProxy` inside.
  490. setLabelStyle(el, getLabelStatesModels(regionModel), {
  491. labelFetcher: labelFetcher,
  492. labelDataIndex: query,
  493. defaultText: regionName
  494. }, specifiedTextOpt);
  495. var textEl = el.getTextContent();
  496. if (textEl) {
  497. mapLabelRaw(textEl).ignore = textEl.ignore;
  498. if (el.textConfig && labelXY) {
  499. // Compute a relative offset based on the el bounding rect.
  500. var rect = el.getBoundingRect().clone();
  501. // Need to make sure the percent position base on the same rect in normal and
  502. // emphasis state. Otherwise if using boundingRect of el, but the emphasis state
  503. // has borderWidth (even 0.5px), the text position will be changed obviously
  504. // if the position is very big like ['1234%', '1345%'].
  505. el.textConfig.layoutRect = rect;
  506. el.textConfig.position = [(labelXY[0] - rect.x) / rect.width * 100 + '%', (labelXY[1] - rect.y) / rect.height * 100 + '%'];
  507. }
  508. }
  509. // PENDING:
  510. // If labelLayout is enabled (test/label-layout.html), el.dataIndex should be specified.
  511. // But el.dataIndex is also used to determine whether user event should be triggered,
  512. // where el.seriesIndex or el.dataModel must be specified. At present for a single el
  513. // there is not case that "only label layout enabled but user event disabled", so here
  514. // we depends `resetEventTriggerForRegion` to do the job of setting `el.dataIndex`.
  515. el.disableLabelAnimation = true;
  516. } else {
  517. el.removeTextContent();
  518. el.removeTextConfig();
  519. el.disableLabelAnimation = null;
  520. }
  521. }
  522. function resetEventTriggerForRegion(mapOrGeoModel, data, eventTrigger, regionName, regionModel,
  523. // Exist only if `viewBuildCtx.data` exists.
  524. dataIdx) {
  525. // setItemGraphicEl, setHoverStyle after all polygons and labels
  526. // are added to the regionGroup
  527. if (data) {
  528. // FIXME: when series-map use a SVG map, and there are duplicated name specified
  529. // on different SVG elements, after `data.setItemGraphicEl(...)`:
  530. // (1) all of them will be mounted with `dataIndex`, `seriesIndex`, so that tooltip
  531. // can be triggered only mouse hover. That's correct.
  532. // (2) only the last element will be kept in `data`, so that if trigger tooltip
  533. // by `dispatchAction`, only the last one can be found and triggered. That might be
  534. // not correct. We will fix it in future if anyone demanding that.
  535. data.setItemGraphicEl(dataIdx, eventTrigger);
  536. }
  537. // series-map will not trigger "geoselectchange" no matter it is
  538. // based on a declared geo component. Because series-map will
  539. // trigger "selectchange". If it trigger both the two events,
  540. // If users call `chart.dispatchAction({type: 'toggleSelect'})`,
  541. // it not easy to also fire event "geoselectchanged".
  542. else {
  543. // Package custom mouse event for geo component
  544. getECData(eventTrigger).eventData = {
  545. componentType: 'geo',
  546. componentIndex: mapOrGeoModel.componentIndex,
  547. geoIndex: mapOrGeoModel.componentIndex,
  548. name: regionName,
  549. region: regionModel && regionModel.option || {}
  550. };
  551. }
  552. }
  553. function resetTooltipForRegion(mapOrGeoModel, data, el, regionName, regionModel) {
  554. if (!data) {
  555. graphic.setTooltipConfig({
  556. el: el,
  557. componentModel: mapOrGeoModel,
  558. itemName: regionName,
  559. // @ts-ignore FIXME:TS fix the "compatible with each other"?
  560. itemTooltipOption: regionModel.get('tooltip')
  561. });
  562. }
  563. }
  564. function resetStateTriggerForRegion(mapOrGeoModel, el, regionName, regionModel) {
  565. // @ts-ignore FIXME:TS fix the "compatible with each other"?
  566. el.highDownSilentOnTouch = !!mapOrGeoModel.get('selectedMode');
  567. // @ts-ignore FIXME:TS fix the "compatible with each other"?
  568. var emphasisModel = regionModel.getModel('emphasis');
  569. var focus = emphasisModel.get('focus');
  570. toggleHoverEmphasis(el, focus, emphasisModel.get('blurScope'), emphasisModel.get('disabled'));
  571. if (isGeoModel(mapOrGeoModel)) {
  572. enableComponentHighDownFeatures(el, mapOrGeoModel, regionName);
  573. }
  574. return focus;
  575. }
  576. function projectPolys(rings,
  577. // Polygons include exterior and interiors. Or polylines.
  578. createStream, isLine) {
  579. var polygons = [];
  580. var curPoly;
  581. function startPolygon() {
  582. curPoly = [];
  583. }
  584. function endPolygon() {
  585. if (curPoly.length) {
  586. polygons.push(curPoly);
  587. curPoly = [];
  588. }
  589. }
  590. var stream = createStream({
  591. polygonStart: startPolygon,
  592. polygonEnd: endPolygon,
  593. lineStart: startPolygon,
  594. lineEnd: endPolygon,
  595. point: function (x, y) {
  596. // May have NaN values from stream.
  597. if (isFinite(x) && isFinite(y)) {
  598. curPoly.push([x, y]);
  599. }
  600. },
  601. sphere: function () {}
  602. });
  603. !isLine && stream.polygonStart();
  604. zrUtil.each(rings, function (ring) {
  605. stream.lineStart();
  606. for (var i = 0; i < ring.length; i++) {
  607. stream.point(ring[i][0], ring[i][1]);
  608. }
  609. stream.lineEnd();
  610. });
  611. !isLine && stream.polygonEnd();
  612. return polygons;
  613. }
  614. function isGeoModel(mapOrGeoModel) {
  615. return mapOrGeoModel.mainType === 'geo';
  616. }
  617. export default MapDraw;
  618. // @ts-ignore FIXME:TS fix the "compatible with each other"?