1

Path hovering under SVG works when using plain JS + HTML. However when importing the same function over to Angular it stops working.

circularHeatChart.js

function loadCircularHeatMap (dataset, dom_element_to_append_to,radial_labels,segment_labels) {

  var margin = {top: 50, right: 50, bottom: 50, left: 50};
  var width = 600 - margin.left - margin.right;

  var height = width;
  var innerRadius = width/14;
  var segmentHeight = (width - margin.top - margin.bottom - 2*innerRadius )/(2*radial_labels.length)

  var chart = circularHeatChart()
  .innerRadius(innerRadius)
  .segmentHeight(segmentHeight)
  .range(["white", "#01579b"])
  .radialLabels(radial_labels)
  .segmentLabels(segment_labels);

  chart.accessor(function(d) {return d.value;})

  var svg = d3.select(dom_element_to_append_to)
  .selectAll('svg')
  .data([dataset])
  .enter()
  .append('svg')
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
  .append('g')
  .attr("transform",
      "translate(" + ( (width )/2 - (radial_labels.length*segmentHeight + innerRadius)  ) + "," + margin.top + ")")
  .call(chart);

  var tooltip = d3.select(dom_element_to_append_to)
  .append('div')
  .attr('class', 'tooltip');

  tooltip.append('div')
  .attr('class', 'month');
  tooltip.append('div')
  .attr('class', 'value');
  tooltip.append('div')
  .attr('class', 'type');

  svg.selectAll("path")
  .on('mouseover', function(d) {
      tooltip.select('.month').html("<b> Month: " + d.month + "</b>");
      tooltip.select('.type').html("<b> Type: " + d.type + "</b>");
      tooltip.select('.value').html("<b> Value: " + d.value + "</b>");

      tooltip.style('display', 'block');
      tooltip.style('opacity',2);
  })
  .on('mousemove', function(d) {

      tooltip.style('top', (d3.event.layerY + 10) + 'px')
      .style('left', (d3.event.layerX - 25) + 'px');
  })
  .on('mouseout', function(d) {
      tooltip.style('display', 'none');
      tooltip.style('opacity',0);
  });
}

