1

I'm using javascript and d3 (v6) and working towards making a sunburst type plot or at least a plot with arcs at different radial distances where each arc has potentially its own gradient (a radial gradient from center to periphery). At this point I've followed some examples (e.g. here, here) where I set up a set of radialGradient elements and then use the ids from that to generate the fill in my actual arcs. But I am generating my arcs and gradients from data from a json file, and this makes it different from previous questions because the svg element generation is within the d3.json call (e.g. here). I have two problems. (1) The gradient setups have lines like:

grads.append("stop")
        .attr("offset", "0%").style("stop-color", "white")
        .attr("offset", "100%").style("stop-color", "green");

but these don't seem to actually get added to the grads elements (when I look at the inspector in the webpage).
(2) The link to the arcs doesn't seem to work, although perhaps that is because of the first problem.

JAVASCRIPT (can be found here)

var width = window.innerWidth,
    height = window.innerHeight;

var body = d3.select('body')

// append svg to the DIV
chart = d3.select(".chart");

const svg = chart.append("svg:svg")
    .attr("width", width)
    .attr("height", height);

///////////////////////////////////////  Global variables controlling the arc appearance //////////////////
const arcMin = 30;
const arcWidth = 45.5;
const arcPad = 1;
///////////////////////////////////////////////////////////////////////////////////////////////

d3.json('dataExample.json')
    .then(function (data) {

        const grads = svg.append("defs").selectAll("radialGradient").data(data.sequences);
        grads.enter().append("radialGradient")
            .attr("gradientUnits", "objectBoundingBox")
            .attr("cx", 0)
            .attr("cy", 0)
            .attr("fr", (d, i) => arcMin + (d.pulse-1) * (arcWidth))
            .attr("r", (d, i) => arcMin + d.pulse * (arcWidth))
            .attr("id", function (d) {
                return "grad" + d.code;
            });
        grads.append("stop")
            .attr("offset", "0%").style("stop-color", "white")
            .attr("offset", "100%").style("stop-color", "green");//eventually this gradient will go between two colors that are functions of the data that is read in from the json file

        console.log(grads);

        var arc = svg.selectAll('path.arc-path')
            .data(data.sequences);
        arc.enter()
            .append('svg:path')
            .attr('d', d3.arc()
                .innerRadius((d, i) => arcMin + (d.pulse - 1) * (arcWidth) + arcPad)
                .outerRadius((d, i) => arcMin + d.pulse * (arcWidth))
                .startAngle(function (d, i) {
                    ang = (i * 30) * Math.PI / 180;
                    return ang;
                })
                .endAngle(function (d, i) {
                    ang = ((i + 1) * 30) * Math.PI / 180;
                    return ang;
                })
            )
            .attr("class", ".arc-path") // assigns a class for easier selecting
            .attr("transform", "translate(600,300)") 
            //.style('fill',(d) => `rgb(${d.code * 10},${d.code*20},${255 -d.code * 7})`); this works - but doesn't use the gradients
            .style("fill", function (d) {return "url(#grad" + d.code + ")";})


    })

JSONFILE (called dataExample.json above) can be found here

{"type":"sequenceData","sequences":[{"pulse":1,"code":0},{"pulse":1,"code":1},{"pulse":1,"code":2},{"pulse":2,"code":3},{"pulse":2,"code":4},{"pulse":2,"code":5},{"pulse":2,"code":6},{"pulse":2,"code":7},{"pulse":2,"code":8},{"pulse":2,"code":9},{"pulse":2,"code":10},{"pulse":3,"code":12}]}

index.html (can be found here)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Gradient arc test</title>
    </style>
    <script type="text/javascript" src="https://d3js.org/d3.v6.min.js"></script>
  </head>
  <body>
    <div class="chart"></div>
        <script src="arcExample.js">    </script>
  </body>
</html>

And if there are other problems or bad coding practices you notice, please let me know since I'm new to this. Thanks.

(edited)

Based on Ruben Helsloot's comment I made some jsfiddles with the various parts. There are 4 fiddles. They differ in 2 aspects: (1) how the json file is read in and (2) how the fill is generated. I'll note that for my final purpose I need to be able to read the json file from a file - either local or from a URL
(1) jsfiddle Version 1 Attempts to read json from a URL and attempts to use gradients for the fill. I think there is a problem reading from the url as well as a problem with the gradient filling.
(2) jsfiddle Version 2 Attempts to read json from a URL but does not use gradients for the fill. This has problems with reading the URL. When I do this on my local machine reading the json from a local file, this version does generate an output (although not gradients).
(3) jsfiddle Version 3 This puts the json in a local variable called data and then uses that. It also tries to use the gradients for filling.
(4) jsfiddle Version 4 This puts the json in a local variable called data and then uses that. It does not use gradients for filling. This is the only one of the 4 that gives the output on jsfiddle.

