Search code examples
javascriptd3.js

How to conditionally fill the area in radial chart correctly in D3?


I can calculate the negative and positive area. However, there are some unexpected gaps in my plot. enter image description here

Red line represents the 24 hour measurements of VALUE1 column and green line represents VALUE2 column. If VALUE1 is bigger than VALUE2, I want to fill the area with darkblue, else with lightblue. However, I ended up having some gaps and my code partially works. How can I solve this issue? Does the problem lie where I code negative and positive area?

I expect to have a radial chart where if VALUE1 is bigger than VALUE2, I want to fill the area between two lines with darkblue color. IF VALUE2 is bigger than VALUE1, I want to fill the area between two lines with lightblue color. How can I achieve it?

Here is my JSFiddle link: https://jsfiddle.net/phvzur7a/ This is the part that does not work. I aim to fill area of crossing oints, but this part of code does not function at all.

function findCrossingPoints(data) {
  const crossingPoints = [];

  for (let i = 1; i < data.length; i++) {
    const d1 = data[i - 1];
    const d2 = data[i];

    if ((d1.VALUE1 - d1.VALUE2) * (d2.VALUE1 - d2.VALUE2) < 0) {
      // There's a crossing point between the two data points
      const t =
        (0 - (d1.VALUE1 - d1.VALUE2)) /
        (d2.VALUE1 - d2.VALUE2 - (d1.VALUE1 - d1.VALUE2));
      const crossingValue1 = d1.VALUE1 + t * (d2.VALUE1 - d1.VALUE1);
      const crossingValue2 = d1.VALUE2 + t * (d2.VALUE2 - d1.VALUE2);
      crossingPoints.push({ VALUE1: crossingValue1, VALUE2: crossingValue2 });
    }
  }

  return crossingPoints;
}

