18

Hi I am using google visualization api to draw a timeline chart in my website. It works just fine. But there is one little thing that is bothering me. I want to display a vertical line in the chart area to represent the current date. Please let me know any kind of solutions.

My code:

<script type="text/javascript" src="https://www.google.com/jsapi?autoload={'modules':[{'name':'visualization','version':'1','packages':['timeline']}]}"></script>

<script type="text/javascript">
google.setOnLoadCallback(drawChart);
function drawChart() {

  var container = document.getElementById('example3.1');
  var chart = new google.visualization.Timeline(container);

  var dataTable = new google.visualization.DataTable();
  dataTable.addColumn({ type: 'string', id: 'Position' });
  dataTable.addColumn({ type: 'string', id: 'Name' });
  dataTable.addColumn({ type: 'date', id: 'Start' });
  dataTable.addColumn({ type: 'date', id: 'End' });
  dataTable.addRows([
    [ 'President',          'George Washington', new Date(2014, 3, 29), new Date(2014, 4, 3)],
    [ 'President',          'John Adams',        new Date(2014, 2, 3),  new Date(2014, 3, 3)],
    [ 'President',          'Thomas Jefferson',  new Date(2014, 2, 3),  new Date(2014, 5, 3)],
    [ 'Vice President',     'John Adams',        new Date(2014, 3, 20), new Date(2014, 5, 3)],
    [ 'Vice President',     'Thomas Jefferson',  new Date(2014, 2, 3),  new Date(2014, 6, 3)],
    [ 'Vice President',     'Aaron Burr',        new Date(2014, 2, 3),  new Date(2014, 2, 3)],
    [ 'Vice President',     'George Clinton',    new Date(2014, 2, 3),  new Date(2014, 2, 19)],

    ]);

  chart.draw(dataTable);
}

</script>

<div id="example3.1" style="width: 1000px; height: 200px;"></div>

Intended Result: Green Line represents current date enter image description here

Edit:

If this is not possible, please suggest any other API which can achieve this.

wishchaser
  • 628
  • 2
  • 7
  • 19

6 Answers6

13

to calculate the placement of the date marker,
find the begin and end dates of the timeline
use data table method --> getColumnRange()

var dateRangeStart = dataTable.getColumnRange(2);
var dateRangeEnd = dataTable.getColumnRange(3);

then divide the width of the chart by the difference in milliseconds
multiply the result by the difference of the start date and marker date

the first 'path' element found is the line separating the row labels and the timeline,
this can be used to offset the width of the row labels

see following working snippet...

