map_layer.js 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571
  1. /**
  2. * DevExtreme (viz/vector_map/map_layer.js)
  3. * Version: 19.1.16
  4. * Build date: Tue Oct 18 2022
  5. *
  6. * Copyright (c) 2012 - 2022 Developer Express Inc. ALL RIGHTS RESERVED
  7. * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
  8. */
  9. "use strict";
  10. var noop = require("../../core/utils/common").noop;
  11. var extend = require("../../core/utils/extend").extend;
  12. var each = require("../../core/utils/iterator").each;
  13. var _Number = Number;
  14. var _String = String;
  15. var _abs = Math.abs;
  16. var _round = Math.round;
  17. var _min = Math.min;
  18. var _max = Math.max;
  19. var _sqrt = Math.sqrt;
  20. var DataHelperMixin = require("../../data_helper");
  21. var _isFunction = require("../../core/utils/type").isFunction;
  22. var _isDefined = require("../../core/utils/type").isDefined;
  23. var _isArray = Array.isArray;
  24. var vizUtils = require("../core/utils");
  25. var _parseScalar = vizUtils.parseScalar;
  26. var _patchFontOptions = vizUtils.patchFontOptions;
  27. var _normalizeEnum = vizUtils.normalizeEnum;
  28. var _noop = noop;
  29. var _extend = extend;
  30. var _each = each;
  31. var _concat = Array.prototype.concat;
  32. var TYPE_AREA = "area";
  33. var TYPE_LINE = "line";
  34. var TYPE_MARKER = "marker";
  35. var STATE_DEFAULT = 0;
  36. var STATE_HOVERED = 1;
  37. var STATE_SELECTED = 2;
  38. var STATE_TO_INDEX = [0, 1, 2, 2];
  39. var TOLERANCE = 1;
  40. var SELECTIONS = {
  41. none: null,
  42. single: -1,
  43. multiple: NaN
  44. };
  45. function getSelection(selectionMode) {
  46. var selection = _normalizeEnum(selectionMode);
  47. selection = selection in SELECTIONS ? SELECTIONS[selection] : SELECTIONS.single;
  48. if (null !== selection) {
  49. selection = {
  50. state: {},
  51. single: selection
  52. }
  53. }
  54. return selection
  55. }
  56. function getName(opt, index) {
  57. return (opt[index] || {}).name
  58. }
  59. function EmptySource() {}
  60. EmptySource.prototype.count = function() {
  61. return 0
  62. };
  63. function ArraySource(raw) {
  64. this.raw = raw
  65. }
  66. ArraySource.prototype = {
  67. constructor: ArraySource,
  68. count: function() {
  69. return this.raw.length
  70. },
  71. item: function(index) {
  72. return this.raw[index]
  73. },
  74. geometry: function(item) {
  75. return {
  76. coordinates: item.coordinates
  77. }
  78. },
  79. attributes: function(item) {
  80. return item.attributes
  81. }
  82. };
  83. function GeoJsonSource(raw) {
  84. this.raw = raw
  85. }
  86. GeoJsonSource.prototype = {
  87. constructor: GeoJsonSource,
  88. count: function() {
  89. return this.raw.features.length
  90. },
  91. item: function(index) {
  92. return this.raw.features[index]
  93. },
  94. geometry: function(item) {
  95. return item.geometry
  96. },
  97. attributes: function(item) {
  98. return item.properties
  99. }
  100. };
  101. function isGeoJsonObject(obj) {
  102. return _isArray(obj.features)
  103. }
  104. function unwrapFromDataSource(source) {
  105. var sourceType;
  106. if (source) {
  107. if (isGeoJsonObject(source)) {
  108. sourceType = GeoJsonSource
  109. } else {
  110. if (1 === source.length && source[0] && isGeoJsonObject(source[0])) {
  111. sourceType = GeoJsonSource;
  112. source = source[0]
  113. } else {
  114. if (_isArray(source)) {
  115. sourceType = ArraySource
  116. }
  117. }
  118. }
  119. }
  120. sourceType = sourceType || EmptySource;
  121. return new sourceType(source)
  122. }
  123. function wrapToDataSource(option) {
  124. return option ? isGeoJsonObject(option) ? [option] : option : []
  125. }
  126. function customizeHandles(proxies, callback, widget) {
  127. callback.call(widget, proxies)
  128. }
  129. function setAreaLabelVisibility(label) {
  130. label.text.attr({
  131. visibility: label.size[0] / label.spaceSize[0] < TOLERANCE && label.size[1] / label.spaceSize[1] < TOLERANCE ? null : "hidden"
  132. })
  133. }
  134. function setLineLabelVisibility(label) {
  135. label.text.attr({
  136. visibility: label.size[0] / label.spaceSize[0] < TOLERANCE || label.size[1] / label.spaceSize[1] < TOLERANCE ? null : "hidden"
  137. })
  138. }
  139. function getDataValue(proxy, dataField) {
  140. return proxy.attribute(dataField)
  141. }
  142. var TYPE_TO_TYPE_MAP = {
  143. Point: TYPE_MARKER,
  144. MultiPoint: TYPE_LINE,
  145. LineString: TYPE_LINE,
  146. MultiLineString: TYPE_LINE,
  147. Polygon: TYPE_AREA,
  148. MultiPolygon: TYPE_AREA
  149. };
  150. function pick(a, b) {
  151. return void 0 !== a ? a : b
  152. }
  153. function guessTypeByData(sample) {
  154. var type = TYPE_TO_TYPE_MAP[sample.type];
  155. var coordinates = sample.coordinates;
  156. if (!type) {
  157. if ("number" === typeof coordinates[0]) {
  158. type = TYPE_MARKER
  159. } else {
  160. if ("number" === typeof coordinates[0][0]) {
  161. type = TYPE_LINE
  162. } else {
  163. type = TYPE_AREA
  164. }
  165. }
  166. }
  167. return type
  168. }
  169. var selectStrategy = function(options, data) {
  170. var type = _normalizeEnum(options.type);
  171. var elementType = _normalizeEnum(options.elementType);
  172. var strategy = _extend({}, emptyStrategy);
  173. if (data.count() > 0) {
  174. var sample = data.geometry(data.item(0));
  175. type = strategiesByType[type] ? type : guessTypeByData(sample);
  176. _extend(strategy, strategiesByType[type]);
  177. strategy.fullType = strategy.type = type;
  178. if (strategiesByGeometry[type]) {
  179. _extend(strategy, strategiesByGeometry[type](sample))
  180. }
  181. if (strategiesByElementType[type]) {
  182. elementType = strategiesByElementType[type][elementType] ? elementType : strategiesByElementType[type]._default;
  183. _extend(strategy, strategiesByElementType[type][elementType]);
  184. strategy.elementType = elementType;
  185. strategy.fullType += ":" + elementType
  186. }
  187. }
  188. return strategy
  189. };
  190. function applyElementState(figure, styles, state, field) {
  191. figure[field].attr(styles[field][state])
  192. }
  193. var emptyStrategy = {
  194. setup: _noop,
  195. reset: _noop,
  196. arrange: _noop,
  197. updateGrouping: _noop,
  198. getDefaultColor: _noop
  199. };
  200. var strategiesByType = {};
  201. strategiesByType[TYPE_AREA] = {
  202. projectLabel: projectAreaLabel,
  203. transform: transformPointList,
  204. transformLabel: transformAreaLabel,
  205. draw: function(context, figure, data) {
  206. figure.root = context.renderer.path([], "area").data(context.dataKey, data)
  207. },
  208. refresh: _noop,
  209. getLabelOffset: function(label) {
  210. setAreaLabelVisibility(label);
  211. return [0, 0]
  212. },
  213. getStyles: function(settings) {
  214. var color = settings.color || null;
  215. var borderColor = settings.borderColor || null;
  216. var borderWidth = pick(settings.borderWidth, null);
  217. var opacity = pick(settings.opacity, null);
  218. return {
  219. root: [{
  220. "class": "dxm-area",
  221. stroke: borderColor,
  222. "stroke-width": borderWidth,
  223. fill: color,
  224. opacity: opacity
  225. }, {
  226. "class": "dxm-area dxm-area-hovered",
  227. stroke: settings.hoveredBorderColor || borderColor,
  228. "stroke-width": pick(settings.hoveredBorderWidth, borderWidth),
  229. fill: settings.hoveredColor || color,
  230. opacity: pick(settings.hoveredOpacity, opacity)
  231. }, {
  232. "class": "dxm-area dxm-area-selected",
  233. stroke: settings.selectedBorderColor || borderColor,
  234. "stroke-width": pick(settings.selectedBorderWidth, borderWidth),
  235. fill: settings.selectedColor || color,
  236. opacity: pick(settings.selectedOpacity, opacity)
  237. }]
  238. }
  239. },
  240. setState: function(figure, styles, state) {
  241. applyElementState(figure, styles, state, "root")
  242. },
  243. hasLabelsGroup: true,
  244. updateGrouping: function(context) {
  245. groupByColor(context)
  246. },
  247. getDefaultColor: _noop
  248. };
  249. strategiesByType[TYPE_LINE] = {
  250. projectLabel: projectLineLabel,
  251. transform: transformPointList,
  252. transformLabel: transformLineLabel,
  253. draw: function(context, figure, data) {
  254. figure.root = context.renderer.path([], "line").data(context.dataKey, data)
  255. },
  256. refresh: _noop,
  257. getLabelOffset: function(label) {
  258. setLineLabelVisibility(label);
  259. return [0, 0]
  260. },
  261. getStyles: function(settings) {
  262. var color = settings.color || settings.borderColor || null;
  263. var width = pick(settings.borderWidth, null);
  264. var opacity = pick(settings.opacity, null);
  265. return {
  266. root: [{
  267. "class": "dxm-line",
  268. stroke: color,
  269. "stroke-width": width,
  270. opacity: opacity
  271. }, {
  272. "class": "dxm-line dxm-line-hovered",
  273. stroke: settings.hoveredColor || settings.hoveredBorderColor || color,
  274. "stroke-width": pick(settings.hoveredBorderWidth, width),
  275. opacity: pick(settings.hoveredOpacity, opacity)
  276. }, {
  277. "class": "dxm-line dxm-line-selected",
  278. stroke: settings.selectedColor || settings.selectedBorderColor || color,
  279. "stroke-width": pick(settings.selectedBorderWidth, width),
  280. opacity: pick(settings.selectedOpacity, opacity)
  281. }]
  282. }
  283. },
  284. setState: function(figure, styles, state) {
  285. applyElementState(figure, styles, state, "root")
  286. },
  287. hasLabelsGroup: true,
  288. updateGrouping: function(context) {
  289. groupByColor(context)
  290. },
  291. getDefaultColor: _noop
  292. };
  293. strategiesByType[TYPE_MARKER] = {
  294. project: projectPoint,
  295. transform: transformPoint,
  296. draw: function(context, figure, data) {
  297. figure.root = context.renderer.g();
  298. this._draw(context, figure, data)
  299. },
  300. refresh: _noop,
  301. hasLabelsGroup: false,
  302. getLabelOffset: function(label, settings) {
  303. return [_round((label.size[0] + _max(settings.size || 0, 0)) / 2) + 2, 0]
  304. },
  305. getStyles: function(settings) {
  306. var styles = {
  307. root: [{
  308. "class": "dxm-marker"
  309. }, {
  310. "class": "dxm-marker dxm-marker-hovered"
  311. }, {
  312. "class": "dxm-marker dxm-marker-selected"
  313. }]
  314. };
  315. this._getStyles(styles, settings);
  316. return styles
  317. },
  318. setState: function(figure, styles, state) {
  319. applyElementState(figure, styles, state, "root");
  320. this._setState(figure, styles, state)
  321. },
  322. updateGrouping: function(context) {
  323. groupByColor(context);
  324. groupBySize(context)
  325. },
  326. getDefaultColor: function(ctx, palette) {
  327. return ctx.params.themeManager.getAccentColor(palette)
  328. }
  329. };
  330. var strategiesByGeometry = {};
  331. strategiesByGeometry[TYPE_AREA] = function(sample) {
  332. var coordinates = sample.coordinates;
  333. return {
  334. project: coordinates[0] && coordinates[0][0] && coordinates[0][0][0] && "number" === typeof coordinates[0][0][0][0] ? projectMultiPolygon : projectPolygon
  335. }
  336. };
  337. strategiesByGeometry[TYPE_LINE] = function(sample) {
  338. var coordinates = sample.coordinates;
  339. return {
  340. project: coordinates[0] && coordinates[0][0] && "number" === typeof coordinates[0][0][0] ? projectPolygon : projectLineString
  341. }
  342. };
  343. var strategiesByElementType = {};
  344. strategiesByElementType[TYPE_MARKER] = {
  345. _default: "dot",
  346. dot: {
  347. setup: function(context) {
  348. context.filter = context.renderer.shadowFilter("-40%", "-40%", "180%", "200%", 0, 1, 1, "#000000", .2)
  349. },
  350. reset: function(context) {
  351. context.filter.dispose();
  352. context.filter = null
  353. },
  354. _draw: function(ctx, figure, data) {
  355. figure.back = ctx.renderer.circle().sharp().data(ctx.dataKey, data).append(figure.root);
  356. figure.dot = ctx.renderer.circle().sharp().data(ctx.dataKey, data).append(figure.root)
  357. },
  358. refresh: function(ctx, figure, data, proxy, settings) {
  359. figure.dot.attr({
  360. filter: settings.shadow ? ctx.filter.id : null
  361. })
  362. },
  363. _getStyles: function(styles, style) {
  364. var size = style.size > 0 ? _Number(style.size) : 0;
  365. var hoveredSize = size;
  366. var selectedSize = size + (style.selectedStep > 0 ? _Number(style.selectedStep) : 0);
  367. var hoveredBackSize = hoveredSize + (style.backStep > 0 ? _Number(style.backStep) : 0);
  368. var selectedBackSize = selectedSize + (style.backStep > 0 ? _Number(style.backStep) : 0);
  369. var color = style.color || null;
  370. var borderColor = style.borderColor || null;
  371. var borderWidth = pick(style.borderWidth, null);
  372. var opacity = pick(style.opacity, null);
  373. var backColor = style.backColor || null;
  374. var backOpacity = pick(style.backOpacity, null);
  375. styles.dot = [{
  376. r: size / 2,
  377. stroke: borderColor,
  378. "stroke-width": borderWidth,
  379. fill: color,
  380. opacity: opacity
  381. }, {
  382. r: hoveredSize / 2,
  383. stroke: style.hoveredBorderColor || borderColor,
  384. "stroke-width": pick(style.hoveredBorderWidth, borderWidth),
  385. fill: style.hoveredColor || color,
  386. opacity: pick(style.hoveredOpacity, opacity)
  387. }, {
  388. r: selectedSize / 2,
  389. stroke: style.selectedBorderColor || borderColor,
  390. "stroke-width": pick(style.selectedBorderWidth, borderWidth),
  391. fill: style.selectedColor || color,
  392. opacity: pick(style.selectedOpacity, opacity)
  393. }];
  394. styles.back = [{
  395. r: size / 2,
  396. stroke: "none",
  397. "stroke-width": 0,
  398. fill: backColor,
  399. opacity: backOpacity
  400. }, {
  401. r: hoveredBackSize / 2,
  402. stroke: "none",
  403. "stroke-width": 0,
  404. fill: backColor,
  405. opacity: backOpacity
  406. }, {
  407. r: selectedBackSize / 2,
  408. stroke: "none",
  409. "stroke-width": 0,
  410. fill: backColor,
  411. opacity: backOpacity
  412. }]
  413. },
  414. _setState: function(figure, styles, state) {
  415. applyElementState(figure, styles, state, "dot");
  416. applyElementState(figure, styles, state, "back")
  417. }
  418. },
  419. bubble: {
  420. _draw: function(ctx, figure, data) {
  421. figure.bubble = ctx.renderer.circle().sharp().data(ctx.dataKey, data).append(figure.root)
  422. },
  423. refresh: function(ctx, figure, data, proxy, settings) {
  424. figure.bubble.attr({
  425. r: settings.size / 2
  426. })
  427. },
  428. _getStyles: function(styles, style) {
  429. var color = style.color || null;
  430. var borderColor = style.borderColor || null;
  431. var borderWidth = pick(style.borderWidth, null);
  432. var opacity = pick(style.opacity, null);
  433. styles.bubble = [{
  434. stroke: borderColor,
  435. "stroke-width": borderWidth,
  436. fill: color,
  437. opacity: opacity
  438. }, {
  439. stroke: style.hoveredBorderColor || borderColor,
  440. "stroke-width": pick(style.hoveredBorderWidth, borderWidth),
  441. fill: style.hoveredColor || style.color,
  442. opacity: pick(style.hoveredOpacity, opacity)
  443. }, {
  444. stroke: style.selectedBorderColor || borderColor,
  445. "stroke-width": pick(style.selectedBorderWidth, borderWidth),
  446. fill: style.selectedColor || style.color,
  447. opacity: pick(style.selectedOpacity, opacity)
  448. }]
  449. },
  450. _setState: function(figure, styles, state) {
  451. applyElementState(figure, styles, state, "bubble")
  452. },
  453. arrange: function(context, handles) {
  454. var values = [];
  455. var i;
  456. var ii = values.length = handles.length;
  457. var settings = context.settings;
  458. var dataField = settings.dataField;
  459. var minSize = settings.minSize > 0 ? _Number(settings.minSize) : 0;
  460. var maxSize = settings.maxSize > minSize ? _Number(settings.maxSize) : minSize;
  461. if (settings.sizeGroups) {
  462. return
  463. }
  464. for (i = 0; i < ii; ++i) {
  465. values[i] = _max(getDataValue(handles[i].proxy, dataField) || 0, 0)
  466. }
  467. var minValue = _min.apply(null, values);
  468. var maxValue = _max.apply(null, values);
  469. var deltaValue = maxValue - minValue || 1;
  470. var deltaSize = maxSize - minSize;
  471. for (i = 0; i < ii; ++i) {
  472. handles[i]._settings.size = minSize + deltaSize * (values[i] - minValue) / deltaValue
  473. }
  474. },
  475. updateGrouping: function(context) {
  476. var dataField = context.settings.dataField;
  477. strategiesByType[TYPE_MARKER].updateGrouping(context);
  478. groupBySize(context, function(proxy) {
  479. return getDataValue(proxy, dataField)
  480. })
  481. }
  482. },
  483. pie: {
  484. _draw: function(ctx, figure, data) {
  485. figure.pie = ctx.renderer.g().append(figure.root);
  486. figure.border = ctx.renderer.circle().sharp().data(ctx.dataKey, data).append(figure.root)
  487. },
  488. refresh: function(ctx, figure, data, proxy, settings) {
  489. var values = getDataValue(proxy, ctx.settings.dataField) || [];
  490. var colors = settings._colors;
  491. var sum = 0;
  492. var pie = figure.pie;
  493. var renderer = ctx.renderer;
  494. var dataKey = ctx.dataKey;
  495. var r = (settings.size > 0 ? _Number(settings.size) : 0) / 2;
  496. var start = 90;
  497. var end = start;
  498. var zeroSum = false;
  499. sum = values.reduce(function(total, item) {
  500. return total + (item || 0)
  501. }, 0);
  502. if (0 === sum) {
  503. zeroSum = true;
  504. sum = 360 / values.length
  505. }
  506. values.forEach(function(item, i) {
  507. start = end;
  508. end += zeroSum ? sum : (item || 0) / sum * 360;
  509. renderer.arc(0, 0, 0, r, start, end).attr({
  510. "stroke-linejoin": "round",
  511. fill: colors[i]
  512. }).data(dataKey, data).append(pie)
  513. });
  514. figure.border.attr({
  515. r: r
  516. })
  517. },
  518. _getStyles: function(styles, style) {
  519. var opacity = pick(style.opacity, null);
  520. var borderColor = style.borderColor || null;
  521. var borderWidth = pick(style.borderWidth, null);
  522. styles.pie = [{
  523. opacity: opacity
  524. }, {
  525. opacity: pick(style.hoveredOpacity, opacity)
  526. }, {
  527. opacity: pick(style.selectedOpacity, opacity)
  528. }];
  529. styles.border = [{
  530. stroke: borderColor,
  531. "stroke-width": borderWidth
  532. }, {
  533. stroke: style.hoveredBorderColor || borderColor,
  534. "stroke-width": pick(style.hoveredBorderWidth, borderWidth)
  535. }, {
  536. stroke: style.selectedBorderColor || borderColor,
  537. "stroke-width": pick(style.selectedBorderWidth, borderWidth)
  538. }]
  539. },
  540. _setState: function(figure, styles, state) {
  541. applyElementState(figure, styles, state, "pie");
  542. applyElementState(figure, styles, state, "border")
  543. },
  544. arrange: function(context, handles) {
  545. var i;
  546. var ii = handles.length;
  547. var dataField = context.settings.dataField;
  548. var values;
  549. var count = 0;
  550. for (i = 0; i < ii; ++i) {
  551. values = getDataValue(handles[i].proxy, dataField);
  552. if (values && values.length > count) {
  553. count = values.length
  554. }
  555. }
  556. if (count > 0) {
  557. var palette = context.params.themeManager.createPalette(context.settings.palette, {
  558. useHighlight: true,
  559. extensionMode: "alternate"
  560. });
  561. values = palette.generateColors(count);
  562. context.settings._colors = values;
  563. context.grouping.color = {
  564. callback: _noop,
  565. field: "",
  566. partition: [],
  567. values: []
  568. };
  569. context.params.dataExchanger.set(context.name, "color", {
  570. partition: [],
  571. values: values
  572. })
  573. }
  574. }
  575. },
  576. image: {
  577. _draw: function(ctx, figure, data) {
  578. figure.image = ctx.renderer.image(null, null, null, null, null, "center").attr({
  579. "pointer-events": "visible"
  580. }).data(ctx.dataKey, data).append(figure.root)
  581. },
  582. refresh: function(ctx, figure, data, proxy) {
  583. figure.image.attr({
  584. href: getDataValue(proxy, ctx.settings.dataField)
  585. })
  586. },
  587. _getStyles: function(styles, style) {
  588. var size = style.size > 0 ? _Number(style.size) : 0;
  589. var hoveredSize = size + (style.hoveredStep > 0 ? _Number(style.hoveredStep) : 0);
  590. var selectedSize = size + (style.selectedStep > 0 ? _Number(style.selectedStep) : 0);
  591. var opacity = pick(style.opacity, null);
  592. styles.image = [{
  593. x: -size / 2,
  594. y: -size / 2,
  595. width: size,
  596. height: size,
  597. opacity: opacity
  598. }, {
  599. x: -hoveredSize / 2,
  600. y: -hoveredSize / 2,
  601. width: hoveredSize,
  602. height: hoveredSize,
  603. opacity: pick(style.hoveredOpacity, opacity)
  604. }, {
  605. x: -selectedSize / 2,
  606. y: -selectedSize / 2,
  607. width: selectedSize,
  608. height: selectedSize,
  609. opacity: pick(style.selectedOpacity, opacity)
  610. }]
  611. },
  612. _setState: function(figure, styles, state) {
  613. applyElementState(figure, styles, state, "image")
  614. }
  615. }
  616. };
  617. function projectPoint(projection, coordinates) {
  618. return projection.project(coordinates)
  619. }
  620. function projectPointList(projection, coordinates) {
  621. var output = [];
  622. var i;
  623. var ii = output.length = coordinates.length;
  624. for (i = 0; i < ii; ++i) {
  625. output[i] = projection.project(coordinates[i])
  626. }
  627. return output
  628. }
  629. function projectLineString(projection, coordinates) {
  630. return [projectPointList(projection, coordinates)]
  631. }
  632. function projectPolygon(projection, coordinates) {
  633. var output = [];
  634. var i;
  635. var ii = output.length = coordinates.length;
  636. for (i = 0; i < ii; ++i) {
  637. output[i] = projectPointList(projection, coordinates[i])
  638. }
  639. return output
  640. }
  641. function projectMultiPolygon(projection, coordinates) {
  642. var output = [];
  643. var i;
  644. var ii = output.length = coordinates.length;
  645. for (i = 0; i < ii; ++i) {
  646. output[i] = projectPolygon(projection, coordinates[i])
  647. }
  648. return _concat.apply([], output)
  649. }
  650. function transformPoint(content, projection, coordinates) {
  651. var data = projection.transform(coordinates);
  652. content.root.attr({
  653. translateX: data[0],
  654. translateY: data[1]
  655. })
  656. }
  657. function transformList(projection, coordinates) {
  658. var output = [];
  659. var i;
  660. var ii = coordinates.length;
  661. var k = 0;
  662. output.length = 2 * ii;
  663. for (i = 0; i < ii; ++i) {
  664. var item = projection.transform(coordinates[i]);
  665. output[k++] = item[0];
  666. output[k++] = item[1]
  667. }
  668. return output
  669. }
  670. function transformPointList(content, projection, coordinates) {
  671. var output = [];
  672. var i;
  673. var ii = output.length = coordinates.length;
  674. for (i = 0; i < ii; ++i) {
  675. output[i] = transformList(projection, coordinates[i])
  676. }
  677. content.root.attr({
  678. points: output
  679. })
  680. }
  681. function transformAreaLabel(label, projection, coordinates) {
  682. var data = projection.transform(coordinates[0]);
  683. label.spaceSize = projection.getSquareSize(coordinates[1]);
  684. label.text.attr({
  685. translateX: data[0],
  686. translateY: data[1]
  687. });
  688. setAreaLabelVisibility(label)
  689. }
  690. function transformLineLabel(label, projection, coordinates) {
  691. var data = projection.transform(coordinates[0]);
  692. label.spaceSize = projection.getSquareSize(coordinates[1]);
  693. label.text.attr({
  694. translateX: data[0],
  695. translateY: data[1]
  696. });
  697. setLineLabelVisibility(label)
  698. }
  699. function getItemSettings(context, proxy, settings) {
  700. var result = combineSettings(context.settings, settings);
  701. applyGrouping(context.grouping, proxy, result);
  702. if (void 0 === settings.color && settings.paletteIndex >= 0) {
  703. result.color = result._colors[settings.paletteIndex]
  704. }
  705. return result
  706. }
  707. function applyGrouping(grouping, proxy, settings) {
  708. _each(grouping, function(name, data) {
  709. var index = findGroupingIndex(data.callback(proxy, data.field), data.partition);
  710. if (index >= 0) {
  711. settings[name] = data.values[index]
  712. }
  713. })
  714. }
  715. function findGroupingIndex(value, partition) {
  716. var start = 0;
  717. var end = partition.length - 1;
  718. var index = -1;
  719. if (partition[start] <= value && value <= partition[end]) {
  720. if (value === partition[end]) {
  721. index = end - 1
  722. } else {
  723. while (end - start > 1) {
  724. var middle = start + end >> 1;
  725. if (value < partition[middle]) {
  726. end = middle
  727. } else {
  728. start = middle
  729. }
  730. }
  731. index = start
  732. }
  733. }
  734. return index
  735. }
  736. function raiseChanged(context, handle, state, name) {
  737. context.params.eventTrigger(name, {
  738. target: handle.proxy,
  739. state: state
  740. })
  741. }
  742. function combineSettings(common, partial) {
  743. var obj = _extend({}, common, partial);
  744. obj.label = _extend({}, common.label, obj.label);
  745. obj.label.font = _extend({}, common.label.font, obj.label.font);
  746. return obj
  747. }
  748. function processCommonSettings(context, options) {
  749. var themeManager = context.params.themeManager;
  750. var strategy = context.str;
  751. var settings = combineSettings(_extend({
  752. label: {},
  753. color: strategy.getDefaultColor(context, options.palette)
  754. }, themeManager.theme("layer:" + strategy.fullType)), options);
  755. var colors;
  756. var i;
  757. if (settings.paletteSize > 0) {
  758. var palette = themeManager.createDiscretePalette(settings.palette, settings.paletteSize);
  759. for (i = 0, colors = []; i < settings.paletteSize; ++i) {
  760. colors.push(palette.getColor(i))
  761. }
  762. settings._colors = colors
  763. }
  764. return settings
  765. }
  766. function valueCallback(proxy, dataField) {
  767. return proxy.attribute(dataField)
  768. }
  769. var performGrouping = function(context, partition, settingField, dataField, valuesCallback) {
  770. if (dataField && partition && partition.length > 1) {
  771. var values = valuesCallback(partition.length - 1);
  772. context.grouping[settingField] = {
  773. callback: _isFunction(dataField) ? dataField : valueCallback,
  774. field: dataField,
  775. partition: partition,
  776. values: values
  777. };
  778. context.params.dataExchanger.set(context.name, settingField, {
  779. partition: partition,
  780. values: values,
  781. defaultColor: context.settings.color
  782. })
  783. }
  784. };
  785. function dropGrouping(context) {
  786. var name = context.name;
  787. var dataExchanger = context.params.dataExchanger;
  788. _each(context.grouping, function(field) {
  789. dataExchanger.set(name, field, null)
  790. });
  791. context.grouping = {}
  792. }
  793. var groupByColor = function(context) {
  794. performGrouping(context, context.settings.colorGroups, "color", context.settings.colorGroupingField, function(count) {
  795. var _palette = context.params.themeManager.createDiscretePalette(context.settings.palette, count);
  796. var list = [];
  797. for (var i = 0; i < count; ++i) {
  798. list.push(_palette.getColor(i))
  799. }
  800. return list
  801. })
  802. };
  803. var groupBySize = function(context, valueCallback) {
  804. var settings = context.settings;
  805. performGrouping(context, settings.sizeGroups, "size", valueCallback || settings.sizeGroupingField, function(count) {
  806. var minSize = settings.minSize > 0 ? _Number(settings.minSize) : 0;
  807. var maxSize = settings.maxSize >= minSize ? _Number(settings.maxSize) : 0;
  808. var i = 0;
  809. var sizes = [];
  810. if (count > 1) {
  811. for (i = 0; i < count; ++i) {
  812. sizes.push((minSize * (count - i - 1) + maxSize * i) / (count - 1))
  813. }
  814. } else {
  815. if (1 === count) {
  816. sizes.push((minSize + maxSize) / 2)
  817. }
  818. }
  819. return sizes
  820. })
  821. };
  822. function setFlag(flags, flag, state) {
  823. if (state) {
  824. flags |= flag
  825. } else {
  826. flags &= ~flag
  827. }
  828. return flags
  829. }
  830. function hasFlag(flags, flag) {
  831. return !!(flags & flag)
  832. }
  833. function createLayerProxy(layer, name, index) {
  834. var proxy = {
  835. index: index,
  836. name: name,
  837. getElements: function() {
  838. return layer.getProxies()
  839. },
  840. clearSelection: function(_noEvent) {
  841. layer.clearSelection(_noEvent);
  842. return proxy
  843. },
  844. getDataSource: function() {
  845. return layer.getDataSource()
  846. }
  847. };
  848. return proxy
  849. }
  850. var MapLayer = function(params, container, name, index) {
  851. var that = this;
  852. that._params = params;
  853. that._onProjection();
  854. that.proxy = createLayerProxy(that, name, index);
  855. that._context = {
  856. name: name,
  857. layer: that.proxy,
  858. renderer: params.renderer,
  859. projection: params.projection,
  860. params: params,
  861. dataKey: params.dataKey,
  862. str: emptyStrategy,
  863. hover: false,
  864. selection: null,
  865. grouping: {},
  866. root: params.renderer.g().attr({
  867. "class": "dxm-layer"
  868. }).linkOn(container, name).linkAppend()
  869. };
  870. that._container = container;
  871. that._options = {};
  872. that._handles = [];
  873. that._data = new EmptySource
  874. };
  875. MapLayer.prototype = _extend({
  876. constructor: MapLayer,
  877. _onProjection: function() {
  878. var that = this;
  879. that._removeHandlers = that._params.projection.on({
  880. engine: function() {
  881. that._project()
  882. },
  883. screen: function() {
  884. that._transform()
  885. },
  886. center: function() {
  887. that._transformCore()
  888. },
  889. zoom: function() {
  890. that._transform()
  891. }
  892. })
  893. },
  894. _dataSourceLoadErrorHandler: function() {
  895. this._dataSourceChangedHandler()
  896. },
  897. _dataSourceChangedHandler: function() {
  898. var that = this;
  899. that._data = unwrapFromDataSource(that._dataSource && that._dataSource.items());
  900. that._update(true)
  901. },
  902. _dataSourceOptions: function() {
  903. return {
  904. paginate: false
  905. }
  906. },
  907. _getSpecificDataSourceOption: function() {
  908. return this._specificDataSourceOption
  909. },
  910. _offProjection: function() {
  911. this._removeHandlers();
  912. this._removeHandlers = null
  913. },
  914. dispose: function() {
  915. var that = this;
  916. that._disposeDataSource();
  917. that._destroyHandles();
  918. dropGrouping(that._context);
  919. that._context.root.linkRemove().linkOff();
  920. that._context.labelRoot && that._context.labelRoot.linkRemove().linkOff();
  921. that._context.str.reset(that._context);
  922. that._offProjection();
  923. that._params = that._container = that._context = that.proxy = null;
  924. return that
  925. },
  926. setOptions: function(options) {
  927. var that = this;
  928. options = that._options = options || {};
  929. if ("dataSource" in options && options.dataSource !== that._options_dataSource) {
  930. that._options_dataSource = options.dataSource;
  931. that._params.notifyDirty();
  932. that._specificDataSourceOption = wrapToDataSource(options.dataSource);
  933. that._refreshDataSource()
  934. } else {
  935. if (that._data.count() > 0) {
  936. that._params.notifyDirty();
  937. that._update(void 0 !== options.type && options.type !== that._context.str.type || void 0 !== options.elementType && options.elementType !== that._context.str.elementType)
  938. }
  939. }
  940. that._transformCore()
  941. },
  942. _update: function(isContextChanged) {
  943. var that = this;
  944. var context = that._context;
  945. if (isContextChanged) {
  946. context.str.reset(context);
  947. context.root.clear();
  948. context.labelRoot && context.labelRoot.clear();
  949. that._params.tracker.reset();
  950. that._destroyHandles();
  951. context.str = selectStrategy(that._options, that._data);
  952. context.str.setup(context);
  953. that.proxy.type = context.str.type;
  954. that.proxy.elementType = context.str.elementType
  955. }
  956. context.settings = processCommonSettings(context, that._options);
  957. context.hasSeparateLabel = !!(context.settings.label.enabled && context.str.hasLabelsGroup);
  958. context.hover = !!_parseScalar(context.settings.hoverEnabled, true);
  959. if (context.selection) {
  960. _each(context.selection.state, function(_, handle) {
  961. handle && handle.resetSelected()
  962. })
  963. }
  964. context.selection = getSelection(context.settings.selectionMode);
  965. if (context.hasSeparateLabel) {
  966. if (!context.labelRoot) {
  967. context.labelRoot = context.renderer.g().attr({
  968. "class": "dxm-layer-labels"
  969. }).linkOn(that._container, {
  970. name: context.name + "-labels",
  971. after: context.name
  972. }).linkAppend();
  973. that._transformCore()
  974. }
  975. } else {
  976. if (context.labelRoot) {
  977. context.labelRoot.linkRemove().linkOff();
  978. context.labelRoot = null
  979. }
  980. }
  981. if (isContextChanged) {
  982. that._createHandles()
  983. }
  984. dropGrouping(context);
  985. context.str.arrange(context, that._handles);
  986. context.str.updateGrouping(context);
  987. that._updateHandles();
  988. that._params.notifyReady()
  989. },
  990. _destroyHandles: function() {
  991. var handles = this._handles;
  992. var i;
  993. var ii = handles.length;
  994. for (i = 0; i < ii; ++i) {
  995. handles[i].dispose()
  996. }
  997. if (this._context.selection) {
  998. this._context.selection.state = {}
  999. }
  1000. this._handles = []
  1001. },
  1002. _createHandles: function() {
  1003. var that = this;
  1004. var handles = that._handles = [];
  1005. var data = that._data;
  1006. var i;
  1007. var ii = handles.length = data.count();
  1008. var context = that._context;
  1009. var geometry = data.geometry;
  1010. var attributes = data.attributes;
  1011. var handle;
  1012. for (i = 0; i < ii; ++i) {
  1013. var dataItem = data.item(i);
  1014. handles[i] = new MapLayerElement(context, i, geometry(dataItem), attributes(dataItem))
  1015. }
  1016. _isFunction(that._options.customize) && customizeHandles(that.getProxies(), that._options.customize, that._params.widget);
  1017. for (i = 0; i < ii; ++i) {
  1018. handle = handles[i];
  1019. handle.project();
  1020. handle.draw();
  1021. handle.transform()
  1022. }
  1023. if (context.selection) {
  1024. _each(context.selection.state, function(_, handle) {
  1025. handle && handle.restoreSelected()
  1026. })
  1027. }
  1028. },
  1029. _updateHandles: function() {
  1030. var handles = this._handles;
  1031. var i;
  1032. var ii = handles.length;
  1033. for (i = 0; i < ii; ++i) {
  1034. handles[i].refresh()
  1035. }
  1036. if (this._context.settings.label.enabled) {
  1037. for (i = 0; i < ii; ++i) {
  1038. handles[i].measureLabel()
  1039. }
  1040. for (i = 0; i < ii; ++i) {
  1041. handles[i].adjustLabel()
  1042. }
  1043. }
  1044. },
  1045. _transformCore: function() {
  1046. var transform = this._params.projection.getTransform();
  1047. this._context.root.attr(transform);
  1048. this._context.labelRoot && this._context.labelRoot.attr(transform)
  1049. },
  1050. _project: function() {
  1051. var handles = this._handles;
  1052. var i;
  1053. var ii = handles.length;
  1054. for (i = 0; i < ii; ++i) {
  1055. handles[i].project()
  1056. }
  1057. },
  1058. _transform: function() {
  1059. var handles = this._handles;
  1060. var i;
  1061. var ii = handles.length;
  1062. this._transformCore();
  1063. for (i = 0; i < ii; ++i) {
  1064. handles[i].transform()
  1065. }
  1066. },
  1067. getProxies: function() {
  1068. var handles = this._handles;
  1069. var proxies = [];
  1070. var i;
  1071. var ii = proxies.length = handles.length;
  1072. for (i = 0; i < ii; ++i) {
  1073. proxies[i] = handles[i].proxy
  1074. }
  1075. return proxies
  1076. },
  1077. getProxy: function(index) {
  1078. return this._handles[index].proxy
  1079. },
  1080. raiseClick: function(i, dxEvent) {
  1081. this._params.eventTrigger("click", {
  1082. target: this._handles[i].proxy,
  1083. event: dxEvent
  1084. })
  1085. },
  1086. hoverItem: function(i, state) {
  1087. this._handles[i].setHovered(state)
  1088. },
  1089. selectItem: function(i, state, _noEvent) {
  1090. this._handles[i].setSelected(state, _noEvent)
  1091. },
  1092. clearSelection: function() {
  1093. var selection = this._context.selection;
  1094. if (selection) {
  1095. _each(selection.state, function(_, handle) {
  1096. handle && handle.setSelected(false)
  1097. });
  1098. selection.state = {}
  1099. }
  1100. }
  1101. }, DataHelperMixin);
  1102. function createProxy(handle, coords, attrs) {
  1103. var proxy = {
  1104. coordinates: function() {
  1105. return coords
  1106. },
  1107. attribute: function(name, value) {
  1108. if (arguments.length > 1) {
  1109. attrs[name] = value;
  1110. return proxy
  1111. } else {
  1112. return arguments.length > 0 ? attrs[name] : attrs
  1113. }
  1114. },
  1115. selected: function(state, _noEvent) {
  1116. if (arguments.length > 0) {
  1117. handle.setSelected(state, _noEvent);
  1118. return proxy
  1119. } else {
  1120. return handle.isSelected()
  1121. }
  1122. },
  1123. applySettings: function(settings) {
  1124. handle.update(settings);
  1125. return proxy
  1126. }
  1127. };
  1128. return proxy
  1129. }
  1130. var MapLayerElement = function(context, index, geometry, attributes) {
  1131. var that = this;
  1132. var proxy = that.proxy = createProxy(that, geometry.coordinates, _extend({}, attributes));
  1133. that._ctx = context;
  1134. that._index = index;
  1135. that._fig = that._label = null;
  1136. that._state = STATE_DEFAULT;
  1137. that._coordinates = geometry.coordinates;
  1138. that._settings = {
  1139. label: {}
  1140. };
  1141. proxy.index = index;
  1142. proxy.layer = context.layer;
  1143. that._data = {
  1144. name: context.name,
  1145. index: index
  1146. }
  1147. };
  1148. MapLayerElement.prototype = {
  1149. constructor: MapLayerElement,
  1150. dispose: function() {
  1151. var that = this;
  1152. that._ctx = that.proxy = that._settings = that._fig = that._label = that.data = null;
  1153. return that
  1154. },
  1155. project: function() {
  1156. var context = this._ctx;
  1157. this._projection = context.str.project(context.projection, this._coordinates);
  1158. if (context.hasSeparateLabel && this._label) {
  1159. this._projectLabel()
  1160. }
  1161. },
  1162. _projectLabel: function() {
  1163. this._labelProjection = this._ctx.str.projectLabel(this._projection)
  1164. },
  1165. draw: function() {
  1166. var that = this;
  1167. var context = this._ctx;
  1168. context.str.draw(context, that._fig = {}, that._data);
  1169. that._fig.root.append(context.root)
  1170. },
  1171. transform: function() {
  1172. var that = this;
  1173. var context = that._ctx;
  1174. context.str.transform(that._fig, context.projection, that._projection);
  1175. if (context.hasSeparateLabel && that._label) {
  1176. that._transformLabel()
  1177. }
  1178. },
  1179. _transformLabel: function() {
  1180. this._ctx.str.transformLabel(this._label, this._ctx.projection, this._labelProjection)
  1181. },
  1182. refresh: function() {
  1183. var that = this;
  1184. var strategy = that._ctx.str;
  1185. var settings = getItemSettings(that._ctx, that.proxy, that._settings);
  1186. that._styles = strategy.getStyles(settings);
  1187. strategy.refresh(that._ctx, that._fig, that._data, that.proxy, settings);
  1188. that._refreshLabel(settings);
  1189. that._setState()
  1190. },
  1191. _refreshLabel: function(settings) {
  1192. var that = this;
  1193. var context = that._ctx;
  1194. var labelSettings = settings.label;
  1195. var label = that._label;
  1196. if (context.settings.label.enabled) {
  1197. if (!label) {
  1198. label = that._label = {
  1199. root: context.labelRoot || that._fig.root,
  1200. text: context.renderer.text().attr({
  1201. "class": "dxm-label"
  1202. }),
  1203. size: [0, 0]
  1204. };
  1205. if (context.hasSeparateLabel) {
  1206. that._projectLabel();
  1207. that._transformLabel()
  1208. }
  1209. }
  1210. label.value = _String(that.proxy.text || that.proxy.attribute(labelSettings.dataField) || "");
  1211. if (label.value) {
  1212. label.text.attr({
  1213. text: label.value,
  1214. x: 0,
  1215. y: 0
  1216. }).css(_patchFontOptions(labelSettings.font)).attr({
  1217. align: "center",
  1218. stroke: labelSettings.stroke,
  1219. "stroke-width": labelSettings["stroke-width"],
  1220. "stroke-opacity": labelSettings["stroke-opacity"]
  1221. }).data(context.dataKey, that._data).append(label.root);
  1222. label.settings = settings
  1223. }
  1224. } else {
  1225. if (label) {
  1226. label.text.remove();
  1227. that._label = null
  1228. }
  1229. }
  1230. },
  1231. measureLabel: function() {
  1232. var label = this._label;
  1233. if (label.value) {
  1234. var bBox = label.text.getBBox();
  1235. label.size = [bBox.width, bBox.height, -bBox.y - bBox.height / 2]
  1236. }
  1237. },
  1238. adjustLabel: function() {
  1239. var label = this._label;
  1240. if (label.value) {
  1241. var offset = this._ctx.str.getLabelOffset(label, label.settings);
  1242. label.settings = null;
  1243. label.text.attr({
  1244. x: offset[0],
  1245. y: offset[1] + label.size[2]
  1246. })
  1247. }
  1248. },
  1249. update: function(settings) {
  1250. var that = this;
  1251. that._settings = combineSettings(that._settings, settings);
  1252. if (that._fig) {
  1253. that.refresh();
  1254. if (that._label && that._label.value) {
  1255. that.measureLabel();
  1256. that.adjustLabel()
  1257. }
  1258. }
  1259. },
  1260. _setState: function() {
  1261. this._ctx.str.setState(this._fig, this._styles, STATE_TO_INDEX[this._state])
  1262. },
  1263. _setForeground: function() {
  1264. var root = this._fig.root;
  1265. this._state ? root.toForeground() : root.toBackground()
  1266. },
  1267. setHovered: function(state) {
  1268. var that = this;
  1269. var currentState = hasFlag(that._state, STATE_HOVERED);
  1270. var newState = !!state;
  1271. if (that._ctx.hover && currentState !== newState) {
  1272. that._state = setFlag(that._state, STATE_HOVERED, newState);
  1273. that._setState();
  1274. that._setForeground();
  1275. raiseChanged(that._ctx, that, newState, "hoverChanged")
  1276. }
  1277. return that
  1278. },
  1279. setSelected: function(state, _noEvent) {
  1280. var that = this;
  1281. var currentState = hasFlag(that._state, STATE_SELECTED);
  1282. var newState = !!state;
  1283. var selection = that._ctx.selection;
  1284. if (selection && currentState !== newState) {
  1285. that._state = setFlag(that._state, STATE_SELECTED, newState);
  1286. var tmp = selection.state[selection.single];
  1287. selection.state[selection.single] = null;
  1288. if (tmp) {
  1289. tmp.setSelected(false)
  1290. }
  1291. selection.state[selection.single || that._index] = state ? that : null;
  1292. if (that._fig) {
  1293. that._setState();
  1294. that._setForeground();
  1295. if (!_noEvent) {
  1296. raiseChanged(that._ctx, that, newState, "selectionChanged")
  1297. }
  1298. }
  1299. }
  1300. },
  1301. isSelected: function() {
  1302. return hasFlag(this._state, STATE_SELECTED)
  1303. },
  1304. resetSelected: function() {
  1305. this._state = setFlag(this._state, STATE_SELECTED, false)
  1306. },
  1307. restoreSelected: function() {
  1308. this._fig.root.toForeground()
  1309. }
  1310. };
  1311. function calculatePolygonCentroid(coordinates) {
  1312. var length = coordinates.length;
  1313. var v2 = coordinates[length - 1];
  1314. var cx = 0;
  1315. var cy = 0;
  1316. var area = 0;
  1317. var minX = 1 / 0;
  1318. var maxX = -(1 / 0);
  1319. var minY = 1 / 0;
  1320. var maxY = -(1 / 0);
  1321. for (var i = 0; i < length; ++i) {
  1322. var v1 = v2;
  1323. v2 = coordinates[i];
  1324. var cross = v1[0] * v2[1] - v2[0] * v1[1];
  1325. area += cross;
  1326. cx += (v1[0] + v2[0]) * cross;
  1327. cy += (v1[1] + v2[1]) * cross;
  1328. minX = _min(minX, v2[0]);
  1329. maxX = _max(maxX, v2[0]);
  1330. minY = _min(minY, v2[1]);
  1331. maxY = _max(maxY, v2[1])
  1332. }
  1333. return {
  1334. area: _abs(area) / 2,
  1335. center: [2 * cx / 3 / area - (minX + maxX) / 2, 2 * cy / 3 / area - (minY + maxY) / 2]
  1336. }
  1337. }
  1338. function calculateLineStringData(coordinates) {
  1339. var i;
  1340. var ii = coordinates.length;
  1341. var v1;
  1342. var v2 = coordinates[0] || [];
  1343. var totalLength = 0;
  1344. var items = [0];
  1345. var min0 = v2[0];
  1346. var max0 = v2[0];
  1347. var min1 = v2[1];
  1348. var max1 = v2[1];
  1349. for (i = 1; i < ii; ++i) {
  1350. v1 = v2;
  1351. v2 = coordinates[i];
  1352. totalLength += _sqrt((v1[0] - v2[0]) * (v1[0] - v2[0]) + (v1[1] - v2[1]) * (v1[1] - v2[1]));
  1353. items[i] = totalLength;
  1354. min0 = _min(min0, v2[0]);
  1355. max0 = _max(max0, v2[0]);
  1356. min1 = _min(min1, v2[1]);
  1357. max1 = _max(max1, v2[1])
  1358. }
  1359. i = findGroupingIndex(totalLength / 2, items);
  1360. v1 = coordinates[i];
  1361. v2 = coordinates[i + 1];
  1362. var t = (totalLength / 2 - items[i]) / (items[i + 1] - items[i]);
  1363. return ii ? [
  1364. [v1[0] * (1 - t) + v2[0] * t, v1[1] * (1 - t) + v2[1] * t],
  1365. [max0 - min0, max1 - min1], totalLength
  1366. ] : []
  1367. }
  1368. function projectAreaLabel(coordinates) {
  1369. var i;
  1370. var ii = coordinates.length;
  1371. var resultCentroid;
  1372. var maxArea = 0;
  1373. for (i = 0; i < ii; ++i) {
  1374. var centroid = calculatePolygonCentroid(coordinates[i]);
  1375. if (centroid.area > maxArea) {
  1376. maxArea = centroid.area;
  1377. resultCentroid = centroid
  1378. }
  1379. }
  1380. return resultCentroid ? [resultCentroid.center, [_sqrt(resultCentroid.area), _sqrt(resultCentroid.area)]] : [
  1381. [],
  1382. []
  1383. ]
  1384. }
  1385. function projectLineLabel(coordinates) {
  1386. var i;
  1387. var ii = coordinates.length;
  1388. var maxLength = 0;
  1389. var resultData;
  1390. for (i = 0; i < ii; ++i) {
  1391. var data = calculateLineStringData(coordinates[i]);
  1392. if (data[2] > maxLength) {
  1393. maxLength = data[2];
  1394. resultData = data
  1395. }
  1396. }
  1397. return resultData || [
  1398. [],
  1399. []
  1400. ]
  1401. }
  1402. function MapLayerCollection(params) {
  1403. var that = this;
  1404. var renderer = params.renderer;
  1405. that._params = params;
  1406. that._layers = [];
  1407. that._layerByName = {};
  1408. that._rect = [0, 0, 0, 0];
  1409. that._clip = renderer.clipRect();
  1410. that._background = renderer.rect().attr({
  1411. "class": "dxm-background"
  1412. }).data(params.dataKey, {
  1413. name: "background"
  1414. }).append(renderer.root);
  1415. that._container = renderer.g().attr({
  1416. "class": "dxm-layers",
  1417. "clip-path": that._clip.id
  1418. }).append(renderer.root).enableLinks();
  1419. that._subscribeToTracker(params.tracker, renderer, params.eventTrigger)
  1420. }
  1421. MapLayerCollection.prototype = {
  1422. constructor: MapLayerCollection,
  1423. dispose: function() {
  1424. var that = this;
  1425. that._clip.dispose();
  1426. that._layers.forEach(function(l) {
  1427. return l.dispose()
  1428. });
  1429. that._offTracker();
  1430. that._params = that._offTracker = that._layers = that._layerByName = that._clip = that._background = that._container = null
  1431. },
  1432. _subscribeToTracker: function(tracker, renderer, eventTrigger) {
  1433. var that = this;
  1434. that._offTracker = tracker.on({
  1435. click: function(arg) {
  1436. var offset = renderer.getRootOffset();
  1437. var layer = that.byName(arg.data.name);
  1438. arg.$event.x = arg.x - offset.left;
  1439. arg.$event.y = arg.y - offset.top;
  1440. if (layer) {
  1441. layer.raiseClick(arg.data.index, arg.$event)
  1442. } else {
  1443. if ("background" === arg.data.name) {
  1444. eventTrigger("click", {
  1445. event: arg.$event
  1446. })
  1447. }
  1448. }
  1449. },
  1450. "hover-on": function(arg) {
  1451. var layer = that.byName(arg.data.name);
  1452. if (layer) {
  1453. layer.hoverItem(arg.data.index, true)
  1454. }
  1455. },
  1456. "hover-off": function(arg) {
  1457. var layer = that.byName(arg.data.name);
  1458. if (layer) {
  1459. layer.hoverItem(arg.data.index, false)
  1460. }
  1461. }
  1462. })
  1463. },
  1464. setOptions: function(options) {
  1465. var that = this;
  1466. var optionList = options ? _isArray(options) ? options : [options] : [];
  1467. var layerByName = that._layerByName;
  1468. var layers = that._layers;
  1469. var needToCreateLayers = optionList.length !== layers.length || layers.some(function(l, i) {
  1470. var name = getName(optionList, i);
  1471. return _isDefined(name) && name !== l.proxy.name
  1472. });
  1473. if (needToCreateLayers) {
  1474. that._params.tracker.reset();
  1475. that._layers.forEach(function(l) {
  1476. return l.dispose()
  1477. });
  1478. that._layerByName = layerByName = {};
  1479. that._layers = layers = [];
  1480. for (var i = 0, ii = optionList.length; i < ii; ++i) {
  1481. var name = getName(optionList, i) || "map-layer-" + i;
  1482. var layer = layers[i] = new MapLayer(that._params, that._container, name, i);
  1483. layerByName[name] = layer
  1484. }
  1485. }
  1486. layers.forEach(function(l, i) {
  1487. l.setOptions(optionList[i])
  1488. })
  1489. },
  1490. _updateClip: function() {
  1491. var rect = this._rect;
  1492. var bw = this._borderWidth;
  1493. this._clip.attr({
  1494. x: rect[0] + bw,
  1495. y: rect[1] + bw,
  1496. width: _max(rect[2] - 2 * bw, 0),
  1497. height: _max(rect[3] - 2 * bw, 0)
  1498. })
  1499. },
  1500. setBackgroundOptions: function(options) {
  1501. this._background.attr({
  1502. stroke: options.borderColor,
  1503. "stroke-width": options.borderWidth,
  1504. fill: options.color
  1505. });
  1506. this._borderWidth = _max(options.borderWidth, 0);
  1507. this._updateClip()
  1508. },
  1509. setRect: function(rect) {
  1510. this._rect = rect;
  1511. this._background.attr({
  1512. x: rect[0],
  1513. y: rect[1],
  1514. width: rect[2],
  1515. height: rect[3]
  1516. });
  1517. this._updateClip()
  1518. },
  1519. byIndex: function(index) {
  1520. return this._layers[index]
  1521. },
  1522. byName: function(name) {
  1523. return this._layerByName[name]
  1524. },
  1525. items: function() {
  1526. return this._layers
  1527. }
  1528. };
  1529. exports.MapLayerCollection = MapLayerCollection;