const crossingPoints = findCrossingPoints(rawdata2);

    const rawdata = [
  {
    "DATE_TIME": "2018-10-31T23:00:00.000Z",
    "VALUE1": 160,
    "VALUE2": 110,
    "INSPECTION": 1,
    "minuteTime": 0
  },
  {
    "DATE_TIME": "2018-10-31T23:20:00.000Z",
    "VALUE1": 102,
    "VALUE2": 118,
    "INSPECTION": 1,
    "minuteTime": 20
  },
  {
    "DATE_TIME": "2018-10-31T23:40:00.000Z",
    "VALUE1": 153,
    "VALUE2": 117,
    "INSPECTION": 1,
    "minuteTime": 40
  },
  {
    "DATE_TIME": "2018-11-01T00:00:00.000Z",
    "VALUE1": 112,
    "VALUE2": 117,
    "INSPECTION": 1,
    "minuteTime": 60
  },
  {
    "DATE_TIME": "2018-11-01T00:20:00.000Z",
    "VALUE1": 95,
    "VALUE2": 103,
    "INSPECTION": 1,
    "minuteTime": 80
  },
  {
    "DATE_TIME": "2018-11-01T00:40:00.000Z",
    "VALUE1": 157,
    "VALUE2": 119,
    "INSPECTION": 1,
    "minuteTime": 100
  },
  {
    "DATE_TIME": "2018-11-01T01:00:00.000Z",
    "VALUE1": 129,
    "VALUE2": 120,
    "INSPECTION": 1,
    "minuteTime": 120
  },
  {
    "DATE_TIME": "2018-11-01T01:20:00.000Z",
    "VALUE1": 135,
    "VALUE2": 108,
    "INSPECTION": 1,
    "minuteTime": 140
  },
  {
    "DATE_TIME": "2018-11-01T01:40:00.000Z",
    "VALUE1": 126,
    "VALUE2": 119,
    "INSPECTION": 1,
    "minuteTime": 160
  },
  {
    "DATE_TIME": "2018-11-01T02:00:00.000Z",
    "VALUE1": 121,
    "VALUE2": 110,
    "INSPECTION": 1,
    "minuteTime": 180
  },
  {
    "DATE_TIME": "2018-11-01T02:20:00.000Z",
    "VALUE1": 133,
    "VALUE2": 101,
    "INSPECTION": 1,
    "minuteTime": 200
  },
  {
    "DATE_TIME": "2018-11-01T02:40:00.000Z",
    "VALUE1": 124,
    "VALUE2": 120,
    "INSPECTION": 1,
    "minuteTime": 220
  },
  {
    "DATE_TIME": "2018-11-01T03:00:00.000Z",
    "VALUE1": 121,
    "VALUE2": 112,
    "INSPECTION": 1,
    "minuteTime": 240
  },
  {
    "DATE_TIME": "2018-11-01T03:20:00.000Z",
    "VALUE1": 147,
    "VALUE2": 108,
    "INSPECTION": 1,
    "minuteTime": 260
  },
  {
    "DATE_TIME": "2018-11-01T03:40:00.000Z",
    "VALUE1": 98,
    "VALUE2": 102,
    "INSPECTION": 1,
    "minuteTime": 280
  },
  {
    "DATE_TIME": "2018-11-01T04:00:00.000Z",
    "VALUE1": 136,
    "VALUE2": 109,
    "INSPECTION": 1,
    "minuteTime": 300
  },
  {
    "DATE_TIME": "2018-11-01T04:20:00.000Z",
    "VALUE1": 130,
    "VALUE2": 112,
    "INSPECTION": 1,
    "minuteTime": 320
  },
  {
    "DATE_TIME": "2018-11-01T04:40:00.000Z",
    "VALUE1": 156,
    "VALUE2": 100,
    "INSPECTION": 1,
    "minuteTime": 340
  },
  {
    "DATE_TIME": "2018-11-01T05:00:00.000Z",
    "VALUE1": 135,
    "VALUE2": 102,
    "INSPECTION": 1,
    "minuteTime": 360
  },
  {
    "DATE_TIME": "2018-11-01T05:20:00.000Z",
    "VALUE1": 112,
    "VALUE2": 120,
    "INSPECTION": 1,
    "minuteTime": 380
  },
  {
    "DATE_TIME": "2018-11-01T05:40:00.000Z",
    "VALUE1": 116,
    "VALUE2": 103,
    "INSPECTION": 1,
    "minuteTime": 400
  },
  {
    "DATE_TIME": "2018-11-01T06:00:00.000Z",
    "VALUE1": 129,
    "VALUE2": 107,
    "INSPECTION": 1,
    "minuteTime": 420
  },
  {
    "DATE_TIME": "2018-11-01T06:20:00.000Z",
    "VALUE1": 107,
    "VALUE2": 118,
    "INSPECTION": 1,
    "minuteTime": 440
  },
  {
    "DATE_TIME": "2018-11-01T06:40:00.000Z",
    "VALUE1": 112,
    "VALUE2": 118,
    "INSPECTION": 1,
    "minuteTime": 460
  },
  {
    "DATE_TIME": "2018-11-01T07:00:00.000Z",
    "VALUE1": 116,
    "VALUE2": 113,
    "INSPECTION": 1,
    "minuteTime": 480
  },
  {
    "DATE_TIME": "2018-11-01T07:20:00.000Z",
    "VALUE1": 92,
    "VALUE2": 109,
    "INSPECTION": 1,
    "minuteTime": 500
  },
  {
    "DATE_TIME": "2018-11-01T07:40:00.000Z",
    "VALUE1": 92,
    "VALUE2": 120,
    "INSPECTION": 1,
    "minuteTime": 520
  },
  {
    "DATE_TIME": "2018-11-01T08:00:00.000Z",
    "VALUE1": 112,
    "VALUE2": 101,
    "INSPECTION": 1,
    "minuteTime": 540
  },
  {
    "DATE_TIME": "2018-11-01T08:20:00.000Z",
    "VALUE1": 126,
    "VALUE2": 105,
    "INSPECTION": 1,
    "minuteTime": 560
  },
  {
    "DATE_TIME": "2018-11-01T08:40:00.000Z",
    "VALUE1": 135,
    "VALUE2": 108,
    "INSPECTION": 1,
    "minuteTime": 580
  },
  {
    "DATE_TIME": "2018-11-01T09:40:00.000Z",
    "VALUE1": 108,
    "VALUE2": 115,
    "INSPECTION": 1,
    "minuteTime": 640
  },
  {
    "DATE_TIME": "2018-11-01T10:40:00.000Z",
    "VALUE1": 112,
    "VALUE2": 114,
    "INSPECTION": 1,
    "minuteTime": 700
  },
  {
    "DATE_TIME": "2018-11-01T11:40:00.000Z",
    "VALUE1": 107,
    "VALUE2": 114,
    "INSPECTION": 1,
    "minuteTime": 760
  },
  {
    "DATE_TIME": "2018-11-01T12:40:00.000Z",
    "VALUE1": 110,
    "VALUE2": 115,
    "INSPECTION": 1,
    "minuteTime": 820
  },
  {
    "DATE_TIME": "2018-11-01T13:40:00.000Z",
    "VALUE1": 101,
    "VALUE2": 101,
    "INSPECTION": 1,
    "minuteTime": 880
  },
  {
    "DATE_TIME": "2018-11-01T14:40:00.000Z",
    "VALUE1": 145,
    "VALUE2": 118,
    "INSPECTION": 1,
    "minuteTime": 940
  },
  {
    "DATE_TIME": "2018-11-01T15:40:00.000Z",
    "VALUE1": 100,
    "VALUE2": 107,
    "INSPECTION": 1,
    "minuteTime": 1000
  },
  {
    "DATE_TIME": "2018-11-01T16:40:00.000Z",
    "VALUE1": 155,
    "VALUE2": 107,
    "INSPECTION": 1,
    "minuteTime": 1060
  },
  {
    "DATE_TIME": "2018-11-01T17:40:00.000Z",
    "VALUE1": 132,
    "VALUE2": 101,
    "INSPECTION": 1,
    "minuteTime": 1120
  },
  {
    "DATE_TIME": "2018-11-01T18:40:00.000Z",
    "VALUE1": 110,
    "VALUE2": 112,
    "INSPECTION": 1,
    "minuteTime": 1180
  },
  {
    "DATE_TIME": "2018-11-01T19:40:00.000Z",
    "VALUE1": 160,
    "VALUE2": 119,
    "INSPECTION": 1,
    "minuteTime": 1240
  },
  {
    "DATE_TIME": "2018-11-01T20:40:00.000Z",
    "VALUE1": 127,
    "VALUE2": 104,
    "INSPECTION": 1,
    "minuteTime": 1300
  },
  {
    "DATE_TIME": "2018-11-01T21:40:00.000Z",
    "VALUE1": 150,
    "VALUE2": 108,
    "INSPECTION": 1,
    "minuteTime": 1360
  },
  {
    "DATE_TIME": "2018-11-01T22:40:00.000Z",
    "VALUE1": 108,
    "VALUE2": 111,
    "INSPECTION": 1,
    "minuteTime": 1420
  },
  {
    "DATE_TIME": "2018-11-01T22:59:00.000Z",
    "VALUE1": 151,
    "VALUE2": 102,
    "INSPECTION": 1,
    "minuteTime": 1439
  }
];

