市长热线演示版

heatmap.src.js 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  1. /**
  2. * @license Highcharts JS v4.0.1 (2014-04-24)
  3. *
  4. * (c) 2011-2014 Torstein Honsi
  5. *
  6. * License: www.highcharts.com/license
  7. */
  8. /*global HighchartsAdapter*/
  9. (function (Highcharts) {
  10. var UNDEFINED,
  11. Axis = Highcharts.Axis,
  12. Chart = Highcharts.Chart,
  13. Color = Highcharts.Color,
  14. Legend = Highcharts.Legend,
  15. LegendSymbolMixin = Highcharts.LegendSymbolMixin,
  16. Series = Highcharts.Series,
  17. defaultOptions = Highcharts.getOptions(),
  18. each = Highcharts.each,
  19. extend = Highcharts.extend,
  20. extendClass = Highcharts.extendClass,
  21. merge = Highcharts.merge,
  22. pick = Highcharts.pick,
  23. numberFormat = Highcharts.numberFormat,
  24. seriesTypes = Highcharts.seriesTypes,
  25. wrap = Highcharts.wrap,
  26. noop = function () {};
  27. /**
  28. * The ColorAxis object for inclusion in gradient legends
  29. */
  30. var ColorAxis = Highcharts.ColorAxis = function () {
  31. this.isColorAxis = true;
  32. this.init.apply(this, arguments);
  33. };
  34. extend(ColorAxis.prototype, Axis.prototype);
  35. extend(ColorAxis.prototype, {
  36. defaultColorAxisOptions: {
  37. lineWidth: 0,
  38. gridLineWidth: 1,
  39. tickPixelInterval: 72,
  40. startOnTick: true,
  41. endOnTick: true,
  42. offset: 0,
  43. marker: { // docs: use another name?
  44. animation: {
  45. duration: 50
  46. },
  47. color: 'gray',
  48. width: 0.01
  49. },
  50. labels: {
  51. overflow: 'justify'
  52. },
  53. minColor: '#EFEFFF',
  54. maxColor: '#003875',
  55. tickLength: 5
  56. },
  57. init: function (chart, userOptions) {
  58. var horiz = chart.options.legend.layout !== 'vertical',
  59. options;
  60. // Build the options
  61. options = merge(this.defaultColorAxisOptions, {
  62. side: horiz ? 2 : 1,
  63. reversed: !horiz
  64. }, userOptions, {
  65. isX: horiz,
  66. opposite: !horiz,
  67. showEmpty: false,
  68. title: null,
  69. isColor: true
  70. });
  71. Axis.prototype.init.call(this, chart, options);
  72. // Base init() pushes it to the xAxis array, now pop it again
  73. //chart[this.isXAxis ? 'xAxis' : 'yAxis'].pop();
  74. // Prepare data classes
  75. if (userOptions.dataClasses) {
  76. this.initDataClasses(userOptions);
  77. }
  78. this.initStops(userOptions);
  79. // Override original axis properties
  80. this.isXAxis = true;
  81. this.horiz = horiz;
  82. this.zoomEnabled = false;
  83. },
  84. /*
  85. * Return an intermediate color between two colors, according to pos where 0
  86. * is the from color and 1 is the to color
  87. */
  88. tweenColors: function (from, to, pos) {
  89. // Check for has alpha, because rgba colors perform worse due to lack of
  90. // support in WebKit.
  91. var hasAlpha = (to.rgba[3] !== 1 || from.rgba[3] !== 1);
  92. return (hasAlpha ? 'rgba(' : 'rgb(') +
  93. Math.round(to.rgba[0] + (from.rgba[0] - to.rgba[0]) * (1 - pos)) + ',' +
  94. Math.round(to.rgba[1] + (from.rgba[1] - to.rgba[1]) * (1 - pos)) + ',' +
  95. Math.round(to.rgba[2] + (from.rgba[2] - to.rgba[2]) * (1 - pos)) +
  96. (hasAlpha ? (',' + (to.rgba[3] + (from.rgba[3] - to.rgba[3]) * (1 - pos))) : '') + ')';
  97. },
  98. initDataClasses: function (userOptions) {
  99. var axis = this,
  100. chart = this.chart,
  101. dataClasses,
  102. colorCounter = 0,
  103. options = this.options;
  104. this.dataClasses = dataClasses = [];
  105. each(userOptions.dataClasses, function (dataClass, i) {
  106. var colors;
  107. dataClass = merge(dataClass);
  108. dataClasses.push(dataClass);
  109. if (!dataClass.color) {
  110. if (options.dataClassColor === 'category') {
  111. colors = chart.options.colors;
  112. dataClass.color = colors[colorCounter++];
  113. // loop back to zero
  114. if (colorCounter === colors.length) {
  115. colorCounter = 0;
  116. }
  117. } else {
  118. dataClass.color = axis.tweenColors(Color(options.minColor), Color(options.maxColor), i / (userOptions.dataClasses.length - 1));
  119. }
  120. }
  121. });
  122. },
  123. initStops: function (userOptions) {
  124. this.stops = userOptions.stops || [
  125. [0, this.options.minColor],
  126. [1, this.options.maxColor]
  127. ];
  128. each(this.stops, function (stop) {
  129. stop.color = Color(stop[1]);
  130. });
  131. },
  132. /**
  133. * Extend the setOptions method to process extreme colors and color
  134. * stops.
  135. */
  136. setOptions: function (userOptions) {
  137. Axis.prototype.setOptions.call(this, userOptions);
  138. this.options.crosshair = this.options.marker;
  139. this.coll = 'colorAxis';
  140. },
  141. setAxisSize: function () {
  142. var symbol = this.legendSymbol,
  143. chart = this.chart,
  144. x,
  145. y,
  146. width,
  147. height;
  148. if (symbol) {
  149. this.left = x = symbol.attr('x');
  150. this.top = y = symbol.attr('y');
  151. this.width = width = symbol.attr('width');
  152. this.height = height = symbol.attr('height');
  153. this.right = chart.chartWidth - x - width;
  154. this.bottom = chart.chartHeight - y - height;
  155. this.len = this.horiz ? width : height;
  156. this.pos = this.horiz ? x : y;
  157. }
  158. },
  159. /**
  160. * Translate from a value to a color
  161. */
  162. toColor: function (value, point) {
  163. var pos,
  164. stops = this.stops,
  165. from,
  166. to,
  167. color,
  168. dataClasses = this.dataClasses,
  169. dataClass,
  170. i;
  171. if (dataClasses) {
  172. i = dataClasses.length;
  173. while (i--) {
  174. dataClass = dataClasses[i];
  175. from = dataClass.from;
  176. to = dataClass.to;
  177. if ((from === UNDEFINED || value >= from) && (to === UNDEFINED || value <= to)) {
  178. color = dataClass.color;
  179. if (point) {
  180. point.dataClass = i;
  181. }
  182. break;
  183. }
  184. }
  185. } else {
  186. if (this.isLog) {
  187. value = this.val2lin(value);
  188. }
  189. pos = 1 - ((this.max - value) / (this.max - this.min));
  190. i = stops.length;
  191. while (i--) {
  192. if (pos > stops[i][0]) {
  193. break;
  194. }
  195. }
  196. from = stops[i] || stops[i + 1];
  197. to = stops[i + 1] || from;
  198. // The position within the gradient
  199. pos = 1 - (to[0] - pos) / ((to[0] - from[0]) || 1);
  200. color = this.tweenColors(
  201. from.color,
  202. to.color,
  203. pos
  204. );
  205. }
  206. return color;
  207. },
  208. getOffset: function () {
  209. var group = this.legendGroup;
  210. if (group) {
  211. Axis.prototype.getOffset.call(this);
  212. if (!this.axisGroup.parentGroup) {
  213. // Move the axis elements inside the legend group
  214. this.axisGroup.add(group);
  215. this.gridGroup.add(group);
  216. this.labelGroup.add(group);
  217. this.added = true;
  218. }
  219. }
  220. },
  221. /**
  222. * Create the color gradient
  223. */
  224. setLegendColor: function () {
  225. var grad,
  226. horiz = this.horiz,
  227. options = this.options;
  228. grad = horiz ? [0, 0, 1, 0] : [0, 0, 0, 1];
  229. this.legendColor = {
  230. linearGradient: { x1: grad[0], y1: grad[1], x2: grad[2], y2: grad[3] },
  231. stops: options.stops || [
  232. [0, options.minColor],
  233. [1, options.maxColor]
  234. ]
  235. };
  236. },
  237. /**
  238. * The color axis appears inside the legend and has its own legend symbol
  239. */
  240. drawLegendSymbol: function (legend, item) {
  241. var padding = legend.padding,
  242. legendOptions = legend.options,
  243. horiz = this.horiz,
  244. box,
  245. width = pick(legendOptions.symbolWidth, horiz ? 200 : 12),
  246. height = pick(legendOptions.symbolHeight, horiz ? 12 : 200),
  247. labelPadding = pick(legendOptions.labelPadding, horiz ? 10 : 30);
  248. this.setLegendColor();
  249. // Create the gradient
  250. item.legendSymbol = this.chart.renderer.rect(
  251. 0,
  252. legend.baseline - 11,
  253. width,
  254. height
  255. ).attr({
  256. zIndex: 1
  257. }).add(item.legendGroup);
  258. box = item.legendSymbol.getBBox();
  259. // Set how much space this legend item takes up
  260. this.legendItemWidth = width + padding + (horiz ? 0 : labelPadding);
  261. this.legendItemHeight = height + padding + (horiz ? labelPadding : 0);
  262. },
  263. /**
  264. * Fool the legend
  265. */
  266. setState: noop,
  267. visible: true,
  268. setVisible: noop,
  269. getSeriesExtremes: function () {
  270. var series;
  271. if (this.series.length) {
  272. series = this.series[0];
  273. this.dataMin = series.valueMin;
  274. this.dataMax = series.valueMax;
  275. }
  276. },
  277. drawCrosshair: function (e, point) {
  278. var newCross = !this.cross,
  279. plotX = point && point.plotX,
  280. plotY = point && point.plotY,
  281. crossPos,
  282. axisPos = this.pos,
  283. axisLen = this.len;
  284. if (point) {
  285. crossPos = this.toPixels(point.value);
  286. if (crossPos < axisPos) {
  287. crossPos = axisPos - 2;
  288. } else if (crossPos > axisPos + axisLen) {
  289. crossPos = axisPos + axisLen + 2;
  290. }
  291. point.plotX = crossPos;
  292. point.plotY = this.len - crossPos;
  293. Axis.prototype.drawCrosshair.call(this, e, point);
  294. point.plotX = plotX;
  295. point.plotY = plotY;
  296. if (!newCross && this.cross) {
  297. this.cross
  298. .attr({
  299. fill: this.crosshair.color
  300. })
  301. .add(this.labelGroup);
  302. }
  303. }
  304. },
  305. getPlotLinePath: function (a, b, c, d, pos) {
  306. if (pos) { // crosshairs only
  307. return this.horiz ?
  308. ['M', pos - 4, this.top - 6, 'L', pos + 4, this.top - 6, pos, this.top, 'Z'] :
  309. ['M', this.left, pos, 'L', this.left - 6, pos + 6, this.left - 6, pos - 6, 'Z'];
  310. } else {
  311. return Axis.prototype.getPlotLinePath.call(this, a, b, c, d);
  312. }
  313. },
  314. update: function (newOptions, redraw) {
  315. each(this.series, function (series) {
  316. series.isDirtyData = true; // Needed for Axis.update when choropleth colors change
  317. });
  318. Axis.prototype.update.call(this, newOptions, redraw);
  319. if (this.legendItem) {
  320. this.setLegendColor();
  321. this.chart.legend.colorizeItem(this, true);
  322. }
  323. },
  324. /**
  325. * Get the legend item symbols for data classes
  326. */
  327. getDataClassLegendSymbols: function () {
  328. var axis = this,
  329. chart = this.chart,
  330. legendItems = [],
  331. legendOptions = chart.options.legend,
  332. valueDecimals = legendOptions.valueDecimals,
  333. valueSuffix = legendOptions.valueSuffix || '',
  334. name;
  335. each(this.dataClasses, function (dataClass, i) {
  336. var vis = true,
  337. from = dataClass.from,
  338. to = dataClass.to;
  339. // Assemble the default name. This can be overridden by legend.options.labelFormatter
  340. name = '';
  341. if (from === UNDEFINED) {
  342. name = '< ';
  343. } else if (to === UNDEFINED) {
  344. name = '> ';
  345. }
  346. if (from !== UNDEFINED) {
  347. name += numberFormat(from, valueDecimals) + valueSuffix;
  348. }
  349. if (from !== UNDEFINED && to !== UNDEFINED) {
  350. name += ' - ';
  351. }
  352. if (to !== UNDEFINED) {
  353. name += numberFormat(to, valueDecimals) + valueSuffix;
  354. }
  355. // Add a mock object to the legend items
  356. legendItems.push(extend({
  357. chart: chart,
  358. name: name,
  359. options: {},
  360. drawLegendSymbol: LegendSymbolMixin.drawRectangle,
  361. visible: true,
  362. setState: noop,
  363. setVisible: function () {
  364. vis = this.visible = !vis;
  365. each(axis.series, function (series) {
  366. each(series.points, function (point) {
  367. if (point.dataClass === i) {
  368. point.setVisible(vis);
  369. }
  370. });
  371. });
  372. chart.legend.colorizeItem(this, vis);
  373. }
  374. }, dataClass));
  375. });
  376. return legendItems;
  377. },
  378. name: '' // Prevents 'undefined' in legend in IE8
  379. });
  380. /**
  381. * Extend the chart getAxes method to also get the color axis
  382. */
  383. wrap(Chart.prototype, 'getAxes', function (proceed) {
  384. var options = this.options,
  385. colorAxisOptions = options.colorAxis;
  386. proceed.call(this);
  387. this.colorAxis = [];
  388. if (colorAxisOptions) {
  389. proceed = new ColorAxis(this, colorAxisOptions); // Fake assignment for jsLint
  390. }
  391. });
  392. /**
  393. * Wrap the legend getAllItems method to add the color axis. This also removes the
  394. * axis' own series to prevent them from showing up individually.
  395. */
  396. wrap(Legend.prototype, 'getAllItems', function (proceed) {
  397. var allItems = [],
  398. colorAxis = this.chart.colorAxis[0];
  399. if (colorAxis) {
  400. // Data classes
  401. if (colorAxis.options.dataClasses) {
  402. allItems = allItems.concat(colorAxis.getDataClassLegendSymbols());
  403. // Gradient legend
  404. } else {
  405. // Add this axis on top
  406. allItems.push(colorAxis);
  407. }
  408. // Don't add the color axis' series
  409. each(colorAxis.series, function (series) {
  410. series.options.showInLegend = false;
  411. });
  412. }
  413. return allItems.concat(proceed.call(this));
  414. });/**
  415. * Mixin for maps and heatmaps
  416. */
  417. var colorSeriesMixin = {
  418. pointAttrToOptions: { // mapping between SVG attributes and the corresponding options
  419. stroke: 'borderColor',
  420. 'stroke-width': 'borderWidth',
  421. fill: 'color',
  422. dashstyle: 'dashStyle'
  423. },
  424. pointArrayMap: ['value'],
  425. axisTypes: ['xAxis', 'yAxis', 'colorAxis'],
  426. optionalAxis: 'colorAxis',
  427. trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'],
  428. getSymbol: noop,
  429. parallelArrays: ['x', 'y', 'value'],
  430. /**
  431. * In choropleth maps, the color is a result of the value, so this needs translation too
  432. */
  433. translateColors: function () {
  434. var series = this,
  435. nullColor = this.options.nullColor,
  436. colorAxis = this.colorAxis;
  437. each(this.data, function (point) {
  438. var value = point.value,
  439. color;
  440. color = value === null ? nullColor : colorAxis ? colorAxis.toColor(value, point) : (point.color) || series.color;
  441. if (color) {
  442. point.color = color;
  443. }
  444. });
  445. }
  446. };
  447. /**
  448. * Extend the default options with map options
  449. */
  450. defaultOptions.plotOptions.heatmap = merge(defaultOptions.plotOptions.scatter, {
  451. animation: false,
  452. borderWidth: 0,
  453. nullColor: '#F8F8F8',
  454. dataLabels: {
  455. format: '{point.value}',
  456. verticalAlign: 'middle',
  457. crop: false,
  458. overflow: false,
  459. style: {
  460. color: 'white',
  461. fontWeight: 'bold',
  462. textShadow: '0 0 5px black'
  463. }
  464. },
  465. marker: null,
  466. tooltip: {
  467. pointFormat: '{point.x}, {point.y}: {point.value}<br/>'
  468. },
  469. states: {
  470. normal: {
  471. animation: true
  472. },
  473. hover: {
  474. brightness: 0.2
  475. }
  476. }
  477. });
  478. // The Heatmap series type
  479. seriesTypes.heatmap = extendClass(seriesTypes.scatter, merge(colorSeriesMixin, {
  480. type: 'heatmap',
  481. pointArrayMap: ['y', 'value'],
  482. hasPointSpecificOptions: true,
  483. supportsDrilldown: true,
  484. getExtremesFromAll: true,
  485. init: function () {
  486. seriesTypes.scatter.prototype.init.apply(this, arguments);
  487. this.pointRange = this.options.colsize || 1;
  488. this.yAxis.axisPointRange = this.options.rowsize || 1; // general point range
  489. },
  490. translate: function () {
  491. var series = this,
  492. options = series.options,
  493. xAxis = series.xAxis,
  494. yAxis = series.yAxis;
  495. series.generatePoints();
  496. each(series.points, function (point) {
  497. var xPad = (options.colsize || 1) / 2,
  498. yPad = (options.rowsize || 1) / 2,
  499. x1 = Math.round(xAxis.len - xAxis.translate(point.x - xPad, 0, 1, 0, 1)),
  500. x2 = Math.round(xAxis.len - xAxis.translate(point.x + xPad, 0, 1, 0, 1)),
  501. y1 = Math.round(yAxis.translate(point.y - yPad, 0, 1, 0, 1)),
  502. y2 = Math.round(yAxis.translate(point.y + yPad, 0, 1, 0, 1));
  503. // Set plotX and plotY for use in K-D-Tree and more
  504. point.plotX = (x1 + x2) / 2;
  505. point.plotY = (y1 + y2) / 2;
  506. point.shapeType = 'rect';
  507. point.shapeArgs = {
  508. x: Math.min(x1, x2),
  509. y: Math.min(y1, y2),
  510. width: Math.abs(x2 - x1),
  511. height: Math.abs(y2 - y1)
  512. };
  513. });
  514. series.translateColors();
  515. },
  516. drawPoints: seriesTypes.column.prototype.drawPoints,
  517. animate: noop,
  518. getBox: noop,
  519. drawLegendSymbol: LegendSymbolMixin.drawRectangle,
  520. getExtremes: function () {
  521. // Get the extremes from the value data
  522. Series.prototype.getExtremes.call(this, this.valueData);
  523. this.valueMin = this.dataMin;
  524. this.valueMax = this.dataMax;
  525. // Get the extremes from the y data
  526. Series.prototype.getExtremes.call(this);
  527. }
  528. }));
  529. }(Highcharts));