MapDraw.js 28 KB

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