Search code examples
chartsgoogle-visualizationgoogle-barchart

Google BarChart - the horizontal base line is not appearing


First of all, sorry for combining multiple questions in to one. The only reason is all of them are related (hopefully) to one particular chart type.

QUESTION 1: The horizontal baseline is not appearing.

Actual enter image description here

Requirement enter image description here

QUESTION 2: Fraction values.

enter image description here

Is there a way to display only integers? I don't need fraction values in grid lines. Please see the above screenshot.

QUESTION 3: Vertical Line annotation text placement.

The annotation text for the vertical black bold line is coming to the right of it hence it is getting cut. Please see the second chart in the following screenshot

enter image description here

This actually needs to appear like this (to the bottom of the line and the annotation text needs to come a bit below base line labels). Please see the following screenshot

enter image description here

Is that is not possible, is there any way to place the annotation text to the left of this line so that it doesn't get cut and the entire annotation text stays inside the chart?

Below is the chart script I am using:

google.charts.load('current', {packages: ['corechart', 'bar']});
google.charts.setOnLoadCallback(drawHorizontalChart_portal_name_stella_york_horz_month_points);

function drawHorizontalChart_portal_name_stella_york_horz_month_points() {

    var data = google.visualization.arrayToDataTable([
        ["", "Goal Achieved", {role: 'style'}, "GOAL 13.1 points", {role: 'style'}, {role: 'annotation'}],
        ["", 1.5, "opacity: .75;", 13.1, "opacity: 0;", "GOAL 13.1 points"]
    ]);

    var view = new google.visualization.DataView(data);
    view.setColumns([0, 1, {
        calc: "stringify",
        sourceColumn: 1,
        type: "string",
        role: "annotation"
    }, 3, 4, 5]);

    var options = {
        title: '',
        width: '100%',
        height: 120,
        chartArea: {
            width: '90%',
            height: 70
        },
        hAxis: {
            title: '',
            minValue: 0,
            gridlines: {
                count: 6
            }
        },
        bar: {
            groupWidth: "60%"
        },
        legend: {
            position: "top"
        },
        series: {
            0: {
                color: '#70b5c5',
                visibleInLegend: false
            }, // Goal Achieved
            1: {
                color: '#000000',
                type: 'line',
                annotations: {
                    textStyle: {
                        color: '#000000',
                        textPosition: 'vertical'
                    },
                    stemColor: 'none',
                    vertical: true
                }
            } // Target Goal
        }
    };
    var chart = new google.visualization.BarChart(document.getElementById("portal-name-stella-york-horz-month-points"));
    chart.draw(view, options);

    drawVAxisLine(chart, 13.1);
}

jQuery(window).resize(function() {
    drawHorizontalChart_portal_name_stella_york_horz_month_points();
});


function drawVAxisLine(chart, value) {
    var layout = chart.getChartLayoutInterface();
    var chartArea = layout.getChartAreaBoundingBox();

    var svg = chart.getContainer().getElementsByTagName('svg')[0];
    var xLoc = layout.getXLocation(value)
    svg.appendChild(createLine(xLoc, chartArea.top + chartArea.height, xLoc, chartArea.top, '#000000', 2)); // axis line
}

function createLine(x1, y1, x2, y2, color, w) {
    var line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
    line.setAttribute('x1', x1);
    line.setAttribute('y1', y1);
    line.setAttribute('x2', x2);
    line.setAttribute('y2', y2);
    line.setAttribute('stroke', color);
    line.setAttribute('stroke-width', w);
    return line;
}