function circularHeatChart() {
  var margin = {top: 20, right: 20, bottom: 20, left: 20},
  innerRadius = 20,
  numSegments = 12,
  segmentHeight = 20,
  domain = null,
  range = ["white", "red"],
  accessor = function(d) {return d;},
  radialLabels = segmentLabels = [];

  function chart(selection) {
      selection.each(function(data) {
          var svg = d3.select(this);

          var offset = innerRadius + Math.ceil(data.length / numSegments) * segmentHeight;
          g = svg.append("g")
              .classed("circular-heat", true)
              .attr("transform", "translate(" + parseInt(margin.left + offset) + "," + parseInt(margin.top + offset) + ")");

          var autoDomain = false;
          if (domain === null) {
              domain = d3.extent(data, accessor);
              autoDomain = true;
          }
          var color = d3.scale.linear().domain(domain).range(range);
          if(autoDomain)
              domain = null;

          g.selectAll("path").data(data)
              .enter().append("path")
              .attr("d", d3.svg.arc().innerRadius(ir).outerRadius(or).startAngle(sa).endAngle(ea))
              .attr("stroke", function(d) {return "#4f5b69";})
              .attr("fill", function(d) {return color(accessor(d));});

          // Unique id so that the text path defs are unique - is there a better way to do this?
          var id = d3.selectAll(".circular-heat")[0].length;

          //Radial labels
          var lsa = 0.01; //Label start angle
          var labels = svg.append("g")
              .classed("labels", true)
              .classed("radial", true)
              .attr("transform", "translate(" + parseInt(margin.left + offset) + "," + parseInt(margin.top + offset) + ")");

          labels.selectAll("def")
              .data(radialLabels).enter()
              .append("def")
              .append("path")
              .attr("id", function(d, i) {return "radial-label-path-"+id+"-"+i;})
              .attr("d", function(d, i) {
                  var r = innerRadius + ((i + 0.2) * segmentHeight);
                  return "m" + r * Math.sin(lsa) + " -" + r * Math.cos(lsa) +
                          " a" + r + " " + r + " 0 1 1 -1 0";
              });

          labels.selectAll("text")
              .data(radialLabels).enter()
              .append("text")
              .append("textPath")
              .attr("xlink:href", function(d, i) {return "#radial-label-path-"+id+"-"+i;})
              .style("font-size", "16px")
              .text(function(d) {return d;});

          //Segment labels
          var segmentLabelOffset = 2;
          var r = innerRadius + Math.ceil(data.length / numSegments) * segmentHeight + segmentLabelOffset;
          labels = svg.append("g")
              .classed("labels", true)
              .classed("segment", true)
              .attr("transform", "translate(" + parseInt(margin.left + offset) + "," + parseInt(margin.top + offset) + ")");

          labels.append("def")
              .append("path")
              .attr("id", "segment-label-path-"+id)
              .attr("d", "m0 -" + r + " a" + r + " " + r + " 0 1 1 -1 0");

          labels.selectAll("text")
              .data(segmentLabels).enter()
              .append("text")
              .append("textPath")
              .attr("xlink:href", "#segment-label-path-"+id)
              .style("font-size", "16px")
              .attr("startOffset", function(d, i) {return i * 100 / numSegments + "%";})
              .text(function(d) {return d;});
      });

  }

  /* Arc functions */
  ir = function(d, i) {
      return innerRadius + Math.floor(i/numSegments) * segmentHeight;
  }
  or = function(d, i) {
      return innerRadius + segmentHeight + Math.floor(i/numSegments) * segmentHeight;
  }
  sa = function(d, i) {
      return (i * 2 * Math.PI) / numSegments;
  }
  ea = function(d, i) {
      return ((i + 1) * 2 * Math.PI) / numSegments;
  }

  /* Configuration getters/setters */
  chart.margin = function(_) {
      if (!arguments.length) return margin;
      margin = _;
      return chart;
  };

  chart.innerRadius = function(_) {
      if (!arguments.length) return innerRadius;
      innerRadius = _;
      return chart;
  };

  chart.numSegments = function(_) {
      if (!arguments.length) return numSegments;
      numSegments = _;
      return chart;
  };

  chart.segmentHeight = function(_) {
      if (!arguments.length) return segmentHeight;
      segmentHeight = _;
      return chart;
  };

  chart.domain = function(_) {
      if (!arguments.length) return domain;
      domain = _;
      return chart;
  };

  chart.range = function(_) {
      if (!arguments.length) return range;
      range = _;
      return chart;
  };

  chart.radialLabels = function(_) {
      if (!arguments.length) return radialLabels;
      if (_ == null) _ = [];
      radialLabels = _;
      return chart;
  };

  chart.segmentLabels = function(_) {
      if (!arguments.length) return segmentLabels;
      if (_ == null) _ = [];
      segmentLabels = _;
      return chart;
  };

  chart.accessor = function(_) {
      if (!arguments.length) return accessor;
      accessor = _;
      return chart;
  };

  return chart;
}

circular-heatmap.component.ts

import { Component, OnInit } from '@angular/core';


declare function loadCircularHeatMap(dataset:any, dom_element_to_append_to:any, radial_labels:any, segment_labels:any):any;

@Component({
  selector: 'app-circular-heatmap',
  templateUrl: './circular-heatmap.component.html',
  styleUrls: ['./circular-heatmap.component.css']
})
export class CircularHeatmapComponent implements OnInit {

  constructor() { }

