3

I am working on building a visual that looks something like this: enter image description here.

So far I've managed to create this: enter image description here

The idea is to map a value to an angle so that I know where to point the arrow and then I will color the arrow the same color as the point on the arc that its pointing to.

I essentially have two questions:

First what can I do in order to make the colors line up better. I've used a linear gradient like so:

 let defs = this.gaugeEl
                .append("defs")
                .classed("definitions",true);
        let gradient = defs
            .append("linearGradient")
            .classed("linearGradient",true);

        gradient
            .attr({
                id: 'gradient',
                x1: '0%',
                y1: '0%',
                x2: '100%',
                y2: '100%',
                spreadMethod: "pad"
            });

        gradient
            .append("stop")
            .classed('start',true)
            .attr({
                offset: '0%',
                'stop-color': 'lawngreen',
                'stop-opacity': 1
            });

        gradient.append("stop")
            .classed('end',true)
            .attr({
                offset: '100%',
                'stop-color': 'red',
                'stop-opacity': 1
            });

The effect is not what I was hoping for, what can be done?

The next question about how the gradient works, I need to be able to associate an angle with a color so that I can color the arrow and the tick marks properly and in my current setup I don't know how to do that. Is it even possible?

Paulie_D
  • 107,962
  • 13
  • 142
  • 161
ClimbingTheCurve
  • 323
  • 2
  • 14

2 Answers2

3

I don't know how much useful this will be for you. But I followed the below implementation

  1. Split the arc into tiny arcs
  2. Used scaleLinear for associating color and angle and divided the arc into four segments

Ignore bad math and code !

<!DOCTYPE html>
<meta charset="utf-8">
<style>
  
  #chart {
    width: 960px;
    height: 350px;
  }

</style>

<body>

  <svg id="chart">  
  </svg>



  <script src="http://d3js.org/d3.v5.min.js"></script>

  <script>
var vis = d3.select("#chart").append("g")
var pi = Math.PI;

var line = d3.line()
    .x(function (d) { return d.x; })
    .y(function (d) { return d.y; });

var lines = []



var breakPoints = 100;
var angleArr = [];
var arcArr = [];

//angleArr[0] = -pi/2; 

var colorScale = d3.scaleLinear()
    .domain([-pi/2, -pi/3,30*pi/180,pi/2])
    .range(['lightgreen', 'lightgreen', 'yellow','red']);
    
 
var angleScale = d3.scaleLinear()
    .range([-pi/2,pi/2])
    .domain([0,breakPoints - 1]); 
 

var prevAngle = -pi/2; 
for(var i = 0; i < breakPoints; i++) {
 angleArr[i] = angleScale(i);
 var singleArrow = [{"x":(150*Math.sin(angleArr[i])), "y":-(150*Math.cos(angleArr[i]))},{ "y":-(170*Math.cos(angleArr[i])), "x":(170*Math.sin(angleArr[i]))}];
 //var subArc = {"start": prev, "end":0};
 var subArc = {};
 lines.push(singleArrow);
 subArc["start"] = prevAngle;
 subArc["end"] = angleArr[i];
 prevAngle = angleArr[i];
 arcArr.push(subArc);
}

 
var arc = d3.arc()
    .innerRadius(160)
    .outerRadius(170)
    .startAngle(-(pi/2)) //converting from degs to radians
    .endAngle(pi/2) //just radians
    
vis.attr("width", "400").attr("height", "400") // Added height and width so arc is visible
    .append("g")
    .attr("transform", "translate(200,200)");

vis.selectAll("line")
   .data(lines)
   .enter()
   .append("path").attr("class","arrow").attr("d", line).attr("stroke",function(d,i) { 
 return colorScale(angleArr[i])}).attr("transform", "translate(200,200)"); 

vis.selectAll("arcs")
   .data(arcArr)
   .enter()
   .append("path").attr("class","arc").attr("d", function(d,i) {
   return d3.arc()
    .innerRadius(160)
    .outerRadius(170)
    .startAngle(d.end)
    .endAngle(d.start)()}).attr("fill",function(d,i) { 
 return colorScale(angleArr[i])}).attr("transform", "translate(200,200)"); 
   
  </script>

</body>
Umesh Maharshi
  • 1,571
  • 10
  • 12
  • That's beautiful! I ended up doing something similar, only using paths elements, which has the advantage of being easily generalized to many forms. Yours came out nicer than mine, I believe I will adopt this approach. Thanks! – ClimbingTheCurve Oct 11 '18 at 06:14
0

There is an easier way to do this now with a css property called conic-gradient

https://css-tricks.com/snippets/css/css-conic-gradient/

It sets the color according to the angle, given a center point. Maybe you could get the angle to the point with a click event, calculate the angel from the center, and set the colour that way.

There's some more info on conic gradients here, including how to calculate it: https://wiki.inkscape.org/wiki/index.php/Advanced_Gradients#Conical_gradient

Joshua Swain
  • 571
  • 2
  • 4
  • 22