sar
  • 23
  • 4
  • Please turn your code into a runnable [mre], it will help us see what's going on. YOu can host your JSON file on github or just store it as the variable `data` and remove the `d3.json` call – Ruben Helsloot Nov 18 '20 at 09:04
  • I edited the post with links to 4 jsfiddles with variants that show the problem. There is a problem reading from the URL using d3.json. Ultimately, I need to be able to read from a file or URL instead of having the data in the javascript file itself. I also posted the index.html, arcExample.js, and dataExample.json file at gitlab (links in the question) for local testing. – sar Nov 18 '20 at 19:06

1 Answers1

0

You should learn basic SVG 1.1 specification first and then how to add elements (not attributes) to the correct SVG structure.

Below is fixed code though I don't know exactly what gradient do you want to achieve. You can try to change the gradientUnits attribute back to objectBoundingBox but first experiment with the r attribute as well.

var width = window.innerWidth,
    height = window.innerHeight;

var body = d3.select('body')

// append svg to the DIV
chart = d3.select(".chart");

const svg = chart.append("svg:svg")
    .attr("width", width)
    .attr("height", height)
    .attr("xmlns", "http://www.w3.org/2000/svg")
    .attr("xmlns:xlink", "http://www.w3.org/1999/xlink");

///////////////////////////////////////  Global variables controlling the arc appearance //////////////////
const arcMin = 30;
const arcWidth = 45.5;
const arcPad = 1;
///////////////////////////////////////////////////////////////////////////////////////////////

var data = {"type":"sequenceData","sequences":[{"pulse":1,"code":0},{"pulse":1,"code":1},{"pulse":1,"code":2},{"pulse":2,"code":3},{"pulse":2,"code":4},{"pulse":2,"code":5},{"pulse":2,"code":6},{"pulse":2,"code":7},{"pulse":2,"code":8},{"pulse":2,"code":9},{"pulse":2,"code":10},{"pulse":3,"code":12}]};

        const grads = svg.append("defs").selectAll("radialGradient").data(data.sequences);
        gradsWrap = grads.enter().append("radialGradient")
            //.attr("gradientUnits", "objectBoundingBox")
            .attr("gradientUnits", "userSpaceOnUse")
            .attr("cx", 0)
            .attr("cy", 0)
            //.attr("fr", (d, i) => arcMin + (d.pulse-1) * (arcWidth))
            //.attr("r", (d, i) => arcMin + (d.pulse-1) * (arcWidth))
            .attr("fr", "0%")
            .attr("r", "25%")
            .attr("id", function (d) {
                return "grad" + d.code;
            })
        gradsWrap.append("stop")
            .attr("offset", "0%")
            .attr("stop-color", "white");
        gradsWrap.append("stop")
            .attr("offset", "100%").attr("stop-color", "green");//eventually this gradient will go between two colors that are functions of the data that is read in from the json file

        console.log(grads);

        var arc = svg.selectAll('path.arc-path')
            .data(data.sequences);
        arc.enter()
            .append('svg:path')
            .attr('d', d3.arc()
                .innerRadius((d, i) => arcMin + (d.pulse - 1) * (arcWidth) + arcPad)
                .outerRadius((d, i) => arcMin + d.pulse * (arcWidth))
                .startAngle(function (d, i) {
                    ang = (i * 30) * Math.PI / 180;
                    return ang;
                })
                .endAngle(function (d, i) {
                    ang = ((i + 1) * 30) * Math.PI / 180;
                    return ang;
                })
            )
            .attr("class", ".arc-path") // assigns a class for easier selecting
            .attr("transform", "translate(" + width/2 + "," + height/2 + ")")
            .attr('stroke', 'white')
            .style('stroke-width', '2px')
            .attr("fill", function (d) {
              //return "red";
              return "url(#grad" + d.code + ")";
            })
<head>
  <meta charset="utf-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
  <title>Gradient arc test</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
</head>

<body>
  <div class="chart"></div>
</body>
Matt Sergej Rinc
  • 565
  • 3
  • 11
  • Based on your code and the example I based my code on [here](https://stackoverflow.com/a/32566727), I also tried removing the semicolon after `const grads = svg.append("defs").selectAll("radialGradient").data(data.sequences)` and going straight into the `.enter().append("radialGradient")...` part instead of adding the additional gradsWrap variable and it worked as well. This clarifies a lot. – sar Nov 18 '20 at 19:36
  • Sure, you just have to distinguish if you are adding the attributes or subelements to one SVG element. In the later case and often requiring several subelements the way to define a "wrapping" (variable) element is practical. – Matt Sergej Rinc Nov 18 '20 at 22:17