  ngOnInit(){
    var inputData =
    [{month:1,type:"Category 1",value:25},
    {month:2,type:"Category 1",value:15},
    {month:3,type:"Category 1",value:27},
    {month:4,type:"Category 1",value:10},
    {month:5,type:"Category 1",value:54},
    {month:6,type:"Category 1",value:23},
    {month:7,type:"Category 1",value:31},
    {month:8,type:"Category 1",value:17},
    {month:9,type:"Category 1",value:8},
    {month:10,type:"Category 1",value:12},
    {month:11,type:"Category 1",value:32},
    {month:12,type:"Category 1",value:35},
    {month:1,type:"Category 2",value:19},
    {month:2,type:"Category 2",value:24},
    {month:3,type:"Category 2",value:27},
    {month:4,type:"Category 2",value:12},
    {month:5,type:"Category 2",value:19},
    {month:6,type:"Category 2",value:30},
    {month:7,type:"Category 2",value:31},
    {month:8,type:"Category 2",value:25},
    {month:9,type:"Category 2",value:20},
    {month:10,type:"Category 2",value:5},
    {month:11,type:"Category 2",value:21},
    {month:12,type:"Category 2",value:10},
    {month:1,type:"Category 3",value:19},
    {month:2,type:"Category 3",value:3},
    {month:3,type:"Category 3",value:32},
    {month:4,type:"Category 3",value:23},
    {month:5,type:"Category 3",value:9},
    {month:6,type:"Category 3",value:17},
    {month:7,type:"Category 3",value:25},
    {month:8,type:"Category 3",value:29},
    {month:9,type:"Category 3",value:32},
    {month:10,type:"Category 3",value:33},
    {month:11,type:"Category 3",value:19},
    {month:12,type:"Category 3",value:24},
    {month:1,type:"Category 4",value:12},
    {month:2,type:"Category 4",value:43},
    {month:3,type:"Category 4",value:12},
    {month:4,type:"Category 4",value:23},
    {month:5,type:"Category 4",value:14},
    {month:6,type:"Category 4",value:19},
    {month:7,type:"Category 4",value:22},
    {month:8,type:"Category 4",value:39},
    {month:9,type:"Category 4",value:22},
    {month:10,type:"Category 4",value:26},
    {month:11,type:"Category 4",value:31},
    {month:12,type:"Category 4",value:25},
    ];

    var radial_labels = ["Category 1","Category 2","Category 3","Category 4"];
    var segment_labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];

    loadCircularHeatMap(inputData,"#chart",radial_labels, segment_labels);

  }
}

circular-heatmap.component.css

path:hover{

  fill:#9b59b6;
  }
  
.tooltip {
  background: #eee;
  box-shadow: 0 0 5px #999999;
  color: #333;
  font-size: 12px;
  left: 130px;
  padding: 10px;
  position: absolute;
  text-align: center;
  top: 95px;
  z-index: 10;
  display: block;
  opacity: 0;
}
  
.title{
  font-size:16px;
}

I am certain that the Javascript function is being properly imported as the heatmap itself is getting imported without a problem.

I have even created a single page JS+HTML webpage if that helps.

<!DOCTYPE>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Circular Heat Map</title>

<!-- JavaScript Libraries //-->
<script src="https://d3js.org/d3.v3.min.js"></script>

<!-- CSS Style //-->
<style type="text/css">
    path:hover{

fill:#9b59b6;
}

.tooltip {
    background: #eee;
    box-shadow: 0 0 5px #999999;
    color: #333;
    font-size: 12px;
    left: 130px;
    padding: 10px;
    position: absolute;
    text-align: center;
    top: 95px;
    z-index: 10;
    display: block;
    opacity: 0;
}

.title{
font-size:16px;
}
</style>

<script>
    function loadCircularHeatMap (dataset, dom_element_to_append_to,radial_labels,segment_labels) {

    var margin = {top: 50, right: 50, bottom: 50, left: 50};
    var width = 600 - margin.left - margin.right;

    var height = width;
    var innerRadius = width/14;
    var segmentHeight = (width - margin.top - margin.bottom - 2*innerRadius )/(2*radial_labels.length)

    var chart = circularHeatChart()
    .innerRadius(innerRadius)
    .segmentHeight(segmentHeight)
    .range(["white", "#01579b"])
    .radialLabels(radial_labels)
    .segmentLabels(segment_labels);

    chart.accessor(function(d) {return d.value;})

    var svg = d3.select(dom_element_to_append_to)
    .selectAll('svg')
    .data([dataset])
    .enter()
    .append('svg')
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append('g')
    .attr("transform",
        "translate(" + ( (width )/2 - (radial_labels.length*segmentHeight + innerRadius)  ) + "," + margin.top + ")")
    .call(chart);

    var tooltip = d3.select(dom_element_to_append_to)
    .append('div')
    .attr('class', 'tooltip');

    tooltip.append('div')
    .attr('class', 'month');
    tooltip.append('div')
    .attr('class', 'value');
    tooltip.append('div')
    .attr('class', 'type');

    svg.selectAll("path")
    .on('mouseover', function(d) {
        tooltip.select('.month').html("<b> Month: " + d.month + "</b>");
        tooltip.select('.type').html("<b> Type: " + d.type + "</b>");
        tooltip.select('.value').html("<b> Value: " + d.value + "</b>");

        tooltip.style('display', 'block');
        tooltip.style('opacity',2);
    })
    .on('mousemove', function(d) {

        tooltip.style('top', (d3.event.layerY + 10) + 'px')
        .style('left', (d3.event.layerX - 25) + 'px');
    })
    .on('mouseout', function(d) {
        tooltip.style('display', 'none');
        tooltip.style('opacity',0);
    });
}

