简体   繁体   English

如何从 JSON 文件向 D3 js 旭日形图添加颜色?

[英]How to add colours to D3 js sunburst diagram from JSON-file?

I created a sunburst diagram based on this JSON file.我根据这个JSON 文件创建了一个旭日形图。 (existing quetions considered) (考虑现有问题)

The first few lines look like this:前几行如下所示:

{
   "name": "A", "children": [
      {
         "name": "B",
         "color": "#A9DFBF",
         "children": [
            {
               "name": "C",
               "color": "#F9E79F",
               "children": [
       ....
       ....
       ....

I am trying to use the colours from the JSON file in the sunburst: in this example everything has to be green besides one yellow arc.我正在尝试使用 sunburst 中 JSON 文件中的颜色:在这个例子中,除了一个黄色弧线之外,所有东西都必须是绿色的。 Instead my code (s. below) gives me the following sunburst visualizations with the two alternatives I tried:相反,我的代码(如下)为我提供了以下旭日形可视化效果,其中包含我尝试过的两种替代方案:

Alternative 1 of the relvant part gives:相关部分的备选方案 1 给出: 在此处输入图像描述 Alternative 2 of the relevant part (search for "alternative") gives:相关部分的备选方案 2(搜索“备选方案”)给出: 在此处输入图像描述

Would appriciate any help on this.会为此提供任何帮助。

Relevant part of code:代码的相关部分:

    newSlice.append('path')
        .attr('class', 'main-arc')
        //Alternative 1
        //.style('fill', d => color((d.children ? d : d.parent).data.name))
        //Alternative 2
        .style('fill', function (d) { return color(d.color);})
        .attr('d', arc);

Full code:完整代码:

<head>
    <style>
        body {
            font-family: Sans-serif;
            font-size: 11px;
        }

        .slice {
            cursor: pointer;
        }

        .slice .main-arc {
            stroke: #fff;
            stroke-width: 1px;
        }

        .slice .hidden-arc {
            fill: none;
        }

        .slice text {
            pointer-events: none;
            dominant-baseline: middle;
            text-anchor: middle;
        }
    </style>

    <script src='https://d3js.org/d3.v4.min.js'></script>
</head>
<body>
    <script>
        const width = window.innerWidth,
            height = window.innerHeight,
            maxRadius = (Math.min(width, height) / 2) - 5;

        const formatNumber = d3.format(',d');

        const x = d3.scaleLinear()
            .range([0, 2 * Math.PI])
            .clamp(true);

        const y = d3.scaleSqrt()
            .range([maxRadius*.1, maxRadius]);

        const color = d3.scaleOrdinal(d3.schemeCategory20);

        const partition = d3.partition();

        const arc = d3.arc()
            .startAngle(d => x(d.x0))
            .endAngle(d => x(d.x1))
            .innerRadius(d => Math.max(0, y(d.y0)))
            .outerRadius(d => Math.max(0, y(d.y1)));

        const middleArcLine = d => {
            const halfPi = Math.PI/2;
            const angles = [x(d.x0) - halfPi, x(d.x1) - halfPi];
            const r = Math.max(0, (y(d.y0) + y(d.y1)) / 2);

            const middleAngle = (angles[1] + angles[0]) / 2;
            const invertDirection = middleAngle > 0 && middleAngle < Math.PI; // On lower quadrants write text ccw
            if (invertDirection) { angles.reverse(); }

            const path = d3.path();
            path.arc(0, 0, r, angles[0], angles[1], invertDirection);
            return path.toString();
        };

        const textFits = d => {
            const CHAR_SPACE = 6;

            const deltaAngle = x(d.x1) - x(d.x0);
            const r = Math.max(0, (y(d.y0) + y(d.y1)) / 2);
            const perimeter = r * deltaAngle;

            return d.data.name.length * CHAR_SPACE < perimeter;
        };

        const svg = d3.select('body').append('svg')
            .style('width', '100vw')
            .style('height', '100vh')
            .attr('viewBox', `${-width / 2} ${-height / 2} ${width} ${height}`)
            .on('click', () => focusOn()); // Reset zoom on canvas click

        d3.json('https://raw.githubusercontent.com/graphineer/viz/master/flare-3.json', (error, root) => {
            if (error) throw error;

            root = d3.hierarchy(root);
            root.sum(d => d.size);

            const slice = svg.selectAll('g.slice')
                .data(partition(root).descendants());

            slice.exit().remove();

            const newSlice = slice.enter()
                .append('g').attr('class', 'slice')
                .on('click', d => {
                    d3.event.stopPropagation();
                    focusOn(d);
                });

            newSlice.append('title')
                .text(d => d.data.name + '\n' + formatNumber(d.value));

            newSlice.append('path')
                .attr('class', 'main-arc')
                //Alternative 1
                //.style('fill', d => color((d.children ? d : d.parent).data.name))
                //Alternative 2
                .style('fill', function (d) { return color(d.color);})
                .attr('d', arc);


            newSlice.append('path')
                .attr('class', 'hidden-arc')
                .attr('id', (_, i) => `hiddenArc${i}`)
                .attr('d', middleArcLine);

            const text = newSlice.append('text')
                .attr('display', d => textFits(d) ? null : 'none');

            // Add white contour
            text.append('textPath')
                .attr('startOffset','50%')
                .attr('xlink:href', (_, i) => `#hiddenArc${i}` )
                .text(d => d.data.name)
                .style('fill', 'none')
                .style('stroke', '#fff')
                .style('stroke-width', 5)
                .style('stroke-linejoin', 'round');

            text.append('textPath')
                .attr('startOffset','50%')
                .attr('xlink:href', (_, i) => `#hiddenArc${i}` )
                .text(d => d.data.name);
        });

        function focusOn(d = { x0: 0, x1: 1, y0: 0, y1: 1 }) {
            // Reset to top-level if no data point specified

            const transition = svg.transition()
                .duration(750)
                .tween('scale', () => {
                    const xd = d3.interpolate(x.domain(), [d.x0, d.x1]),
                        yd = d3.interpolate(y.domain(), [d.y0, 1]);
                    return t => { x.domain(xd(t)); y.domain(yd(t)); };
                });

            transition.selectAll('path.main-arc')
                .attrTween('d', d => () => arc(d));

            transition.selectAll('path.hidden-arc')
                .attrTween('d', d => () => middleArcLine(d));

            transition.selectAll('text')
                .attrTween('display', d => () => textFits(d) ? null : 'none');

            moveStackToFront(d);

            //

            function moveStackToFront(elD) {
                svg.selectAll('.slice').filter(d => d === elD)
                    .each(function(d) {
                        this.parentNode.appendChild(this);
                        if (d.parent) { moveStackToFront(d.parent); }
                    })
            }
        }
    </script>
</body>

Three observations:三个观察:

  1. The property you want is inside the property data , created by the hierarchy generator;您想要的属性位于由层次结构生成器创建的属性data中;
  2. You don't need the color ordinal scale, since you already have the color hex value as the string;您不需要color序数比例,因为您已经将颜色十六进制值作为字符串;
  3. Your root node has no such color property, therefore, assign a specific color to it ("red" in the example below, using logical OR).您的根节点没有这样的color属性,因此,为它分配一个特定的颜色(在下面的示例中为“红色”,使用逻辑 OR)。

So, it is:所以,它是:

    .style('fill', function(d) {
      return d.data.color || "red"
    })

Here is your code with that change only:这是您仅进行该更改的代码:

 <head> <style> body { font-family: Sans-serif; font-size: 11px; }.slice { cursor: pointer; }.slice.main-arc { stroke: #fff; stroke-width: 1px; }.slice.hidden-arc { fill: none; }.slice text { pointer-events: none; dominant-baseline: middle; text-anchor: middle; } </style> <script src='https://d3js.org/d3.v4.min.js'></script> </head> <body> <script> const width = window.innerWidth, height = window.innerHeight, maxRadius = (Math.min(width, height) / 2) - 5; const formatNumber = d3.format(',d'); const x = d3.scaleLinear().range([0, 2 * Math.PI]).clamp(true); const y = d3.scaleSqrt().range([maxRadius *.1, maxRadius]); const color = d3.scaleOrdinal(d3.schemeCategory20); const partition = d3.partition(); const arc = d3.arc().startAngle(d => x(d.x0)).endAngle(d => x(d.x1)).innerRadius(d => Math.max(0, y(d.y0))).outerRadius(d => Math.max(0, y(d.y1))); const middleArcLine = d => { const halfPi = Math.PI / 2; const angles = [x(d.x0) - halfPi, x(d.x1) - halfPi]; const r = Math.max(0, (y(d.y0) + y(d.y1)) / 2); const middleAngle = (angles[1] + angles[0]) / 2; const invertDirection = middleAngle > 0 && middleAngle < Math.PI; // On lower quadrants write text ccw if (invertDirection) { angles.reverse(); } const path = d3.path(); path.arc(0, 0, r, angles[0], angles[1], invertDirection); return path.toString(); }; const textFits = d => { const CHAR_SPACE = 6; const deltaAngle = x(d.x1) - x(d.x0); const r = Math.max(0, (y(d.y0) + y(d.y1)) / 2); const perimeter = r * deltaAngle; return d.data.name.length * CHAR_SPACE < perimeter; }; const svg = d3.select('body').append('svg').style('width', '100vw').style('height', '100vh').attr('viewBox', `${-width / 2} ${-height / 2} ${width} ${height}`).on('click', () => focusOn()); // Reset zoom on canvas click d3.json('https://raw.githubusercontent.com/graphineer/viz/master/flare-3.json', (error, root) => { if (error) throw error; root = d3.hierarchy(root); root.sum(d => d.size); const slice = svg.selectAll('g.slice').data(partition(root).descendants()); slice.exit().remove(); const newSlice = slice.enter().append('g').attr('class', 'slice').on('click', d => { d3.event.stopPropagation(); focusOn(d); }); newSlice.append('title').text(d => d.data.name + '\n' + formatNumber(d.value)); newSlice.append('path').attr('class', 'main-arc') //Alternative 1 //.style('fill', d => color((d.children? d: d.parent).data.name)) //Alternative 2.style('fill', function(d) { return d.data.color || "red" }).attr('d', arc); newSlice.append('path').attr('class', 'hidden-arc').attr('id', (_, i) => `hiddenArc${i}`).attr('d', middleArcLine); const text = newSlice.append('text').attr('display', d => textFits(d)? null: 'none'); // Add white contour text.append('textPath').attr('startOffset', '50%').attr('xlink:href', (_, i) => `#hiddenArc${i}`).text(d => d.data.name).style('fill', 'none').style('stroke', '#fff').style('stroke-width', 5).style('stroke-linejoin', 'round'); text.append('textPath').attr('startOffset', '50%').attr('xlink:href', (_, i) => `#hiddenArc${i}`).text(d => d.data.name); }); function focusOn(d = { x0: 0, x1: 1, y0: 0, y1: 1 }) { // Reset to top-level if no data point specified const transition = svg.transition().duration(750).tween('scale', () => { const xd = d3.interpolate(x.domain(), [d.x0, d.x1]), yd = d3.interpolate(y.domain(), [d.y0, 1]); return t => { x.domain(xd(t)); y.domain(yd(t)); }; }); transition.selectAll('path.main-arc').attrTween('d', d => () => arc(d)); transition.selectAll('path.hidden-arc').attrTween('d', d => () => middleArcLine(d)); transition.selectAll('text').attrTween('display', d => () => textFits(d)? null: 'none'); moveStackToFront(d); // function moveStackToFront(elD) { svg.selectAll('.slice').filter(d => d === elD).each(function(d) { this.parentNode.appendChild(this); if (d.parent) { moveStackToFront(d.parent); } }) } } </script> </body>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM