-2

I am using google visualization bubble chart, I need to align the vertical axis labels something like below, I want to align the labels to the margin of the chart not to the axis line, also need 2 lines and extend the major grid line to outside of the chart area. enter image description here

Also here is the code :

<div data-ng-app="mainApp" data-ng-controller="mainSearchController"
     ng-init="ShowChart()">
    <div class="row" ng-mouseover="mousepoints($event)">
        <div google-chart chart="saleChart"
             agc-on-mouseover="showTooltip(row)"
             agc-on-mouseout="hideTooltip()">
        </div>
        <div id="custom_tooltip"
             style="position:fixed; border:0px solid #777777;
                    padding-left:10px; line-height:15px; color:#5f5f5f;
                    font-family:Arial; background-color:#FFFFFF;
                    height:auto; width:auto; font-size:10px;">
        </div>
    </div>
</div>

And here is the angularjs code to bind the chart

var app = angular.module('mainApp', ['googlechart']);

app.controller('mainSearchController', function ($scope) {
    
    $scope.ShowChart = function () {     
        var saleChart = {};
        saleChart.type = 'BubbleChart';
        saleChart.cssStyle = "height:100%; width:100%;";
        var options = {
            sizeAxis: {
                maxSize: 7,
                minSize: 1
            },
            fontSize:10,
            legend: 'none',
            height: 200,
            width: 400,
            bubble: { stroke: '#fdca0f', opacity: 1 },
            colors: ['#fdca0f', '#fdca0f'],
            tooltip: {
                trigger: 'none'
            },
            hAxis: {
                ticks: [
                    { v: 800, f: '2015' },
                    { v: 1200, f: '2016' },
                    { v: 1600, f: '2017' },
                    { v: 2000, f: '2018' },
                    { v: 2400, f: '2019' },
                    { v: 2800, f: '2020' }
                ],
                gridlines: { color: '#dedede' },
                minorGridlines: { color: '#f7f7f7', count: 3 },
                textStyle: { color: '#5f5f5f' }    
            },
            vAxis: {
                ticks: [
                    { v: 1, f: 'Chennai in March' },
                    { v: 2, f: 'Mumbai in March' },
                    { v: 3, f: 'Delhi in April' },
                    { v: 4, f: 'Chennai in April' }
                    
                ],
                gridlines: { color: '#dedede' },
                textStyle: { color: '#5f5f5f' }
            }
        };
            
        var d = [
          ["Name", "Year", "Place", "", "Sales", "tooltip"],
          ["", 1000, 2, "", 26, "Sale List"],
          ["",1200,3,"",28,"Sale List"],
          ["",1400,3,"",48,"S"],
          ["",1600,3,"",29,"S"]
        ];
        saleChart.data = d;
        $scope.chartData = d;
        saleChart.options = options;
        $scope.saleChart = saleChart;        
        
    }

    var mouseX;
    var mouseY;
    $scope.mousepoints = function (e) {
        mouseX = e.pageX;
        mouseY = e.pageY;
    }

    $scope.showTooltip = function (row) {    
        var x = mouseX;
        var y = mouseY + 10;        
        if (row != null) {        
            dataTable = google.visualization.arrayToDataTable($scope.chartData);
            var v = dataTable.getValue(row, 5);
            //var v = $scope.chartData.rows[row][5];
            v = v.toString().replace(/,/g, "<br/>")

            $('#custom_tooltip').html('<div>' + v + '</div>').css({
                'top': y,
                'left': x
            }).fadeIn('slow');
        }
    }
    $scope.hideTooltip = function () {
        $('#custom_tooltip').fadeOut('fast');
    }
});
JPil
  • 191
  • 2
  • 15
  • @georgeawg tag removed, thanks – Duke Mar 08 '20 at 22:21
  • 1
    can you please create a working instance (JSFIDDLE) of your code ? – Jasdeep Singh Mar 09 '20 at 17:39
  • @georgeawg. Actually this code is in AngularJS, that is the reason I added that tag too. – JPil Mar 09 '20 at 22:23
  • @SJPadikkoth you can use codesandbox to share code with community – TwistedOwl Mar 10 '20 at 17:29
  • This question is not clear. – JMA Mar 10 '20 at 21:52
  • @Prince I have added jsfiddle url and code here. chart works perfect only thing I need is just the vertical axis labels needs to be the same as in the above given image, break to new line and lines extend to outside the chart area. Thanks – JPil Mar 11 '20 at 23:01
  • 1
    this can only be accomplished by manually modifying the chart's svg, once the chart's ready event has fired. but there's an issue. you want to center the labels between the gridlines. however, if you notice in the example, there are four labels, but only enough room for 3 once centered. if you move each label down, to the center of the gridlines, where will you place the last label? – WhiteHat Mar 13 '20 at 12:17
  • @WhiteHat for fourth label I will be adding more ticks with blank text , but can you explain to me how can I extend that lines to outside the chart area?? – JPil Mar 13 '20 at 20:40
  • by modifying the svg, you can change the `` element's `'x'` and `'width'` attributes... – WhiteHat Mar 13 '20 at 21:04
  • @WhiteHat can you give me some sample codes for this please? – JPil Mar 17 '20 at 00:57