function circularHeatChart() {
    var margin = {top: 20, right: 20, bottom: 20, left: 20},
    innerRadius = 20,
    numSegments = 12,
    segmentHeight = 20,
    domain = null,
    range = ["white", "red"],
    accessor = function(d) {return d;},
    radialLabels = segmentLabels = [];

    function chart(selection) {
        selection.each(function(data) {
            var svg = d3.select(this);

            var offset = innerRadius + Math.ceil(data.length / numSegments) * segmentHeight;
            g = svg.append("g")
                .classed("circular-heat", true)
                .attr("transform", "translate(" + parseInt(margin.left + offset) + "," + parseInt(margin.top + offset) + ")");

            var autoDomain = false;
            if (domain === null) {
                domain = d3.extent(data, accessor);
                autoDomain = true;
            }
            var color = d3.scale.linear().domain(domain).range(range);
            if(autoDomain)
                domain = null;

            g.selectAll("path").data(data)
                .enter().append("path")
                .attr("d", d3.svg.arc().innerRadius(ir).outerRadius(or).startAngle(sa).endAngle(ea))
                .attr("stroke", function(d) {return "#4f5b69";})
                .attr("fill", function(d) {return color(accessor(d));});

            // Unique id so that the text path defs are unique - is there a better way to do this?
            var id = d3.selectAll(".circular-heat")[0].length;

            //Radial labels
            var lsa = 0.01; //Label start angle
            var labels = svg.append("g")
                .classed("labels", true)
                .classed("radial", true)
                .attr("transform", "translate(" + parseInt(margin.left + offset) + "," + parseInt(margin.top + offset) + ")");

            labels.selectAll("def")
                .data(radialLabels).enter()
                .append("def")
                .append("path")
                .attr("id", function(d, i) {return "radial-label-path-"+id+"-"+i;})
                .attr("d", function(d, i) {
                    var r = innerRadius + ((i + 0.2) * segmentHeight);
                    return "m" + r * Math.sin(lsa) + " -" + r * Math.cos(lsa) +
                            " a" + r + " " + r + " 0 1 1 -1 0";
                });

            labels.selectAll("text")
                .data(radialLabels).enter()
                .append("text")
                .append("textPath")
                .attr("xlink:href", function(d, i) {return "#radial-label-path-"+id+"-"+i;})
                .style("font-size", "16px")
                .text(function(d) {return d;});

            //Segment labels
            var segmentLabelOffset = 2;
            var r = innerRadius + Math.ceil(data.length / numSegments) * segmentHeight + segmentLabelOffset;
            labels = svg.append("g")
                .classed("labels", true)
                .classed("segment", true)
                .attr("transform", "translate(" + parseInt(margin.left + offset) + "," + parseInt(margin.top + offset) + ")");

            labels.append("def")
                .append("path")
                .attr("id", "segment-label-path-"+id)
                .attr("d", "m0 -" + r + " a" + r + " " + r + " 0 1 1 -1 0");

            labels.selectAll("text")
                .data(segmentLabels).enter()
                .append("text")
                .append("textPath")
                .attr("xlink:href", "#segment-label-path-"+id)
                .style("font-size", "16px")
                .attr("startOffset", function(d, i) {return i * 100 / numSegments + "%";})
                .text(function(d) {return d;});
        });

    }

    /* Arc functions */
    ir = function(d, i) {
        return innerRadius + Math.floor(i/numSegments) * segmentHeight;
    }
    or = function(d, i) {
        return innerRadius + segmentHeight + Math.floor(i/numSegments) * segmentHeight;
    }
    sa = function(d, i) {
        return (i * 2 * Math.PI) / numSegments;
    }
    ea = function(d, i) {
        return ((i + 1) * 2 * Math.PI) / numSegments;
    }

    /* Configuration getters/setters */
    chart.margin = function(_) {
        if (!arguments.length) return margin;
        margin = _;
        return chart;
    };

    chart.innerRadius = function(_) {
        if (!arguments.length) return innerRadius;
        innerRadius = _;
        return chart;
    };

    chart.numSegments = function(_) {
        if (!arguments.length) return numSegments;
        numSegments = _;
        return chart;
    };

    chart.segmentHeight = function(_) {
        if (!arguments.length) return segmentHeight;
        segmentHeight = _;
        return chart;
    };

    chart.domain = function(_) {
        if (!arguments.length) return domain;
        domain = _;
        return chart;
    };

    chart.range = function(_) {
        if (!arguments.length) return range;
        range = _;
        return chart;
    };

    chart.radialLabels = function(_) {
        if (!arguments.length) return radialLabels;
        if (_ == null) _ = [];
        radialLabels = _;
        return chart;
    };

    chart.segmentLabels = function(_) {
        if (!arguments.length) return segmentLabels;
        if (_ == null) _ = [];
        segmentLabels = _;
        return chart;
    };

    chart.accessor = function(_) {
        if (!arguments.length) return accessor;
        accessor = _;
        return chart;
    };

    return chart;
}
</script>
</head>

