0

I have drawn the following chart with D3 Charting tool v4. I have attached the full code at the bottom of this post.

enter image description here

The red line is the target goal to be achieved. The following code block is drawing this line:

var targetGoalArr = [7];
svg.selectAll(".targetgoal")
  .data(targetGoalArr)
  .enter().append("line")
  .attr("class", "targetgoal")
  .attr("x1", 0)
  .attr("x2", width)
  .attr("y1", y)
  .attr("y2", y)
  .style("stroke", "#cc0000");

Now I need to label this line with the text Growth Target (7) to the right of it and in two lines. The label has to be broken in two lines as well!

The following screenshot shows the desired output.

enter image description here

How can I achieve the above?

One more thing I am not able to draw is the Y-Axis baseline. In my chart (with red line) I am creating the horizontal lines using a custom tick array. Here is the code:

function draw_yAxis_gridlines() {
  return d3.axisLeft(y)
    .tickValues(yTicks);
}

svg.append("g")
  .attr("class", "grid axis")
  .call(draw_yAxis_gridlines()
  .tickSize(-width)
);

However, if I do not use custom ticks for Y-Axis, the baseline appears but I am missing the horizontal grid lines. I have to display both at the same time.

Here is my full code:

public function evd_unitary_growth_plan_chart( $data_str ){
        ob_start(); ?>

        <style> /* set the CSS */

            .line {
                fill: none;
                stroke: steelblue;
                stroke-width: 2px;
            }

            .grid line {
                stroke: lightgrey;
                stroke-opacity: 0.5;
                shape-rendering: crispEdges;
            }

            .grid path {
                stroke-width: 0;
            }

            .axis {
                font-size: 13px;
                font-family: 'Roboto';
                color: #808888;
            }

        </style>

        <script type="text/javascript">
            var h = 300;
            var w = 750;
            var barPadding = 2;

            function barColor(data_month, current_month) {
                if( parseInt(data_month) >= current_month)
                    return "#008600";
                else
                    return "#c4c4c4";
            }

            // set the dimensions and margins of the graph
            var margin = {top: 30, right: 20, bottom: 30, left: 40},
                width = w - margin.left - margin.right,
                height = h - margin.top - margin.bottom;

            var data = <?php echo $data_str ?>;

            // set the ranges
            var x = d3.scaleBand().range([0, width]).padding(0.2);
            var y = d3.scaleLinear().range([height, 0]);

            var svg = d3.select("#ecbg_unitary").append("svg")
                .attr("width", width + margin.left + margin.right)
                .attr("height", height + margin.top + margin.bottom)
            .append("g")
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

            // Scale the range of the data in the domains
            x.domain(data.map(function(d) { return d.month; }));

            var y_domain_upperBound = d3.max(data, function(d) { return d.points; });
            y_domain_upperBound = Math.round(y_domain_upperBound / 10) * 10 + 10;
            y.domain([0, y_domain_upperBound]);

            // Create Y-Axis tick array to draw grid lines
            var yTicks = [];
            var tickInterval = 5;
            for(var i = 0; i <= y_domain_upperBound; i = i + tickInterval) {
                yTicks.push(i);
            }

            console.log(yTicks);

            // gridlines in y axis function
            function draw_yAxis_gridlines() {
                return d3.axisLeft(y)
                    .tickValues(yTicks);
            }

            // Reference line - The red line
            var targetGoalArr = [7];
            svg.selectAll(".targetgoal")
                .data(targetGoalArr)
                .enter().append("line")
                .attr("class", "targetgoal")
                .attr("x1", 0)
                .attr("x2", width)
                .attr("y1", y)
                .attr("y2", y)
                .style("stroke", "#cc0000");

            // append the rectangles for the bar chart
            svg.selectAll("rect")
                .data(data)
                .enter().append("rect")
                .attr("x", function(d) {
                    return x(d.month);
                })
                .attr("width", x.bandwidth())
                .attr("y", function(d) { return y(d.points); })
                .attr("height", function(d) { return height - y(d.points); })
                .attr("fill", function(d){return barColor(d.data_month_number, d.current_month_number)});



            // column labels
            svg.selectAll("text")
                .data(data)
                .enter()
                .append("text")
                .text(function(d) {
                    return d.points;
                })
                .attr("text-anchor", "middle")
                .attr("x", function(d, i) {
                    return x(d.month) + x.bandwidth() / 2;
                })
                .attr("y", function(d) {
                    return y(d.points) - 10;
                })
                .attr("font-family", "Roboto")
                .attr("font-size", "13px")
                .attr("font-weight", "bold")
                .attr("fill", "#606668");

            // add the x Axis
            svg.append("g")
                .attr("class", "axis")
                .attr("transform", "translate(0," + height + ")")
                .call(d3.axisBottom(x));


            // add the Y gridlines
            svg.append("g")
                .attr("class", "grid axis")
                .call(draw_yAxis_gridlines()
                    .tickSize(-width)
                );

        </script>

        <?php return ob_get_clean();
    }
Subrata Sarkar
  • 2,975
  • 6
  • 45
  • 85
  • Sorry if I have misunderstood it. I am absolutely new to this and hope my beginner knowledge would be considered. It would be great if you please guide me with how I would accomplish the above! – Subrata Sarkar Nov 28 '19 at 06:22

1 Answers1

1

To add a label to your target line, you are best to create group (g) element, and then append a line and text element to it. The g element can be translated to the correct y position, so that the line and text can be positioned relatively to the g.

var targetGoalArr = [7];

var target = g.selectAll(".targetgoal")
  .data(targetGoalArr)
  .enter()
  .append("g")
  .attr("transform", function(d){
    return "translate(0, " + y(d) +")"
  })

target.append("line")
  .attr("class", "targetgoal")
  .attr("x1", 0)
  .attr("x2", width)
  .attr("y1", 0)  //these can be omitted
  .attr("y2", 0)
  .style("stroke", "#cc0000");

target.append("text")
    .text(function(d){ return "Target growth: " + d })
    .attr("x", width)
    .attr("y", "0.35em")
Tom Shanley
  • 1,757
  • 1
  • 7
  • 9
  • Perfect solution! Thank you so much @Tom Shanley!! However, I needed to change `var target = g.selectAll(".targetgoal")` to `var target = svg.selectAll(".targetgoal")`. And for breaking the label in two lines I took the reference of https://stackoverflow.com/a/13254862/1496518 (two separate `text` elements) and positioned them accordingly. – Subrata Sarkar Nov 29 '19 at 06:39
  • @tom-shaley, would you please suggest me how do I accomplish what I posted here: https://stackoverflow.com/questions/59105143/d3-chart-how-do-i-prevent-y-axis-grid-lines-from-using-the-entire-chart-area-wi? – Subrata Sarkar Nov 29 '19 at 12:49