2 Answers2

1

the requested changes can only be made by manually modifying the chart's SVG,
this can be done on the chart's 'ready' event.

first, add the ready event to the <div google-chart> element...

<div google-chart chart="saleChart" agc-on-ready="onReady(chartWrapper)"
     agc-on-mouseover="showTooltip(row)" agc-on-mouseout="hideTooltip()">
</div>

then add the listener to the controller...

in order to move the labels down, find the <text> elements,
and the change their 'y' attribute.

as for the grid lines (<rect>), we need to change the 'x' attribute, as well as the 'width'.
not only on the grid lines, but the <rect> elements that contain the grid lines.

// ready event
$scope.onReady = function (chartWrapper) {
  // find, move labels
  var labels = chartWrapper.getChart().getContainer().getElementsByTagName('text');
  Array.prototype.forEach.call(labels, function(label) {
    if (label.getAttribute('text-anchor') === 'end') {
      var yLabel = parseFloat(label.getAttribute('y')) + (parseFloat(label.getAttribute('font-size')) * 2);
      label.setAttribute('y', yLabel);
    }
  });

  // find, expand grid lines
  var gridLines = chartWrapper.getChart().getContainer().getElementsByTagName('rect');
  Array.prototype.forEach.call(gridLines, function(line) {
    if ((line.getAttribute('height') === '1') ||
        ((line.getAttribute('x') !== '0') &&
        ((line.getAttribute('fill') === null) || (line.getAttribute('fill') === '#ffffff')))) {
      var lineWidth = parseFloat(line.getAttribute('width')) + parseFloat(line.getAttribute('x')) - 2;
      line.setAttribute('x', 2);
      line.setAttribute('width', lineWidth);
    }
  });
}

see following working snippet...

var app = angular.module('mainApp', ['googlechart']);