const width = 954;
const height = width;
const margin = 10;
const innerRadius = width / 5;
const outerRadius = width / 2 - margin;


    
      let rawdata2 = rawdata.filter(d => d.INSPECTION == 1);

      const minuteScale = d3.scaleLinear()
        .domain([0, 1439]) // 0 to 23:59 (24 hours * 60 minutes without -1 or else change to 1439 //- 1)
        .range([0, 2 * Math.PI]);

      const minuteTimeValues = rawdata2.map(d => d.minuteTime);

      const x = d3.scaleTime()
        .domain([0, 1439]) // Set the domain using the extent of the "minuteTime" values
        .range([0, 2 * Math.PI]); // Set the desired range

      console.log(rawdata)

      const minValue1 = d3.min(rawdata, d => d.VALUE1);
      const minValue2 = d3.min(rawdata, d => d.VALUE2);
      const maxValue1 = d3.max(rawdata, d => d.VALUE1);
      const maxValue2 = d3.max(rawdata, d => d.VALUE2);

      const yMin = minValue1 < minValue2 ? minValue1 : minValue2;
      const yMax = maxValue1 > maxValue2 ? maxValue1 : maxValue2;

      const y = d3.scaleLinear()
                  .domain([yMin, yMax])
                  .range([innerRadius, outerRadius]);

// Modify the xAxis generator to display only the hour and add padding

const xAxis = (g) => g
  .attr("font-family", "sans-serif")
  .attr("font-size", 10)
  .call((g) => g.selectAll("g")
    .data(d3.range(0, 24)) // create an array of hour values
    .join("g")
      .each((d, i) => d.id = `hour-${i}`)
      .call((g) => g.append("path")
          .attr("stroke", "#000")
          .attr("stroke-opacity", 0.2)
          .attr("d", (d) => `
            M${d3.pointRadial(x(d * 60), innerRadius)}
            L${d3.pointRadial(x(d * 60), outerRadius)}
          `))
      .call((g) => g.append("path")
          .attr("id", (d) => `hour-${d}`)
          .datum((d) => [d * 60, (d + 1) * 60])
          .attr("fill", "none")
          .attr("d", ([a, b]) => `
            M${d3.pointRadial(x(a), innerRadius-10)}
            A${innerRadius},${innerRadius} 0,0,1 ${d3.pointRadial(x(b), innerRadius)}
          `))
      .call((g) => g.append("text")
        .append("textPath")
          .attr("startOffset", 6)
          .attr("href", (d) => `#hour-${d}`)
          .text((d) => d.toString().padStart(2, "0"))
          .attr("text-anchor", "middle")
          .attr("alignment-baseline", "middle")
          .attr("transform", (d) => {
            const angle = x(d * 60);
            const radius = innerRadius - 20; // reduce the radius to move the labels closer to the center
                        return `translate(${d3.pointRadial(x(d * 60), radius)})`;
          })
      )
  );



      const yAxis = g => g
        .attr("text-anchor", "middle")
        .attr("font-family", "sans-serif")
        .attr("font-size", 10)
        .call(g => g.selectAll("g")
          .data(y.ticks().reverse())
          .join("g")
          .attr("fill", "none")
          .call(g => g.append("circle")
            .attr("stroke", "#000")
            .attr("stroke-opacity", 0.2)
            .attr("r", y))
          .call(g => g.append("text")
            .attr("y", d => -y(d))
            .attr("dy", "0.35em")
            .attr("stroke", "#fff")
            .attr("stroke-width", 5)
            .text((x, i) => `${x.toFixed(0)}${i ? "" : ""}`)
          .clone(true)
            .attr("y", d => y(d))
          .selectAll(function() { return [this, this.previousSibling]; })
          .clone(true)
            .attr("fill", "currentColor")
            .attr("stroke", "none")));

      const line = d3.lineRadial()
                    .curve(d3.curveLinear)
                    .angle(d => x(d.minuteTime));

      const area = d3.areaRadial()
                    .curve(d3.curveLinear)
                    .angle(d => x(d.minuteTime))

      const svg = d3.create("svg")
        .attr("viewBox", [-width / 2, -height / 2, width, height])
        .attr("stroke-linejoin", "round")
        .attr("stroke-linecap", "round");

      svg.append("path")
        .attr("fill", "none")
        .attr("stroke", "red")
        .attr("stroke-width", 3)
        .attr("d", line
          .radius(d => y(d.VALUE1))
          (rawdata2));

      svg.append("path")
        .attr("fill", "none")
        .attr("stroke", "green")
        .attr("stroke-width", 3)
        .attr("d", line
          .radius(d => y(d.VALUE2))
          (rawdata2));