<body>
    <div style="margin-left:50px;"  id="chart"></div>
</body>
<script>
    var inputData =
    [{month:1,type:"Category 1",value:25},
    {month:2,type:"Category 1",value:15},
    {month:3,type:"Category 1",value:27},
    {month:4,type:"Category 1",value:10},
    {month:5,type:"Category 1",value:54},
    {month:6,type:"Category 1",value:23},
    {month:7,type:"Category 1",value:31},
    {month:8,type:"Category 1",value:17},
    {month:9,type:"Category 1",value:8},
    {month:10,type:"Category 1",value:12},
    {month:11,type:"Category 1",value:32},
    {month:12,type:"Category 1",value:35},
    {month:1,type:"Category 2",value:19},
    {month:2,type:"Category 2",value:24},
    {month:3,type:"Category 2",value:27},
    {month:4,type:"Category 2",value:12},
    {month:5,type:"Category 2",value:19},
    {month:6,type:"Category 2",value:30},
    {month:7,type:"Category 2",value:31},
    {month:8,type:"Category 2",value:25},
    {month:9,type:"Category 2",value:20},
    {month:10,type:"Category 2",value:5},
    {month:11,type:"Category 2",value:21},
    {month:12,type:"Category 2",value:10},
    {month:1,type:"Category 3",value:19},
    {month:2,type:"Category 3",value:3},
    {month:3,type:"Category 3",value:32},
    {month:4,type:"Category 3",value:23},
    {month:5,type:"Category 3",value:9},
    {month:6,type:"Category 3",value:17},
    {month:7,type:"Category 3",value:25},
    {month:8,type:"Category 3",value:29},
    {month:9,type:"Category 3",value:32},
    {month:10,type:"Category 3",value:33},
    {month:11,type:"Category 3",value:19},
    {month:12,type:"Category 3",value:24},
    {month:1,type:"Category 4",value:12},
    {month:2,type:"Category 4",value:43},
    {month:3,type:"Category 4",value:12},
    {month:4,type:"Category 4",value:23},
    {month:5,type:"Category 4",value:14},
    {month:6,type:"Category 4",value:19},
    {month:7,type:"Category 4",value:22},
    {month:8,type:"Category 4",value:39},
    {month:9,type:"Category 4",value:22},
    {month:10,type:"Category 4",value:26},
    {month:11,type:"Category 4",value:31},
    {month:12,type:"Category 4",value:25},
    ];

    var radial_labels = ["Category 1","Category 2","Category 3","Category 4"];
    var segment_labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];

    loadCircularHeatMap(inputData,"#chart",radial_labels, segment_labels);
</script>
</html> 

Any leads on this issue is appreciated. Thanks in advance.

0 Answers0