2016-08-11 8 views
-1

Я работаю с this example диаграммы солнечных лучей. То, что я пытаюсь получить, - удалить внешний слой, тот, который отображает информацию SKU категории, и имеет только слои группы и категории. Я никогда не работал с D3, поэтому я думаю, что глубина дерева - это проблема, но я не мог понять, какую часть кода редактировать, поэтому я попробовал альтернативный подход.D3 Глубина диаграммы солнечных лучей

Я надеялся решить проблему, чтобы удалить все данные JSON, которые используются во внешнем слое. Это fiddle показать результат. К сожалению, кажется, что код недостаточно «умный», чтобы заполнить пробелы/настроить ширину срезов. И так как я не умнее, я прихожу к вам за помощью.

Из-за ограничения по характеру я публикую только те части кода, которые, как я думаю, актуальны.

Оригинальная структура данных:

var data1 = JSON.parse('[ 
{ 
"group":["Books","Arts","ZD111111"],"current":{"count":37} 
},{ 
"group":["Electronics","Audio","ZD111288"],"current":{"count":36} 
},{ 
"group":["Electronics","Camcorders","ZD111301"],"current":{"count":35} 
}, ... ]); 

Отредактированный структура данных:

var data1 = JSON.parse('[ 
{ 
"group":["Books","Arts"],"current":{"count":37} 
},{ 
"group":["Electronics","Audio"],"current":{"count":36} 
},{ 
"group":["Electronics","Camcorders"],"current":{"count":35} 
}, ... ]); 

Sunburst диаграмма:

treePath = ["group","category"], 

var controller = function(data, progress) { 
    if(progress === 100) { 
     data2 = $.extend(true, [], data2); 
     var flatData = []; 
     data.map(function(d) { 
      var item = {}; 
      for(var i = 0; i < treePath.length; i++) { 
       item[treePath[i]] = d.group[i]; 
      } 
      //item.size = d3.selectAll("input").filter(function (d) { return this.checked; }).attr("value") === "count" ? d.current.count : d.current.metrics.price.sum; 
      item.size = d.current.count; // always show count data    
      item.model = d; 
      return flatData.push(item); 
     }); 
     flatData.forEach(function(d) { 
      d.model.group = d.model.group[d.model.group.length - 1]; 
     }); 

     var treeData = genJSON(flatData, treePath.slice(0, treePath.length - 1)); 
     d3.select("#vis") 
      .datum(treeData) 
      .call(chart); 
    } 
}; 

function genJSON(csvData, groups) { 

    var genGroups = function(data) { 
     return _.map(data, function(element, index) { 
      return { name : index, children : element }; 
     }); 
    }; 

    var nest = function(node, curIndex) { 
     if (curIndex === 0) { 
      node.children = genGroups(_.groupBy(csvData, groups[0])); 
      _.each(node.children, function (child) { 
       nest(child, curIndex + 1); 
      }); 
     } 
     else { 
      if (curIndex < groups.length) { 
       node.children = genGroups(
        _.groupBy(node.children, groups[curIndex]) 
       ); 
       _.each(node.children, function (child) { 
        nest(child, curIndex + 1); 
       }); 
      } 
     } 
     return node; 
    }; 
    return nest({}, 0); 
} 

function isInt(n) { 
    return n % 1 === 0; 
} 