// +++++++++++++++++++++++++++++++++++ calc crossing points ++++++++++++++++++++++++++++++++
function findCrossingPoints(data) {
  const crossingPoints = [];

  for (let i = 1; i < data.length; i++) {
    const d1 = data[i - 1];
    const d2 = data[i];

    if ((d1.VALUE1 - d1.VALUE2) * (d2.VALUE1 - d2.VALUE2) < 0) {
      // There's a crossing point between the two data points
      const t = (0 - (d1.VALUE1 - d1.VALUE2)) / ((d2.VALUE1 - d2.VALUE2) - (d1.VALUE1 - d1.VALUE2));
      const crossingValue1 = d1.VALUE1 + t * (d2.VALUE1 - d1.VALUE1);
      const crossingValue2 = d1.VALUE2 + t * (d2.VALUE2 - d1.VALUE2);
      crossingPoints.push({ VALUE1: crossingValue1, VALUE2: crossingValue2 });
    }
  }

  return crossingPoints;
}

const crossingPoints = findCrossingPoints(rawdata2);







// +++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++ ++++++++++++++++++++++++++++++++



// separate data in positive and negative value
let separated = rawdata2.reduce((a, b,index) => {
    let sub = b.VALUE1 - b.VALUE2;
    if (sub > 0) {
        return {positive: [...a.positive, {...b,index}], negative: [...a.negative]};
    }
    return {positive: [...a.positive], negative: [...a.negative, {...b,index}]};

}, {positive: [], negative: []})

const positiveSets = [];

let previous = undefined

separated.positive.forEach((e)=>{
    //if not contiguous
    if (!previous || previous.index + 1 !== e.index) {
        // create a new set
        positiveSets.push([e]);
    } else {
        // append value to previous set
        positiveSets[positiveSets.length - 1] = [...positiveSets[positiveSets.length - 1], e]
    }
    previous = e;
})
//same for negatives
const negativeSets = [];
previous = undefined
separated.negative.forEach((e)=>{
    if (!previous || previous.index + 1 !== e.index) {
        negativeSets.push([e]);
    } else {
        negativeSets[negativeSets.length - 1] = [...negativeSets[negativeSets.length - 1], e]
    }
    previous = e;
})

const posG = svg.selectAll('.positive').data(positiveSets)
posG.enter().append('path')
    .attr('class','positive')
    .attr('fill','darkblue') // "positive" color
    .attr("d", (d)=>area
        .innerRadius(d => y(d.VALUE1))
        .outerRadius(d => y(d.VALUE2))
        (d));

const negG = svg.selectAll('.negative').data(negativeSets)
negG.enter().append('path')
    .attr('class','negative') // "negative" color
    .attr('fill','lightblue')
    .attr("d", (d)=>area
        .innerRadius(d => y(d.VALUE1))
        .outerRadius(d => y(d.VALUE2))
        (d));

      svg.append("g")
        .call(xAxis);

      svg.append("g")
        .call(yAxis);

      // console.log(negG)

      const container = d3.select("body").append("div");
      container.node().appendChild(svg.node());
#csvdata {
    display: none;
}
  <script src="https://d3js.org/d3.v7.min.js"></script>
  <h1>D3 CSV Example</h1>