google.charts.load('current', {
  packages:['timeline']
}).then(function () {
  var container = document.getElementById('timeline');
  var chart = new google.visualization.Timeline(container);
  var dataTable = new google.visualization.DataTable();
  dataTable.addColumn({type: 'string', id: 'Row'});
  dataTable.addColumn({type: 'string', id: 'Bar'});
  dataTable.addColumn({type: 'date', id: 'Start'});
  dataTable.addColumn({type: 'date', id: 'End'});
  var currentYear = (new Date()).getFullYear();  // keep example current
  dataTable.addRows([
    ['Row 1', 'A-1', new Date(currentYear, 0, 1), new Date(currentYear, 2, 31)],
    ['Row 1', 'A-2', new Date(currentYear, 3, 1), new Date(currentYear, 5, 30)],
    ['Row 2', 'B-1', new Date(currentYear, 6, 1), new Date(currentYear, 8, 31)],
    ['Row 2', 'B-2', new Date(currentYear, 9, 1), new Date(currentYear, 11, 31)]
  ]);
  var dataTableGroup = google.visualization.data.group(dataTable, [0]);
  var dateRangeStart = dataTable.getColumnRange(2);
  var dateRangeEnd = dataTable.getColumnRange(3);
  var formatDate = new google.visualization.DateFormat({
    pattern: 'MM/dd/yyyy'
  });
  var rowHeight = 44;
  var options = {
    height: (dataTableGroup.getNumberOfRows() * rowHeight) + rowHeight
  };

  function drawChart() {
    chart.draw(dataTable, options);
  }

  function addMarker(markerDate) {
    var baseline;
    var baselineBounds;
    var chartElements;
    var markerLabel;
    var markerLine;
    var markerSpan;
    var svg;
    var timeline;
    var timelineUnit;
    var timelineWidth;
    var timespan;

    baseline = null;
    timeline = null;
    svg = null;
    markerLabel = null;
    chartElements = container.getElementsByTagName('svg');
    if (chartElements.length > 0) {
      svg = chartElements[0];
    }
    chartElements = container.getElementsByTagName('rect');
    if (chartElements.length > 0) {
      timeline = chartElements[0];
    }
    chartElements = container.getElementsByTagName('path');
    if (chartElements.length > 0) {
      baseline = chartElements[0];
    }
    chartElements = container.getElementsByTagName('text');
    if (chartElements.length > 0) {
      markerLabel = chartElements[0].cloneNode(true);
    }
    if ((svg === null) || (timeline === null) || (baseline === null) || (markerLabel === null) ||
        (markerDate.getTime() < dateRangeStart.min.getTime()) ||
        (markerDate.getTime() > dateRangeEnd.max.getTime())) {
      return;
    }

    // calculate placement
    timelineWidth = parseFloat(timeline.getAttribute('width'));
    baselineBounds = baseline.getBBox();
    timespan = dateRangeEnd.max.getTime() - dateRangeStart.min.getTime();
    timelineUnit = (timelineWidth - baselineBounds.x) / timespan;
    markerSpan = markerDate.getTime() - dateRangeStart.min.getTime();

    // add label
    markerLabel.setAttribute('fill', '#e91e63');
    markerLabel.setAttribute('y', options.height);
    markerLabel.setAttribute('x', (baselineBounds.x + (timelineUnit * markerSpan) - 4));
    markerLabel.textContent = formatDate.formatValue(markerDate);
    svg.appendChild(markerLabel);

    // add line
    markerLine = timeline.cloneNode(true);
    markerLine.setAttribute('y', 0);
    markerLine.setAttribute('x', (baselineBounds.x + (timelineUnit * markerSpan)));
    markerLine.setAttribute('height', options.height);
    markerLine.setAttribute('width', 1);
    markerLine.setAttribute('stroke', 'none');
    markerLine.setAttribute('stroke-width', '0');
    markerLine.setAttribute('fill', '#e91e63');
    svg.appendChild(markerLine);
  }

  google.visualization.events.addListener(chart, 'ready', function () {
    // add marker for current date
    addMarker(new Date());
  });

  window.addEventListener('resize', drawChart, false);
  drawChart();
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="timeline"></div>
WhiteHat
  • 59,912
  • 7
  • 51
  • 133
12

Create a first task to represent current date:

      dataTable.addRows([
['', 'Hoy', new Date(2014,9,2), new Date(2014,9,2) ],

Create a function with jQuery to make this task longer:

function MarcarHoy (div, filas){
  $('#'+div+' text:contains("Hoy")').css('font-size','11px').attr('fill','#A6373C').prev().first().attr('height',filas*41+'px').attr('width','1px').attr('y','0');
  }

Call the function:

    chart.draw(dataTable, options);
  MarcarHoy('example1',23);
  google.visualization.events.addListener(chart, 'onmouseover', function(obj) {
    MarcarHoy('example1');
    });
}

The result:

enter image description here

Source: Viviendo en la Era de la Web 2.0

jpussacq
  • 573
  • 9
  • 29
6

I found a video in Sept 2016 about adding the vertical line in the timeline chart. https://www.youtube.com/watch?v=sG9tB04aaqE&t=416s

It also included the example in the video (https://jsfiddle.net/k5se146d/1/)

But the red line may be disappeared when the mouseover event triggered. I tried to add the folllowing line in the function. It seems a hacking on the timeline chart but I found no result on the google. Hope it can help anyone need.

function nowLine(div){

//get the height of the timeline div
    var height;
  $('#' + div + ' rect').each(function(index){
    var x = parseFloat($(this).attr('x'));
    var y = parseFloat($(this).attr('y'));

    if(x == 0 && y == 0) {height = parseFloat($(this).attr('height'))}
  })

    var nowWord = $('#' + div + ' text:contains("Now")');

  nowWord.prev().first().attr('height', height + 'px').attr('width', '1px').attr('y', '0');
// add this line to remove the display:none style on the vertical line
        $('#' + div + '  text:contains("Now")').each(function(idx, value) {
            if (idx == 0) {
                $(value).parent().find("rect").first().removeAttr("style");
            } else if (idx == 1) {
                $(value).parent().find("rect").first().attr("style", "display:none;");
            }

        });
}
Long Ranger
  • 5,888
  • 8
  • 43
  • 72
1

Use an "annotation" role column on the domain (date) column. In the chart options, set the annotation.<annotation column index>.style option to 'line':

function drawVisualization() {
    var data = google.visualization.arrayToDataTable([
        ['Date', {role: 'annotation'}, 'Value'],
        [new Date(2014, 1, 10), null, 5],
        [new Date(2014, 1, 11), null, 4],
        [new Date(2014, 1, 12), null, 3],
        [new Date(2014, 1, 13), null, 7],
        [new Date(2014, 1, 14), null, 5],
        [new Date(2014, 1, 15), null, 6],
        [new Date(2014, 1, 16), null, 9],
        [new Date(2014, 1, 17), null, 2],
        [new Date(2014, 1, 18), null, 2],
        [new Date(2014, 1, 19), 'Today', 4],
        [new Date(2014, 1, 20), null, 6],
        [new Date(2014, 1, 22), null, 5],
        [new Date(2014, 1, 23), null, 8],
        [new Date(2014, 1, 24), null, 8]
    ]);

    var chart = new google.visualization.LineChart(document.querySelector('#chart_div'));
    chart.draw(data, {
        width: 500,
        height: 400,
        annotation: {
            1: {
                style: 'line'
            }
        }
    });
}
google.load('visualization', '1', {packages:['corechart'], callback: drawVisualization});

see example here: http://jsfiddle.net/asgallant/r37uf/

asgallant
  • 26,060
  • 6
  • 72
  • 87
  • Thanks for the reply. Actually I want exactly what u have shown. Except, I want it in the chart I have included. Please tell me how to achieve the same result in this timeline chart I included. Thank you. – wishchaser Feb 20 '14 at 06:35
  • Annotations are not supported in Timeline charts yet, so you can't do this, sorry. – asgallant Feb 20 '14 at 17:00
  • The OP has been asking specifically for timeline chart, so I don't see how this answers the question. – Michał Górny May 01 '21 at 17:29
1

The important thing to note is that on hover, google charts will create 2 of the same text nodes.

The css of one of which is set to "display: none", and the other is displayed with a lighter background.

This gives an illusion that the background colour of the rectangle has dimmed

export const drawCurrentTime = (
  label: string
) => {
  if (ref.current != null) {
    const nodes = Array.from(document.getElementsByTagName("text")).filter(
      (node) => node.textContent === label
    );
    // To observe the duplication of the nodes...
    console.log(nodes.map((node) => node.outerHTML));

    // For each rectangle rendered, expand the height of both of those
    for (const node of nodes) {
      const rect = node.previousElementSibling as SVGElement;
      rect.style.height = "100%";
    }
  }
};

Then, for the data row, make the second row contain the current time, with a label

const currentTime = new Date();
const currentTimeLabel = "Now";
rows.splice(1, 0, ["\0", currentTimeLabel, currentTime, currentTime]);
// Lastly, feed the rows to the corresponding google chart renderer method
eugbyte
  • 11
  • 2
  • 2
0

I had the same issue myself, could not find anything on the web (although it may do exist) and I kind of solved the matter by overlaying a floating div in the shape of a vertical line which is relatively positioned as follows:

The width of the timeline chart (minus the row label width) is related to the difference between the min-max dates displayed on the chart. Assuming that today's date will be somewhere between the min-max dates, then the ratio (today - min date)/(max date - min date) equals to the ratio (current date line position)/(chart width - label width). The catch is that unless you can somehow set the label width to a specific number (which you can then use to calculate the effective chart width) you will end up with a "shifted" today line.

I could not find anything on how to set the row label width (any help on this greatly appreciated) so my today's line position is approximate... (but consistent while the label width remains unchanged)

Hope this helps, Nikolas

Dirk
  • 10,668
  • 2
  • 35
  • 49
  • Thank you for this solution Nicholas. I will try it and get back later. And for your problem, if the label is not really important, you can just hide it using 'showRowLabels' option. – wishchaser Mar 04 '14 at 08:16
  • BTW how did you manage to set the min and max dates? I tried using `hAxis.viewWindow.min` and `hAxis.viewWindow.max` . They don't seem to work in these charts. – wishchaser Mar 12 '14 at 04:43