function sunburst() { 
    var instance = this, 
     svg = null, 
     timestamp = new Date().getTime(), 
     widgetHeight = 600, 
     widgetWidth = 600, 
     widgetSize = 'large', 
     margin = {top: 0, right: 0, bottom: 0, left: 10}, 
     width = widgetWidth - margin.left - margin.right, 
     height = widgetHeight - margin.top - margin.bottom, 
     radius = Math.min(width, height)/2, 
     x = d3.scale.linear().range([0, 2 * Math.PI]), 
     y = d3.scale.pow().exponent(1), 
     pgColor = d3.scale.ordinal().range([ 
      {"family": "Blue", 1: "#0000CC", 2: "#0099FF", 3: "#CCFFFF"}, 
      {"family": "Orange", 1: "#FF6600", 2: "#FFCC00", 3: "#FFFFCC"}, 
      {"family": "Green", 1: "#009900", 2: "#99CC33", 3: "#CCFF99"}, 
      {"family": "Red", 1: "#FF3333", 2: "#FF9999", 3: "#FFCCCC"}, 
      {"family": "Purple", 1: "#CC0099", 2: "#FF66CC", 3: "#FFCCFF"}, 
      {"family": "Grey", 1: "#7b7b7b", 2: "#999999", 3: "#eeeeee"}]), 
     luminance = d3.scale.sqrt() 
      .domain([0, 1e6]) 
      .clamp(true) 
      .range([90, 20]), 
     i = 0, 
     partition = d3.layout.partition().sort(function(a, b) { return d3.ascending(a.name || a[treePath[treePath.length - 1]], b.name || b[treePath[treePath.length - 1]])}), 
     arc = d3.svg.arc() 
      .startAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x))); }) 
      .endAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))); }) 
      .innerRadius(function(d) { return Math.max(0, d.y ? y(d.y) : d.y); }) 
      .outerRadius(function(d) { return Math.max(0, y(d.y + d.dy)); }); 

    function chart(selection) { 
     selection.each(function(data) { 
      instance.data = data; 
      width = widgetWidth - margin.left - margin.right; 
      height = widgetHeight - margin.top - margin.bottom; 
      radius = Math.min(width, height)/2; 

      y.range([0, radius]); 

      // Select the svg element, if it exists. 
      svg = d3.select(this).selectAll("svg").data([data]); 

      var gEnter = svg.enter() 
       .append("svg") 
       .attr("width", "600") 
       .attr("height", "600") 
       .append("g") 
       .attr("class", "main-group"); 

      gEnter.append("defs") 
       .append("clipPath") 
       .attr("id", "clip-" + timestamp) 
       .append("rect") 
       .attr("x", 0) 
       .attr("y", 0); 

      var sunburstGroup = gEnter.append("g") 
       .attr("class", "sunburst-area") 
       .append("g") 
       .attr("class", "sunburst-group"); 


      sunburstGroup.append("rect") 
       .attr("class", "sunburst-background") 
       .attr("x", 0) 
       .attr("y", 0) 
       .style("fill", "white"); 

      // Update the inner group dimensions. 
      var g = svg.select("g.main-group") 
       .attr("transform", "translate(" + (width/2 + margin.left) + "," + (height/2 + margin.top) + ")"); 

      g.select(".sunburst-background") 
       .attr("width", width) 
       .attr("height", height); 

      partition.value(function(d) { return d.size; }) 
      .nodes(data) 
      .forEach(function(d) { 
       d.key = key(d); 
      }); 

      var path = g.select(".sunburst-group").selectAll(".sunArcs") 
       .data(partition.nodes(data), function(d) { return d.key; }); 

      path.enter().append("path") 
       .attr("class", "sunArcs") 
       .attr("d", arc) 
       .style("fill", function(d) { 
        if(d.depth === 0) return "#fff"; 
        var color = pgColor(d.key.split(".")[0]); 
        return color[d.depth]; 
       }) 
       .style("fill-opacity", 0) 
       .on("click", click) 
       .on("mouseover", mouseover) 
       .on("mouseleave", mouseleave) 
       .each(function(d) { 
        this.x0 = d.x; 
        this.dx0 = d.dx; 
       }); 

      path.transition() 
       .duration(duration) 
       .style("fill-opacity", 1) 
       .attrTween("d", arcTweenUpdate); 

      path.exit() 
       .transition() 
       .duration(duration) 
       .attrTween("d", arcTweenUpdate) 
       .style("fill-opacity", 0) 
       .remove(); 

      function key(d) { 
       var k = [], p = d; 
       while (p.depth) k.push(p.name || p[treePath[treePath.length - 1]]), p = p.parent; 
       return k.reverse().join("."); 
      }  

      function click(d) { 
       path.transition() 
        .duration(duration) 
        .attrTween("d", arcTween(d)); 
      } 

      function getParents(d) { 
       var parents = [], p = d; 
       while (p.depth >= 1) { 
        parents.push(p); 
        p = p.parent; 
       } 
       return parents; 
      } 

      function mouseover(d) { 
       if(d.depth === 0) return; 
       var parentNodes = getParents(d); 
       // Fade all the arcs. 
       d3.selectAll(".sunArcs") 
       .style("opacity", 0.3); 

       // Highlight all arcs in path  
       d3.selectAll(".sunArcs").filter(function(d){ 
        return (parentNodes.indexOf(d) >= 0); 
       }) 
       .style("opacity", 1); 


       // Initialize variables for tooltip 
       var group = d.name || d[treePath[treePath.length - 1]], 
        valueFormat = d3.format(",.0f"), 
        textMargin = 5, 
        popupMargin = 10, 
        opacity = 1, 
        fill = d3.select(this).style("fill"), 
        hoveredPoint = d3.select(this), 
        pathEl = hoveredPoint.node(), 

       // Fade the popup stroke mixing the shape fill with 60% white 
        popupStrokeColor = d3.rgb(
         d3.rgb(fill).r + 0.6 * (255 - d3.rgb(fill).r), 
         d3.rgb(fill).g + 0.6 * (255 - d3.rgb(fill).g), 
         d3.rgb(fill).b + 0.6 * (255 - d3.rgb(fill).b) 
        ), 

       // Fade the popup fill mixing the shape fill with 80% white 
        popupFillColor = d3.rgb(
         d3.rgb(fill).r + 0.8 * (255 - d3.rgb(fill).r), 
         d3.rgb(fill).g + 0.8 * (255 - d3.rgb(fill).g), 
         d3.rgb(fill).b + 0.8 * (255 - d3.rgb(fill).b) 
        ), 

       // The running y value for the text elements 
        y = 0, 
       // The maximum bounds of the text elements 
        w = 0, 
        h = 0, 
        t, 
        box, 
        rows = [], p = d, 
        overlap; 

       var hoverGroup = d3.select(this.parentNode.parentNode.parentNode.parentNode).append("g").attr("class", "hoverGroup"); 

       // Add a group for text 
       t = hoverGroup.append("g"); 
       // Create a box for the popup in the text group 
       box = t.append("rect") 
        .attr("class", "tooltip"); 


        if(!isInt(d.value)) { 
         valueFormat = d3.format(",.2f"); 
        } 


       while (p.depth >= 1) { 
        rows.push(treePath[p.depth - 1] + ": " + (p.name || p[treePath[treePath.length - 1]])); 
        p = p.parent; 
       } 
       rows.reverse(); 
       rows.push("Volume: " + valueFormat(d.value)); 

       t.selectAll(".textHoverShapes").data(rows).enter() 
        .append("text") 
        .attr("class", "textHoverShapes") 
        .text(function (d) { return d; }) 
        .style("font-size", 14); 

       // Get the max height and width of the text items 
       t.each(function() { 
        w = (this.getBBox().width > w ? this.getBBox().width : w); 
        h = (this.getBBox().width > h ? this.getBBox().height : h); 
       }); 

       // Position the text relatve to the bubble, the absolute positioning 
       // will be done by translating the group 
       t.selectAll("text") 
        .attr("x", 0) 
        .attr("y", function() { 
         // Increment the y position 
         y += this.getBBox().height; 
         // Position the text at the centre point 
         return y - (this.getBBox().height/2); 
        }); 

       // Draw the box with a margin around the text 
       box.attr("x", -textMargin) 
        .attr("y", -textMargin) 
        .attr("height", Math.floor(y + textMargin) - 0.5) 
        .attr("width", w + 2 * textMargin) 
        .attr("rx", 5) 
        .attr("ry", 5) 
        .style("fill", popupFillColor) 
        .style("stroke", popupStrokeColor) 
        .style("stroke-width", 2) 
        .style("opacity", 0.95); 

       // Move the tooltip box next to the line point 
       t.attr("transform", "translate(" + margin.left + " , " + 10 + ")"); 
      } 

      // Mouseleave Handler 
      function mouseleave(d) { 
       d3.selectAll(".sunArcs") 
       .style("opacity", 1); 
       d3.selectAll(".hoverGroup") 
       .remove(); 
      }    

      // Interpolate the scales! 
      function arcTween(d) { 
       xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]), 
       yd = d3.interpolate(y.domain(), [d.y, 1]), 
       yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]); 
       return function(d, i) { 
        return i 
        ? function(t) { return arc(d); } 
        : function(t) { x.domain(xd(t)); y.domain(yd(t)).range(yr(t)); return arc(d); }; 
       }; 
      } 

      function arcTweenUpdate(a) { 
       var updateArc = this; 
       var i = d3.interpolate({x: updateArc.x0, dx: updateArc.dx0}, a); 
       return function(t) { 
        var b = i(t); 
        updateArc.x0 = b.x; 
        updateArc.dx0 = b.dx; 
        return arc(i(t)); 
       }; 
      }    
     }); 
    } 

Я принимаю либо решение.

Спасибо.

+0

Существует хороший шанс, что никто не ответит на это ... это не очень специфично, что вы просите .. но кто знает :-) – Ryan

ответ

0

После удаления группы SKU существует несколько объектов с одинаковыми группами. Пример:

{ 
    "group": ["Jewelry", "Diamonds"], 
    "current": { 
     "count": 26 
    } 
} 

{ 
    "group": ["Jewelry", "Diamonds"], 
    "current": { 
     "count": 28 
    } 
} 

Вы должны либо исправить данные или суммировать эти записи. Я обновил свою скрипку, чтобы проверить после записи агрегации с тем же группами, и это выглядит хорошо сейчас (https://jsfiddle.net/zwg31n7h/):

var oldData = _.find(flatData, function(f){ 
       var found = true; 
       _.each(treePath, function(t){ 
        if(f[t] !== item[t]) { 
         found = false; 
        } 
       }); 
       return found; 
      }); 
      if(oldData) { 
       oldData.size += item.size; 
       oldData.model.current.count += item.size; 
      } else { 
       flatData.push(item); 
      } 
+0

Я рад, что сценарий «умнее» меня. Я полностью упустил тот факт, что потеря группы SKU создает дубликаты. : D Спасибо. – skrunic