Issue with multi line graph using D3 V6

Hello,

I’m having a problem with D3. Here’s what i would like to achieve : a multi line graph by cities (“ville”) with colours corresponding to categories.
I finally get a graph, tooltip and colours, but they don’t correspond to the data.
I believe the problem comes from my dataNest array but after multiple hours, trying to fix the array, the range and the domain of colours, i couldn’t find the right one.

Thanks for your answers

Here’s a sample of my data.csv file :
“ville,annee,taux,codeHexa
Arbonne la Forêt,2015,NaN,#65509C
Arbonne la Forêt,2016,NaN,#65509C
Arbonne la Forêt,2017,NaN,#65509C
Arbonne la Forêt,2018,9.13,#65509C
Arbonne la Forêt,2019,5.21,#65509C
Avon,2015,12.29,#65509C
Avon,2016,14.15,#65509C
Avon,2017,14.28,#65509C
Avon,2018,15.57,#65509C

And here’s my code :

<!DOCTYPE html>
<meta charset="utf-8">
<style> /* set the CSS */

body {
  font-family: "PT Sans";
}

path {
  stroke-width: 10px;
  fill: none;
  overflow: visible;
  opacity: 0.5;
}
.axis path,
.axis line {
  fill: none;
  /*stroke: grey;*/
  stroke-width: 0.2px;
  shape-rendering: crispEdges;
}

 .tooltip {
  position: absolute;
  text-align: center;
  /*display: none;*/
  padding: 5px;
  background: #50C688;
  opacity: 0.6;
  border: 0px;
  border-radius: 7px;
  pointer-events: none;
}

</style>
<body>

    <div id="container" height="500" width="960" , style="border:none"></>
    <!--<g id="body"></g>-->

</svg>

</body>

<!-- load the d3.js library --> 

<script src="https://d3js.org/d3.v6.min.js"></script>

 <script>


// set the dimensions and margins of the graph 
var margin = {top: 20, right: 20, bottom: 30, left: 50}, 
width = 960 - margin.left - margin.right, 
height = 500 - margin.top - margin.bottom;

// parse the date / time 
var parseTime = d3.timeParse("%Y");

// set the ranges 
var x = d3.scaleTime().range([0, width]); 
var y = d3.scaleLinear().range([height, 0]);

// define the line

var tauxRefus = d3.line()
  //line with missing data 
  .defined(d => !isNaN(d.taux))
  .x(d => x(d.annee))
  .y(d => y(d.taux));

// define the container width and height

var svg = d3.select("#container")
  .append("svg")
    .attr("width", width + margin.left +margin.right)
    .attr("height", height + margin.top + margin.bottom) 
  .append("g")
    .attr("transform", 
      "translate(" + margin.left + "," + margin.top + ")");

// define the tooltip 

var tooltip = d3.select("#container").append("div")
    .attr("class", "tooltip")
    .style("opacity", 0);

// Get the data 

d3.csv("SMICTOM_VilleAnneeTaux.csv").then(function(data) {
  data.forEach(function(d) {
  d.annee = parseTime(d.annee);
  d.taux = +d.taux;
  d.ville = d.ville;
  d.couleur = d.codeHexa;
});

// Scale the range of the data 

x.domain(d3.extent(data, function(d) { return d.annee; }));
y.domain([0, d3.max(data, function(d) { return d.taux; })]).nice();

// Grouper les entrées par nom de ville 

var dataNest = Array.from(
    d3.group(data, d => d.ville), ([ville, taux]) => ({ville, taux}))

  var couleur = d3.scaleOrdinal()
  .range(d3.map(dataNest, function(d) {return d.taux.couleur}))
  .domain(d3.map(dataNest, function(d) {return d.ville; }));

 // Boucle à travers chaque clef 

dataNest.forEach(function(d, i) {
  svg.append("path")
    .attr("class", "line")
    .style("stroke", function() {
      return d.couleur = couleur(i)
    })

    .attr("d", tauxRefus(d.taux))
            .on("mouseover", function(event, d) {
            tooltip.transition()
            
            tooltip.html("Ville : " + dataNest[i].ville + "<br>" + "Couleur : " + data[i].couleur)
              .style("left", event.clientX + "px")
              .style("top", event.clientY + 25 + "px")
              .style("display", "inline")
              d3.select(this).style("opacity", 1).style("stroke-width", 5)
            })
        
        .on("mousemove", function(event, d) {
          tooltip.transition()
            tooltip.html("Ville : " + dataNest[i].ville + "<br>" + "Année : " + data[i].couleur)
              .style("left", event.clientX + "px")
              .style("top", event.clientY + 25 + "px")
              .style("display", "inline")
              d3.select(this).style("opacity", 1)
          })
        .on("mouseleave", function(event, d) {
          tooltip.transition()
          .style("display", "none")
          .style("opacity", 0.8)
        d3.select(this).style("opacity", 0.5)
        .style("stroke-width", 1)
        })
});

// Add the X and Y axis

svg.append("g")
  .attr("class", "axis")
  .attr("transform", "translate(0," + height + ")")
  .style("font-family", "PT Sans")
  .call(d3.axisBottom(x).ticks(width / 80).tickSizeOuter(0));

svg.append("g")
  .attr("class", "axis")
  .style("font-family", "PT Sans")
  .call(d3.axisLeft(y))
  .call(g => g.select(".domain").remove())
  ;

// line with missing data

svg.append("path")
  .datum(dataNest.filter(tauxRefus.defined()))
  .attr("stroke", "grey")
  .attr("d", tauxRefus);

});

 </script> 

 </html>

PS : i’m a kind of a noob in D3 but working hard to fix it.

I finally got a chance to look at this. First, I rearranged your code so that the css, html, and js were in separate files and reordered some of your html. When I tried to load the page, I hit a CORS error (locally) on your d3.csv() call in the browser console, since fetch wants a http/https address. I worked around that.

I can only get a graph that looks like the data if I set do the stroke part like

dataNest.forEach(function(d, i) {
  svg.append("path")
    .attr("class", "line")
    .style("stroke", "#65590C")

I get a line plot that looks like the data, but I don’t know if it’s what you want.

You use forEach to iterate over your data, but d3 will handle that for you by using calls like

    const graph = svg.selectAll('rect')
          .data(data)
          .enter()
    ...

which will then place the current data item in any calls to functions to set the stroke, color, etc.

Codepen.

Thanks so much for your time and your answer. I’ve just seen it.
Here’s what i’ve succeded to achieve until now (Et si on améliorait le recyclage ?).
Colors are taken from the data, but they don’t fit to dataNest. So, i’m gonna try your suggestion : if my understanding is correct, i should use
svg.selectAll(‘path’)
and replace forEach to select all path and apply my attributes (colors from the data).
I’ll follow that lead and come back with some answers.

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.