Solution

  • Here you go:

    const rawdata = [{
        "DATE_TIME": "2018-10-31T23:00:00.000Z",
        "VALUE1": 160,
        "VALUE2": 110,
        "INSPECTION": 1,
        "minuteTime": 0
      },
      {
        "DATE_TIME": "2018-10-31T23:20:00.000Z",
        "VALUE1": 102,
        "VALUE2": 118,
        "INSPECTION": 1,
        "minuteTime": 20
      },
      {
        "DATE_TIME": "2018-10-31T23:40:00.000Z",
        "VALUE1": 153,
        "VALUE2": 117,
        "INSPECTION": 1,
        "minuteTime": 40
      },
      {
        "DATE_TIME": "2018-11-01T00:00:00.000Z",
        "VALUE1": 112,
        "VALUE2": 117,
        "INSPECTION": 1,
        "minuteTime": 60
      },
      {
        "DATE_TIME": "2018-11-01T00:20:00.000Z",
        "VALUE1": 95,
        "VALUE2": 103,
        "INSPECTION": 1,
        "minuteTime": 80
      },
      {
        "DATE_TIME": "2018-11-01T00:40:00.000Z",
        "VALUE1": 157,
        "VALUE2": 119,
        "INSPECTION": 1,
        "minuteTime": 100
      },
      {
        "DATE_TIME": "2018-11-01T01:00:00.000Z",
        "VALUE1": 129,
        "VALUE2": 120,
        "INSPECTION": 1,
        "minuteTime": 120
      },
      {
        "DATE_TIME": "2018-11-01T01:20:00.000Z",
        "VALUE1": 135,
        "VALUE2": 108,
        "INSPECTION": 1,
        "minuteTime": 140
      },
      {
        "DATE_TIME": "2018-11-01T01:40:00.000Z",
        "VALUE1": 126,
        "VALUE2": 119,
        "INSPECTION": 1,
        "minuteTime": 160
      },
      {
        "DATE_TIME": "2018-11-01T02:00:00.000Z",
        "VALUE1": 121,
        "VALUE2": 110,
        "INSPECTION": 1,
        "minuteTime": 180
      },
      {
        "DATE_TIME": "2018-11-01T02:20:00.000Z",
        "VALUE1": 133,
        "VALUE2": 101,
        "INSPECTION": 1,
        "minuteTime": 200
      },
      {
        "DATE_TIME": "2018-11-01T02:40:00.000Z",
        "VALUE1": 124,
        "VALUE2": 120,
        "INSPECTION": 1,
        "minuteTime": 220
      },
      {
        "DATE_TIME": "2018-11-01T03:00:00.000Z",
        "VALUE1": 121,
        "VALUE2": 112,
        "INSPECTION": 1,
        "minuteTime": 240
      },
      {
        "DATE_TIME": "2018-11-01T03:20:00.000Z",
        "VALUE1": 147,
        "VALUE2": 108,
        "INSPECTION": 1,
        "minuteTime": 260
      },
      {
        "DATE_TIME": "2018-11-01T03:40:00.000Z",
        "VALUE1": 98,
        "VALUE2": 102,
        "INSPECTION": 1,
        "minuteTime": 280
      },
      {
        "DATE_TIME": "2018-11-01T04:00:00.000Z",
        "VALUE1": 136,
        "VALUE2": 109,
        "INSPECTION": 1,
        "minuteTime": 300
      },
      {
        "DATE_TIME": "2018-11-01T04:20:00.000Z",
        "VALUE1": 130,
        "VALUE2": 112,
        "INSPECTION": 1,
        "minuteTime": 320
      },
      {
        "DATE_TIME": "2018-11-01T04:40:00.000Z",
        "VALUE1": 156,
        "VALUE2": 100,
        "INSPECTION": 1,
        "minuteTime": 340
      },
      {
        "DATE_TIME": "2018-11-01T05:00:00.000Z",
        "VALUE1": 135,
        "VALUE2": 102,
        "INSPECTION": 1,
        "minuteTime": 360
      },
      {
        "DATE_TIME": "2018-11-01T05:20:00.000Z",
        "VALUE1": 112,
        "VALUE2": 120,
        "INSPECTION": 1,
        "minuteTime": 380
      },
      {
        "DATE_TIME": "2018-11-01T05:40:00.000Z",
        "VALUE1": 116,
        "VALUE2": 103,
        "INSPECTION": 1,
        "minuteTime": 400
      },
      {
        "DATE_TIME": "2018-11-01T06:00:00.000Z",
        "VALUE1": 129,
        "VALUE2": 107,
        "INSPECTION": 1,
        "minuteTime": 420
      },
      {
        "DATE_TIME": "2018-11-01T06:20:00.000Z",
        "VALUE1": 107,
        "VALUE2": 118,
        "INSPECTION": 1,
        "minuteTime": 440
      },
      {
        "DATE_TIME": "2018-11-01T06:40:00.000Z",
        "VALUE1": 112,
        "VALUE2": 118,
        "INSPECTION": 1,
        "minuteTime": 460
      },
      {
        "DATE_TIME": "2018-11-01T07:00:00.000Z",
        "VALUE1": 116,
        "VALUE2": 113,
        "INSPECTION": 1,
        "minuteTime": 480
      },
      {
        "DATE_TIME": "2018-11-01T07:20:00.000Z",
        "VALUE1": 92,
        "VALUE2": 109,
        "INSPECTION": 1,
        "minuteTime": 500
      },
      {
        "DATE_TIME": "2018-11-01T07:40:00.000Z",
        "VALUE1": 92,
        "VALUE2": 120,
        "INSPECTION": 1,
        "minuteTime": 520
      },
      {
        "DATE_TIME": "2018-11-01T08:00:00.000Z",
        "VALUE1": 112,
        "VALUE2": 101,
        "INSPECTION": 1,
        "minuteTime": 540
      },
      {
        "DATE_TIME": "2018-11-01T08:20:00.000Z",
        "VALUE1": 126,
        "VALUE2": 105,
        "INSPECTION": 1,
        "minuteTime": 560
      },
      {
        "DATE_TIME": "2018-11-01T08:40:00.000Z",
        "VALUE1": 135,
        "VALUE2": 108,
        "INSPECTION": 1,
        "minuteTime": 580
      },
      {
        "DATE_TIME": "2018-11-01T09:40:00.000Z",
        "VALUE1": 108,
        "VALUE2": 115,
        "INSPECTION": 1,
        "minuteTime": 640
      },
      {
        "DATE_TIME": "2018-11-01T10:40:00.000Z",
        "VALUE1": 112,
        "VALUE2": 114,
        "INSPECTION": 1,
        "minuteTime": 700
      },
      {
        "DATE_TIME": "2018-11-01T11:40:00.000Z",
        "VALUE1": 107,
        "VALUE2": 114,
        "INSPECTION": 1,
        "minuteTime": 760
      },
      {
        "DATE_TIME": "2018-11-01T12:40:00.000Z",
        "VALUE1": 110,
        "VALUE2": 115,
        "INSPECTION": 1,
        "minuteTime": 820
      },
      {
        "DATE_TIME": "2018-11-01T13:40:00.000Z",
        "VALUE1": 101,
        "VALUE2": 101,
        "INSPECTION": 1,
        "minuteTime": 880
      },
      {
        "DATE_TIME": "2018-11-01T14:40:00.000Z",
        "VALUE1": 145,
        "VALUE2": 118,
        "INSPECTION": 1,
        "minuteTime": 940
      },
      {
        "DATE_TIME": "2018-11-01T15:40:00.000Z",
        "VALUE1": 100,
        "VALUE2": 107,
        "INSPECTION": 1,
        "minuteTime": 1000
      },
      {
        "DATE_TIME": "2018-11-01T16:40:00.000Z",
        "VALUE1": 155,
        "VALUE2": 107,
        "INSPECTION": 1,
        "minuteTime": 1060
      },
      {
        "DATE_TIME": "2018-11-01T17:40:00.000Z",
        "VALUE1": 132,
        "VALUE2": 101,
        "INSPECTION": 1,
        "minuteTime": 1120
      },
      {
        "DATE_TIME": "2018-11-01T18:40:00.000Z",
        "VALUE1": 110,
        "VALUE2": 112,
        "INSPECTION": 1,
        "minuteTime": 1180
      },
      {
        "DATE_TIME": "2018-11-01T19:40:00.000Z",
        "VALUE1": 160,
        "VALUE2": 119,
        "INSPECTION": 1,
        "minuteTime": 1240
      },
      {
        "DATE_TIME": "2018-11-01T20:40:00.000Z",
        "VALUE1": 127,
        "VALUE2": 104,
        "INSPECTION": 1,
        "minuteTime": 1300
      },
      {
        "DATE_TIME": "2018-11-01T21:40:00.000Z",
        "VALUE1": 150,
        "VALUE2": 108,
        "INSPECTION": 1,
        "minuteTime": 1360
      },
      {
        "DATE_TIME": "2018-11-01T22:40:00.000Z",
        "VALUE1": 108,
        "VALUE2": 111,
        "INSPECTION": 1,
        "minuteTime": 1420
      },
      {
        "DATE_TIME": "2018-11-01T22:59:00.000Z",
        "VALUE1": 151,
        "VALUE2": 102,
        "INSPECTION": 1,
        "minuteTime": 1439
      }
    ];
    
    function checkLineIntersection(line1StartX, line1StartY, line1EndX, line1EndY, line2StartX, line2StartY, line2EndX, line2EndY) {
      // if the lines intersect, the result contains the x and y of the intersection (treating the lines as infinite) and booleans for whether line segment 1 or line segment 2 contain the point
      var denominator, a, b, numerator1, numerator2, result = {
        x: null,
        y: null,
        onLine1: false,
        onLine2: false
      };
      denominator = ((line2EndY - line2StartY) * (line1EndX - line1StartX)) - ((line2EndX - line2StartX) * (line1EndY - line1StartY));
      if (denominator === 0) {
        return result;
      }
      a = line1StartY - line2StartY;
      b = line1StartX - line2StartX;
      numerator1 = ((line2EndX - line2StartX) * a) - ((line2EndY - line2StartY) * b);
      numerator2 = ((line1EndX - line1StartX) * a) - ((line1EndY - line1StartY) * b);
      a = numerator1 / denominator;
      b = numerator2 / denominator;
    
      // if we cast these lines infinitely in both directions, they intersect here:
      result.x = line1StartX + (a * (line1EndX - line1StartX));
      result.y = line1StartY + (a * (line1EndY - line1StartY));
      /*
              // it is worth noting that this should be the same as:
              x = line2StartX + (b * (line2EndX - line2StartX));
              y = line2StartX + (b * (line2EndY - line2StartY));
              */
      // if line1 is a segment and line2 is infinite, they intersect if:
      if (a > 0 && a < 1) {
        result.onLine1 = true;
      }
      // if line2 is a segment and line1 is infinite, they intersect if:
      if (b > 0 && b < 1) {
        result.onLine2 = true;
      }
      // if line1 and line2 are segments, they intersect if both of the above are true
      return result;
    }
    const width = 954;
    const height = width;
    const margin = 10;
    const innerRadius = width / 5;
    const outerRadius = width / 2 - margin;
    
    
    
    let rawdata2 = rawdata.filter(d => d.INSPECTION == 1);
    
    const minuteScale = d3.scaleLinear()
      .domain([0, 1439]) // 0 to 23:59 (24 hours * 60 minutes without -1 or else change to 1439 //- 1)
      .range([0, 2 * Math.PI]);
    
    const minuteTimeValues = rawdata2.map(d => d.minuteTime);
    
    const x = d3.scaleTime()
      .domain([0, 1439]) // Set the domain using the extent of the "minuteTime" values
      .range([0, 2 * Math.PI]); // Set the desired range
    
    console.log(rawdata)
    
    const minValue1 = d3.min(rawdata, d => d.VALUE1);
    const minValue2 = d3.min(rawdata, d => d.VALUE2);
    const maxValue1 = d3.max(rawdata, d => d.VALUE1);
    const maxValue2 = d3.max(rawdata, d => d.VALUE2);
    
    const yMin = minValue1 < minValue2 ? minValue1 : minValue2;
    const yMax = maxValue1 > maxValue2 ? maxValue1 : maxValue2;
    
    const y = d3.scaleLinear()
      .domain([yMin, yMax])
      .range([innerRadius, outerRadius]);
    
    // Modify the xAxis generator to display only the hour and add padding
    
    const xAxis = (g) => g
      .attr("font-family", "sans-serif")
      .attr("font-size", 10)
      .call((g) => g.selectAll("g")
        .data(d3.range(0, 24)) // create an array of hour values
        .join("g")
        .each((d, i) => d.id = `hour-${i}`)
        .call((g) => g.append("path")
          .attr("stroke", "#000")
          .attr("stroke-opacity", 0.2)
          .attr("d", (d) => `
                M${d3.pointRadial(x(d * 60), innerRadius)}
                L${d3.pointRadial(x(d * 60), outerRadius)}
              `))
        .call((g) => g.append("path")
          .attr("id", (d) => `hour-${d}`)
          .datum((d) => [d * 60, (d + 1) * 60])
          .attr("fill", "none")
          .attr("d", ([a, b]) => `
                M${d3.pointRadial(x(a), innerRadius-10)}
                A${innerRadius},${innerRadius} 0,0,1 ${d3.pointRadial(x(b), innerRadius)}
              `))
        .call((g) => g.append("text")
          .append("textPath")
          .attr("startOffset", 6)
          .attr("href", (d) => `#hour-${d}`)
          .text((d) => d.toString().padStart(2, "0"))
          .attr("text-anchor", "middle")
          .attr("alignment-baseline", "middle")
          .attr("transform", (d) => {
            const angle = x(d * 60);
            const radius = innerRadius - 20; // reduce the radius to move the labels closer to the center
            return `translate(${d3.pointRadial(x(d * 60), radius)})`;
          })
        )
      );
    
    
    
    const yAxis = g => g
      .attr("text-anchor", "middle")
      .attr("font-family", "sans-serif")
      .attr("font-size", 10)
      .call(g => g.selectAll("g")
        .data(y.ticks().reverse())
        .join("g")
        .attr("fill", "none")
        .call(g => g.append("circle")
          .attr("stroke", "#000")
          .attr("stroke-opacity", 0.2)
          .attr("r", y))
        .call(g => g.append("text")
          .attr("y", d => -y(d))
          .attr("dy", "0.35em")
          .attr("stroke", "#fff")
          .attr("stroke-width", 5)
          .text((x, i) => `${x.toFixed(0)}${i ? "" : ""}`)
          .clone(true)
          .attr("y", d => y(d))
          .selectAll(function() {
            return [this, this.previousSibling];
          })
          .clone(true)
          .attr("fill", "currentColor")
          .attr("stroke", "none")));
    
    const line = d3.lineRadial()
      .curve(d3.curveLinear)
      .angle(d => x(d.minuteTime));
    
    const area = d3.areaRadial()
      .curve(d3.curveLinear)
      .angle(d => x(d.minuteTime))
    
    const svg = d3.create("svg")
      .attr("viewBox", [-width / 2, -height / 2, width, height])
      .attr("stroke-linejoin", "round")
      .attr("stroke-linecap", "round");
    
    svg.append("path")
      .attr("fill", "none")
      .attr("stroke", "red")
      .attr("stroke-width", 3)
      .attr("d", line
        .radius(d => y(d.VALUE1))
        (rawdata2));
    
    svg.append("path")
      .attr("fill", "none")
      .attr("stroke", "green")
      .attr("stroke-width", 3)
      .attr("d", line
        .radius(d => y(d.VALUE2))
        (rawdata2));
    // +++++++++++++++++++++++++++++++++++ calc crossing points ++++++++++++++++++++++++++++++++
    function findCrossingPoints(data) {
      const crossingPoints = [];
    
      for (let i = 1; i < data.length; i++) {
        const d1 = data[i - 1];
        const d2 = data[i];
    
        if ((d1.VALUE1 - d1.VALUE2) * (d2.VALUE1 - d2.VALUE2) < 0) {
          // There's a crossing point between the two data points
          const t = (0 - (d1.VALUE1 - d1.VALUE2)) / ((d2.VALUE1 - d2.VALUE2) - (d1.VALUE1 - d1.VALUE2));
          const crossingValue1 = d1.VALUE1 + t * (d2.VALUE1 - d1.VALUE1);
          const crossingValue2 = d1.VALUE2 + t * (d2.VALUE2 - d1.VALUE2);
          crossingPoints.push({
            VALUE1: crossingValue1,
            VALUE2: crossingValue2
          });
        }
      }
    
      return crossingPoints;
    }
    
    const crossingPoints = findCrossingPoints(rawdata2);
    
    function getX(d) {
      return Number(line.radius(d => y(d.VALUE1))([d]).match('M(-?[0-9.]*),(-?[0-9.]*)')[1]);
    }
    
    function getY(d) {
      return Number(line.radius(d => y(d.VALUE1))([d]).match('M(-?[0-9.]*),(-?[0-9.]*)')[2]);
    }
    
    function getX2(d) {
      return Number(line.radius(d => y(d.VALUE2))([d]).match('M(-?[0-9.]*),(-?[0-9.]*)')[1]);
    }
    
    function getY2(d) {
      return Number(line.radius(d => y(d.VALUE2))([d]).match('M(-?[0-9.]*),(-?[0-9.]*)')[2]);
    }
    
    
    
    
    
    
    // +++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++ ++++++++++++++++++++++++++++++++
    
    
    
    // separate data in positive and negative value
    let separated = rawdata2.reduce((a, b, index) => {
      let sub = b.VALUE1 - b.VALUE2;
      if (sub > 0) {
        return {
          positive: [...a.positive, { ...b,
            index
          }],
          negative: [...a.negative]
        };
      }
      return {
        positive: [...a.positive],
        negative: [...a.negative, { ...b,
          index
        }]
      };
    
    }, {
      positive: [],
      negative: []
    })
    
    let positiveSets = [];
    
    let previous = undefined
    
    separated.positive.forEach((e) => {
      //if not contiguous
      if (!previous || previous.index + 1 !== e.index) {
        // create a new set
        positiveSets.push([e]);
      } else {
        // append value to previous set
        positiveSets[positiveSets.length - 1] = [...positiveSets[positiveSets.length - 1], e]
      }
      previous = e;
    })
    //same for negatives
    let negativeSets = [];
    previous = undefined
    separated.negative.forEach((e) => {
      if (!previous || previous.index + 1 !== e.index) {
        negativeSets.push([e]);
      } else {
        negativeSets[negativeSets.length - 1] = [...negativeSets[negativeSets.length - 1], e]
      }
      previous = e;
    })
    
    negativeSets = negativeSets.map(s => {
    
      let first = s[0];
      let last = s[s.length - 1];
      let next;
      let prev;
      if (first.index > 0) {
        prev = rawdata2[first.index - 1]
      } else {
    
        prev = rawdata2[rawdata2.length - 1]
      }
      if (last.index === rawdata2.length - 1) {
        next = rawdata2[0];
      } else {
        next = rawdata2[last.index + 1];
      }
    
    
      let w = checkLineIntersection(getX(prev), getY(prev), getX(first), getY(first), getX2(prev), getY2(prev), getX2(first), getY2(first))
      let n = checkLineIntersection(getX(last), getY(last), getX(next), getY(next), getX2(last), getY2(last), getX2(next), getY2(next))
    
      return [{
        x1: w.x,
        y1: w.y,
        x2: w.x,
        y2: w.y
      }, ...s.map(w => ({
        x1: getX(w),
        y1: getY(w),
        x2: getX2(w),
        y2: getY2(w)
      })), {
        x1: n.x,
        y1: n.y,
        x2: n.x,
        y2: n.y
      }]
    
    })
    
    positiveSets = positiveSets.map(s => {
    
      let first = s[0];
      let last = s[s.length - 1];
      let next;
      let prev;
      if (first.index > 0) {
        prev = rawdata2[first.index - 1]
      } else {
    
        prev = rawdata2[rawdata2.length - 1]
      }
      if (last.index === rawdata2.length - 1) {
        next = rawdata2[0];
      } else {
        next = rawdata2[last.index + 1];
      }
    
    
      let w = checkLineIntersection(getX(prev), getY(prev), getX(first), getY(first), getX2(prev), getY2(prev), getX2(first), getY2(first))
      let n = checkLineIntersection(getX(last), getY(last), getX(next), getY(next), getX2(last), getY2(last), getX2(next), getY2(next))
    
      return [{
        x1: w.x,
        y1: w.y,
        x2: w.x,
        y2: w.y
      }, ...s.map(w => ({
        x1: getX(w),
        y1: getY(w),
        x2: getX2(w),
        y2: getY2(w)
      })), {
        x1: n.x,
        y1: n.y,
        x2: n.x,
        y2: n.y
      }]
    
    })
    const negG = svg.selectAll('.negative').data(negativeSets)
    negG.enter().append('path')
      .attr('class', 'negative') // "negative" color
      .attr('fill', 'lightblue')
      .attr("d", (d) => d3.area().x0(d => d.x1).x1(d => d.x2).y0(d => d.y1).y1(d => d.y2)(d));
    
    
    const posG = svg.selectAll('.positive').data(positiveSets)
    posG.enter().append('path')
      .attr('class', 'positive') // "negative" color
      .attr('fill', 'darkblue')
      .attr("d", (d) => d3.area().x0(d => d.x1).x1(d => d.x2).y0(d => d.y1).y1(d => d.y2)(d));
    
    
    svg.append("g")
      .call(xAxis);
    
    svg.append("g")
      .call(yAxis);
    
    
    
    // console.log(negG)
    
    const container = d3.select("body").append("div");
    container.node().appendChild(svg.node());
    #csvdata {
      display: none;
    }
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <h1>D3 CSV Example</h1>

    JSFiddle Example

    checkLineIntersection(...) compute crossing point for given segment

    yes, it took me 9 days to understand and do it:

    What I did (in an awful manner), I calculated crossing point of every extremity of negative and positive set, created a new set of point for area and plotted those areas.

    My first idea was to compute actual data entry of every crossing point, but I discovered that they do not match crossing point in polar coordinate.

    I don't know if this is any use to you.

    The only thing I learned is : You can't interpolate between two point in polar coordinate with straight lines.

    Good luck