Solution

  • 1) horizontal baseline

    the horizontal baseline does not appear because you have a string value in the first column
    this creates a discrete axis

    // string used here --> ["", 1.5, "opacity: .75;", 13.1, "opacity: 0;", "GOAL 13.1 points"]
    

    instead, use a continuous x-axis (number, date, etc...)

    // number --> [1, 1.5, "opacity: .75;", 13.1, "opacity: 0;", "GOAL 13.1 points"]
    

    in order to hide the axis label, as done using the string, we can provide custom axis ticks
    we can use object notation to provide both the value (v:) and the formatted value (f:)
    which allows us to provide an empty string for the formatted value
    just make sure the tick value matches the value provided in the first column above.

        vAxis: {
            gridlines: {
                color: 'transparent'
            },
            ticks: [{v: 1, f: ''}]
        }
    

    note: a continuous axis will also cause other gridlines to appear,
    we can remove those by making them transparent...


    2) Fraction values

    we can provide a format string for the axis labels...

        hAxis: {
            format: '0'  // <-- format as integer
        },
    

    3) annotation text placement

    the only available option here is stem.length
    we can provide a negative value to move the annotation to the left...

    stem: {
        color: 'transparent',
        length: -128
    },
    

    however, the actual position will not remain constant as the chart is resized
    when the chart is smaller, the text will be farther away from the line (larger closer).

    instead, we can manually move the annotation text, on the chart's 'ready' event.
    but we should still use a negative stem length, to ensure the annotation appears to the left, and prevent from being cut. otherwise, we'll end up moving a cut annotation.
    and since we're moving the annotation below the axis,
    we need to increase chartArea.bottom or else it will be cut there as well.

    finally, the chart will reset the annotation's position on any interactivity,
    such as hover. we must use a MutationObserver to keep the annotation in the new position.


    see following working snippet...

    google.charts.load('current', {
      packages: ['corechart']
    }).then(drawHorizontalChart_portal_name_stella_york_horz_month_points);
    
    function drawHorizontalChart_portal_name_stella_york_horz_month_points() {
        var data = google.visualization.arrayToDataTable([
            ["", "Goal Achieved", {role: 'style'}, "GOAL 13.1 points", {role: 'style'}, {role: 'annotation'}],
            [1, 1.5, "opacity: .75;", 13.1, "opacity: 0;", "GOAL 13.1 points"]
        ]);
    
        var view = new google.visualization.DataView(data);
        view.setColumns([0, 1, {
            calc: "stringify",
            sourceColumn: 1,
            type: "string",
            role: "annotation"
        }, 3, 4, 5]);
    
        var options = {
            title: '',
            width: '100%',
            height: 132,
            chartArea: {
              height: '100%',
              width: '100%',
              top: 36,
              left: 18,
              right: 18,
              bottom: 48
            },
            hAxis: {
                title: '',
                minValue: 0,
                gridlines: {
                    count: 6
                },
                format: '0'
            },
            bar: {
                groupWidth: "60%"
            },
            legend: {
                position: "top"
            },
            series: {
                0: {
                    color: '#70b5c5',
                    visibleInLegend: false
                }, // Goal Achieved
                1: {
                    color: '#000000',
                    type: 'line',
                    annotations: {
                        textStyle: {
                            color: '#000000'
                        },
                        stem: {
                            color: 'transparent',
                            length: -128
                        },
                        vertical: true
                    }
                } // Target Goal
            },
            vAxis: {
                gridlines: {
                    color: 'transparent'
                },
                ticks: [{v: 1, f: ''}]
            }
        };
    
        var chart = new google.visualization.BarChart(document.getElementById("portal-name-stella-york-horz-month-points"));
    
        google.visualization.events.addListener(chart, 'ready', function () {
          // get x location of goal
          var layout = chart.getChartLayoutInterface();
          var xLoc = drawVAxisLine(chart, layout, data.getValue(0, 3));
    
          // prevent annotation reset
          var observer = new MutationObserver(function () {
            var annotationText = data.getValue(0, data.getNumberOfColumns() -1);
            Array.prototype.forEach.call(chart.getContainer().getElementsByTagName('text'), function(annotation) {
              // find annotation
              if ((annotation.textContent === annotationText) &&
                  (annotation.getAttribute('fill') === options.series[1].annotations.textStyle.color)) {
                // move annotation
                annotationBounds = annotation.getBBox();
                annotation.setAttribute('x',
                  xLoc - (annotationBounds.width / 2)
                );
                annotation.setAttribute('y',
                  layout.getYLocation(0) + (parseInt(annotation.getAttribute('font-size')) * 3)
                );
              }
            });
          });
          observer.observe(chart.getContainer(), {
            childList: true,
            subtree: true
          });
        });
    
        chart.draw(view, options);
    }
    
    jQuery(window).resize(function() {
        drawHorizontalChart_portal_name_stella_york_horz_month_points();
    });
    
    
    function drawVAxisLine(chart, layout, value) {
        var chartArea = layout.getChartAreaBoundingBox();
        var svg = chart.getContainer().getElementsByTagName('svg')[0];
        var xLoc = layout.getXLocation(value)
        svg.appendChild(createLine(xLoc, chartArea.top + chartArea.height, xLoc, chartArea.top, '#000000', 2)); // axis line
        return xLoc;
    }
    
    function createLine(x1, y1, x2, y2, color, w) {
        var line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
        line.setAttribute('x1', x1);
        line.setAttribute('y1', y1);
        line.setAttribute('x2', x2);
        line.setAttribute('y2', y2);
        line.setAttribute('stroke', color);
        line.setAttribute('stroke-width', w);
        return line;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://www.gstatic.com/charts/loader.js"></script>
    <div id="portal-name-stella-york-horz-month-points"></div>


    note: you should wait for the 'ready' event before making any changes / adding elements to the chart.