Home > Software engineering >  d3 to create paths based on separate array of data
d3 to create paths based on separate array of data

Time:07-07

I am trying to create svg paths with d3 and trying to understand how can I ask d3 to create paths based on separate data.

For example, here I have fixed data for path Value and two separate sets as index1 and index2. I want to create paths based on these, meaning I want to create 11 paths altogether, whereas the code below creates only 8 paths.

const width = 1280;
height = 720;

svg = d3.select('svg')
    .attr('xmlns', 'http://www.w3.org/2000/svg')
    .attr('viewBox', `0 0 ${width} ${height}`)

rect = svg.append('rect')
    .attr('x', '0')
    .attr('y', '0')
    .attr('width', `${width}`)
    .attr('height', `${height}`)
    .attr('fill', `#EFEFEF`)

//src
dataHorizontal = [
    { x: 0, y: 0 },
    { x: 1280, y: 0 }
];

pathVal = d3.line()
    .x(d => d.x)
    .y(d => d.y)
    (dataHorizontal);

index1 = [1, 2, 3];

index2 = [4, 5, 6, 7, 8, 9, 10, 11]

multiplier = 60;

//based on index1

svg.selectAll('path')
    .data(index1)
    .join('path')
    .attr('class', (d) => `ln${d}`)
    .attr('d', pathVal)
    .attr('stroke', () => { return `hsla(${Math.random() * 360} , ${((Math.random()   Math.random()/2)*100).toFixed(2)}%, 50%, 1)`; })
    .style('transform', (d, i) => { return `translateY(${d*multiplier}px)` })
    .attr('stroke-width', '2')
   


//based on index2

svg.selectAll('path')
    .data(index2)
    .join('path')
    .attr('class', (d) => `ln${d}`)
    .attr('d', pathVal)
    .attr('stroke', () => { return `hsla(${Math.random() * 360} , ${((Math.random()   Math.random()/2)*100).toFixed(2)}%, 50%, 1)`; })
    .style('transform', (d, i) => { return `translateY(${d*multiplier}px)` })
    .attr('stroke-width', '2')
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>
<body>

    <link rel="stylesheet" href="style.css"></link>    
    <div id="container" ></div>
    <svg>
    </svg>
    <!--d3 script-->
    <script src="prod.js"></script>
</body>

</html>

My best guess is, d3 is probably overwriting the first 3 paths from index1 when it starts working on path2 as the first 3 paths were already available in the DOM.

How can I ask d3 to ignore the first 3 paths that are available in the DOM and start from scratch for index2? I don't want to combine index1 and index 2. What is a d3 way to reach the desired output?

I know I can do this, but is this the only way?

const width = 1280;
height = 720;

svg = d3.select('svg')
    .attr('xmlns', 'http://www.w3.org/2000/svg')
    .attr('viewBox', `0 0 ${width} ${height}`)

rect = svg.append('rect')
    .attr('x', '0')
    .attr('y', '0')
    .attr('width', `${width}`)
    .attr('height', `${height}`)
    .attr('fill', `#EFEFEF`)

//src
dataHorizontal = [
    { x: 0, y: 0 },
    { x: 1280, y: 0 }
];

pathVal = d3.line()
    .x(d => d.x)
    .y(d => d.y)
    (dataHorizontal);

index1 = [1, 2, 3];

index2 = [4, 5, 6, 7, 8, 9, 10, 11]

multiplier = 60;

//based on index1

svg.selectAll('path')
    .data(index1)
    .join('path')
    .attr('class', (d) => `ln${d}`)
    .attr('d', pathVal)
    .attr('stroke', () => { return `hsla(${Math.random() * 360} , ${((Math.random()   Math.random()/2)*100).toFixed(2)}%, 50%, 1)`; })
    .style('transform', (d, i) => { return `translateY(${d*multiplier}px)` })
    .attr('stroke-width', '2')
   


//based on index2
index2.forEach(
    (a) => {
        svg.append('path')
            .attr('class', () => `ln${a}`)
            .attr('d', pathVal)
            .attr('stroke', () => { return `hsla(${Math.random() * 360} , ${((Math.random()   Math.random()/2)*100).toFixed(2)}%, 50%, 1)`; })
            .style('transform', () => { return `translateY(${a*multiplier}px)` })
            .attr('stroke-width', '2')
    }
)
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>
<body>

    <link rel="stylesheet" href="style.css"></link>    
    <div id="container" ></div>
    <svg>
    </svg>
    <!--d3 script-->
    <script src="prod.js"></script>
</body>

</html>

CodePudding user response:

It is important for you to understand what does selection.join() mean in d3. Read this post: https://observablehq.com/@d3/selection-join

It is explained well in above post-If the joining selection isn’t empty—as on subsequent iterations of the loop above— selection.join appends entering elements and removes exiting elements to match the data!

You pass index1 as data and when you pass index2 as another set of data, join will remove the exiting elements. And only for entering elements, it will create a path. In case you want to create lines for both index1 and index2, combine the data and draw with join.

const width = 1280;
height = 720;

svg = d3.select('svg')
    .attr('xmlns', 'http://www.w3.org/2000/svg')
    .attr('viewBox', `0 0 ${width} ${height}`)

rect = svg.append('rect')
    .attr('x', '0')
    .attr('y', '0')
    .attr('width', `${width}`)
    .attr('height', `${height}`)
    .attr('fill', `#EFEFEF`)

//src
dataHorizontal = [
    { x: 0, y: 0 },
    { x: 1280, y: 0 }
];

pathVal = d3.line()
    .x(d => d.x)
    .y(d => d.y)
    (dataHorizontal);

index1 = [1, 2, 3];

index2 = [4, 5, 6, 7, 8, 9, 10, 11]

multiplier = 60;



//based on index2

svg.selectAll('path')
    .data([...index1,...index2])
    .join('path')
    .attr('class', (d) => `ln${d}`)
    .attr('d', pathVal)
    .attr('stroke', () => { return `hsla(${Math.random() * 360} , ${((Math.random()   Math.random()/2)*100).toFixed(2)}%, 50%, 1)`; })
    .style('transform', (d, i) => { return `translateY(${d*multiplier}px)` })
    .attr('stroke-width', '2')
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>
<body>

    <link rel="stylesheet" href="style.css"></link>    
    <div id="container" ></div>
    <svg>
    </svg>
    <!--d3 script-->
    <script src="prod.js"></script>
</body>

</html>

  • Related