app.controller('mainSearchController', function ($scope) {
    $scope.ShowChart = function () {
        var saleChart = {};
        saleChart.type = 'BubbleChart';
        saleChart.cssStyle = "height:100%; width:100%;";
        var options = {
            sizeAxis: {
                maxSize: 7,
                minSize: 1
            },
            fontSize:10,
            legend: 'none',
            height: 200,
            width: 400,
            bubble: { stroke: '#fdca0f', opacity: 1 },
            colors: ['#fdca0f', '#fdca0f'],
            tooltip: {
                trigger: 'none'
            },
            hAxis: {
                ticks: [
                    { v: 800, f: '2015' },
                    { v: 1200, f: '2016' },
                    { v: 1600, f: '2017' },
                    { v: 2000, f: '2018' },
                    { v: 2400, f: '2019' },
                    { v: 2800, f: '2020' }
                ],
                gridlines: { color: '#dedede' },
                minorGridlines: { color: '#f7f7f7', count: 3 },
                textStyle: { color: '#5f5f5f' }
            },
            vAxis: {
                ticks: [
                    // add line break --> \n
                    { v: 1, f: 'Chennai\nin March' },
                    { v: 2, f: 'Mumbai\nin March' },
                    { v: 3, f: 'Delhi\nin April' },
                    { v: 4, f: 'Chennai\nin April' }
                ],
                gridlines: { color: '#dedede' },
                textStyle: { color: '#5f5f5f' }
            }
        };

        var d = [["Name", "Year", "Place", "", "Sales", "tooltip"],
        ["", 1000, 2, "", 26, "Sale List"],
        ["",1200,3,"",28,"Sale List"],
        ["",1400,3,"",48,"S"],["",1600,3,"",29,"S"]];
        saleChart.data = d;
        $scope.chartData = d;
        saleChart.options = options;
        $scope.saleChart = saleChart;

    }

    var mouseX;
    var mouseY;
    $scope.mousepoints = function (e) {
        mouseX = e.pageX;
        mouseY = e.pageY;
    }

    $scope.showTooltip = function (row) {
        var x = mouseX;
        var y = mouseY + 10;
        if (row != null) {
            dataTable = google.visualization.arrayToDataTable($scope.chartData);
            var v = dataTable.getValue(row, 5);
            //var v = $scope.chartData.rows[row][5];
            v = v.toString().replace(/,/g, "<br/>")

            $('#custom_tooltip').html('<div>' + v + '</div>').css({
                'top': y,
                'left': x
            }).fadeIn('slow');
        }
    }
    $scope.hideTooltip = function () {
        $('#custom_tooltip').fadeOut('fast');
    }
    
    $scope.onReady = function (chartWrapper) {
      var labels = chartWrapper.getChart().getContainer().getElementsByTagName('text');
      var labelIndex = 0;
      var nextLabels = [];
      Array.prototype.forEach.call(labels, function(label) {
        // find label
        if (label.getAttribute('text-anchor') === 'end') {
          // move label down
          var yLabel = parseFloat(label.getAttribute('y')) + (parseFloat(label.getAttribute('font-size')) * 1.5);
          label.setAttribute('y', yLabel);
          
          // set text line 1
          var labelText = chartWrapper.getOption('vAxis.ticks')[labelIndex].f.split('\n');
          label.textContent = labelText[0].toUpperCase();
          
          // save label
          nextLabels.push(label);
          labelIndex++;
        }
      });
      
      // add line 2
      nextLabels.forEach(function (label, labelIndex) {
        var yLabel = parseFloat(label.getAttribute('y')) + (parseFloat(label.getAttribute('font-size')) + 1);
        var nextLabel = label.parentNode.appendChild(label.cloneNode(true)); 
        var labelText = chartWrapper.getOption('vAxis.ticks')[labelIndex].f.split('\n');
        nextLabel.textContent = labelText[1];
        nextLabel.setAttribute('y', yLabel);
        
        // increase font size of line 1
        label.setAttribute('font-size', (parseFloat(label.getAttribute('font-size')) + 1));
        
        // re-align labels to left
        var labelWidth = label.getBBox().width;
        label.setAttribute('x', labelWidth + 2);
        labelWidth = nextLabel.getBBox().width;
        nextLabel.setAttribute('x', labelWidth + 2);
      });


      var gridLines = chartWrapper.getChart().getContainer().getElementsByTagName('rect');
      Array.prototype.forEach.call(gridLines, function(line) {
        if ((line.getAttribute('height') === '1') ||
            ((line.getAttribute('x') !== '0') &&
            ((line.getAttribute('fill') === null) || (line.getAttribute('fill') === '#ffffff')))) {
          var lineWidth = parseFloat(line.getAttribute('width')) + parseFloat(line.getAttribute('x')) - 2;
          line.setAttribute('x', 2);
          line.setAttribute('width', lineWidth);
        }
      });
    }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.8/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-google-chart/0.1.0/ng-google-chart.min.js"></script>

<div data-ng-app="mainApp" data-ng-controller="mainSearchController" ng-init="ShowChart()">
  <div class="row" ng-mouseover="mousepoints($event)">
    <div google-chart chart="saleChart" agc-on-mouseover="showTooltip(row)" agc-on-mouseout="hideTooltip()" agc-on-ready="onReady(chartWrapper)"></div>
    <div id="custom_tooltip" style="position:fixed; border:0px solid #777777; padding-left:10px; line-height:15px; color:#5f5f5f; font-family:Arial; background-color:#FFFFFF; height:auto; width:auto; font-size:10px;"></div>
  </div>
</div>
WhiteHat
  • 59,912
  • 7
  • 51
  • 133
  • How can I add new line in the labels as shown in the above image? – JPil Mar 17 '20 at 23:07
  • this helped me, thank you very much. But how can add new line to the labels as shown in the above image?? – JPil Mar 18 '20 at 00:46
  • 1
    changed above snippet, added second line for label, see line break in `vAxis.ticks` -- in latest version of google charts, this would be enough, but `ng-google-chart` uses an old version -- also increased font size of line 1 and re-aligned to the left... – WhiteHat Mar 18 '20 at 14:23
  • Sorry, it looks like this labels not working in ipad ?? – JPil May 11 '20 at 00:31
  • Ok, I found that this is happening in both ipad and PC as well. Y-axis text-anchor automatically change to 'middle' so can't see the y-axis labels. how can I fix that? – JPil May 25 '20 at 06:29
-1

google charts uses clip-path to attach visual graphics. You can take control over this using cx (horizontal axis), cy (vertical axis), and r (radius) attributes on <circle> elements. As each row will (or must) have same height you can simply add some js to make cy = cy - 10; (or whatever number that makes appear your circle where you want.

BUT there's another issue here, you can't set circles over the top axis, or bottom. Well, you can but it will be half-out of the canvas. At this point i think there's not much to do here, can't use z-index css property on this elements so you may not being able to reach the desired approach.

JoelBonetR
  • 1,551
  • 1
  • 15
  • 21