1

Been looking for a solution to this with either chartist or chart.js, but keep coming up empty. I'm looking for a way to create a line graph and change the color of the line based on the data.

Example data set:

[
    {time: '3:00:00', speed: 20, direction: 345},
    {time: '3:01:00', speed: 0, direction: 0},
    {time: '3:02:00', speed: 25, direction: 90},
    {time: '3:03:00', speed: 10, direction: 180},
    {time: '3:04:00', speed: 5, direction: 0}
]

I'm looking for the time to be the x axis, the speed to be the y axis, and then to change the color of the line segment based on the direction. Seems like most of the charting libraries (unless I start digging into d3) when they create line graphs create one big line for the whole series and doesn't let you style the individual segments of the line. There's a great chartjs example here, but the color isn't based on the data (it's just an arbitrary set of colors):

https://blog.vanila.io/chart-js-tutorial-how-to-make-gradient-line-chart-af145e5c92f9

dgwebb
  • 321
  • 3
  • 16
  • Provide some code – EugenSunic Feb 14 '20 at 19:30
  • 1
    Take a look at this post. It may be useful to you. It appears you may need to change programming in the script that draws the line, however there is some useful information in the post. https://github.com/chartjs/Chart.js/issues/3071 – Scott Purtan Feb 14 '20 at 20:24
  • 1
    Does this answer your question? [ChartJS different background gradient depending on data (line graph)](https://stackoverflow.com/questions/52636353/chartjs-different-background-gradient-depending-on-data-line-graph) – timclutton Feb 14 '20 at 21:18
  • Saw a few solutions like that, but they are more focused on a "threshold" - like an over/under. If it's over, let's say, 0, it's filled with one color; if it's under, it's filled with another. What I need to do is have multiple colors on that line based on multiple different values. For example, to show "direction" (which could have approx 16 different color values) on the line that shows speed. – dgwebb Feb 14 '20 at 23:06

2 Answers2

2

You could create a scatter chart and draw the lines directly on the canvas using the Plugin Core API. The API offers a range of hooks that may be used for performing custom code.

In below code snippet, I use the afterDraw hook to draw connection lines between data points. Their color depends on the direction property of the start data point.

const rawData = [
    {time: '3:00:00', speed: 20, direction: 345},
    {time: '3:01:00', speed: 0, direction: 0},
    {time: '3:02:00', speed: 25, direction: 290},
    {time: '3:03:00', speed: 10, direction: 180},
    {time: '3:04:00', speed: 5, direction: 0}
];
const data = rawData.map(o => ({ x: moment(o.time, 'H:mm:ss').toDate().getTime(), y: o.speed}));
const colorsPer100 = ['green', 'orange', 'blue', 'red']; 

new Chart(document.getElementById("myChart"), {
  type: "scatter",
  plugins: [{
    afterDraw: chart => {      
      var ctx = chart.chart.ctx; 
      var xAxis = chart.scales['x-axis-1'];
      var yAxis = chart.scales['y-axis-1'];      
      chart.config.data.datasets[0].data.forEach((value, index) => {
        if (index > 0) {        
           var valueFrom = data[index - 1];
           var xFrom = xAxis.getPixelForValue(valueFrom.x);     
           var yFrom = yAxis.getPixelForValue(valueFrom.y);     
           var direction = rawData[index -1].direction;
           var xTo = xAxis.getPixelForValue(value.x);         
           var yTo = yAxis.getPixelForValue(value.y); 
           ctx.save();           
           ctx.strokeStyle = colorsPer100[Math.floor(direction / 100)];           
           ctx.lineWidth = 2;
           ctx.beginPath();
           ctx.moveTo(xFrom, yFrom);             
           ctx.lineTo(xTo, yTo);
           ctx.stroke();
           ctx.restore();
        }
      });      
    }
  }],
  data: {
    datasets: [{
      label: "My Dataset",
      data: data,
      borderColor: "rgb(75, 192, 192)"
    }]
  },
  options: {
    legend: {
      display: false
    },
    scales: {
      xAxes: [{
        type: 'time',
        time: {
          unit: 'minute',
          displayFormats: {
            minute: 'H:mm:ss'
          }
        },
      }]
    }
  }  
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="myChart" height="90"></canvas>
uminder
  • 23,831
  • 5
  • 37
  • 72
0

uminder has an interesting solution. In another thread, someone posted a solution which I went with - they just created an array of colors that's the same size as the dataset (and just feed that in with the data). That worked great and was much simpler than I think I was trying to make it.

dgwebb
  • 321
  • 3
  • 16