binning.js 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. /**
  2. * Hexagonal binning
  3. * Rendered as normally projected svg paths, which mean they *do not*
  4. * clip on spheres appropriately. To fix this, we would need to translate
  5. * the svg path into a geo-path
  6. */
  7. function displayBinning(scope, dr, dimensions) {
  8. var hexbin = d3.hexbin()
  9. .size(dimensions)
  10. .radius(scope.panel.display.binning.hexagonSize);
  11. var binPoints = [],
  12. binnedPoints = [],
  13. binRange = 0;
  14. if (scope.panel.display.binning.enabled) {
  15. /**
  16. * primary field is just binning raw counts
  17. *
  18. * Secondary field is binning some metric like mean/median/total. Hexbins doesn't support that,
  19. * so we cheat a little and just add more points to compensate.
  20. * However, we don't want to add a million points, so normalize against the largest value
  21. */
  22. if (scope.panel.display.binning.areaEncodingField === 'secondary') {
  23. var max = Math.max.apply(Math, _.map(scope.data, function(k,v){return k;})),
  24. scale = 50/max;
  25. _.map(scope.data, function (k, v) {
  26. var decoded = geohash.decode(v);
  27. return _.map(_.range(0, k*scale), function(a,b) {
  28. binPoints.push(dr.projection([decoded.longitude, decoded.latitude]));
  29. })
  30. });
  31. } else {
  32. binPoints = dr.projectedPoints;
  33. }
  34. //bin and sort the points, so we can set the various ranges appropriately
  35. binnedPoints = hexbin(binPoints).sort(function(a, b) { return b.length - a.length; });
  36. binRange = binnedPoints[0].length;
  37. //clean up some memory
  38. binPoints = [];
  39. } else {
  40. //not enabled, so just set an empty array. D3.exit will take care of the rest
  41. binnedPoints = [];
  42. binRange = 0;
  43. }
  44. var radius = d3.scale.sqrt()
  45. .domain([0, binRange])
  46. .range([0, scope.panel.display.binning.hexagonSize]);
  47. var color = d3.scale.linear()
  48. .domain([0,binRange])
  49. .range(["white", "steelblue"])
  50. .interpolate(d3.interpolateLab);
  51. var hex = dr.g.selectAll(".hexagon")
  52. .data(binnedPoints);
  53. hex.enter().append("path")
  54. .attr("d", function (d) {
  55. if (scope.panel.display.binning.areaEncoding === false) {
  56. return hexbin.hexagon();
  57. } else {
  58. return hexbin.hexagon(radius(d.length));
  59. }
  60. })
  61. .attr("class", "hexagon")
  62. .attr("transform", function (d) {
  63. return "translate(" + d.x + "," + d.y + ")";
  64. })
  65. .style("fill", function (d) {
  66. if (scope.panel.display.binning.colorEncoding === false) {
  67. return color(binnedPoints[0].length / 2);
  68. } else {
  69. return color(d.length);
  70. }
  71. })
  72. .attr("opacity", scope.panel.display.binning.hexagonAlpha);
  73. hex.exit().remove();
  74. }