MapDraw.js 27 KB

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