2016-06-13 102 views
0

目標:創建一個網格,其中X軸包含作業標題,Y軸包含每個作業的零件編號。如果一項工作需要一個以上的特定零件號碼,它將把該單元格垂直分割爲2/3/4/5 ...個不同的單元格,但仍然適合同一行/列。用多種顏色填充D3.js矩形或將矩形分割成多個矩形

問題:我無法爲特定數據點附加多個rects。我試着用這個答案here,但沒有運氣。我試着用每個循環都沒有運氣。任何建議?

進展情況:

enter image description here

目標:在問題

enter image description here

代碼片段:

dayOffset = dayFormat(dateExtent[0]); 
       rect = heatmap.selectAll('rect') 
        .data(data) 
       .enter().append('rect') 
        .attr('width', function (d) { 
         return Xcellscale/d.NumberofPart; 
        }) 
        .attr('height', Ycellscale) 
        .attr('x', function (d) { 
         return xAxisScale(d.JobNumber); 
        }) 
        .attr('y', function (d) { 
         return yAxisScale(d.PartNumber); 
        }) 
        .attr('fill', '#000FF0'); 

JSON:

{ 
     "data": [ 
      { 
       "PartNumber": "a", 
       "JobNumber": "a", 
       "NumberofPart": "2", 
       "timestamp": "2014-09-25T00:00:00", 
       "value": { 
        "PM2.5": 30.22 
       } 
      }, 
      { 
       "PartNumber": "b", 
       "JobNumber": "b", 
       "NumberofPart": "1", 
       "timestamp": "2014-09-25T01:00:00", 
       "value": { 
        "PM2.5": 41.61 
       } 
      }, 
      { 
       "PartNumber": "c", 
       "JobNumber": "c", 
       "NumberofPart": "1", 
       "timestamp": "2014-09-25T02:00:00", 
       "value": { 
        "PM2.5": 50.71 
       } 
      }, 
      { 
       "PartNumber": "d", 
       "JobNumber": "c", 
       "NumberofPart": "1", 
       "timestamp": "2014-09-25T02:00:00", 
       "value": { 
        "PM2.5": 50.71 
       } 
      }, 
      { 
       "PartNumber": "e", 
       "JobNumber": "c", 
       "NumberofPart": "1", 
       "timestamp": "2014-09-25T02:00:00", 
       "value": { 
        "PM2.5": 50.71 
       } 
      }, 
      { 
       "PartNumber": "f", 
       "JobNumber": "c", 
       "NumberofPart": "1", 
       "timestamp": "2014-09-25T02:00:00", 
       "value": { 
        "PM2.5": 50.71 
       } 
      }, 
      { 
       "PartNumber": "g", 
       "JobNumber": "c", 
       "NumberofPart": "1", 
       "timestamp": "2014-09-25T02:00:00", 
       "value": { 
        "PM2.5": 1000.71 
       } 
      }, 
      { 
       "PartNumber": "h", 
       "JobNumber": "c", 
       "NumberofPart": "1", 
       "timestamp": "2014-09-25T03:00:00", 
       "value": { 
        "PM2.5": 250.34 
       } 
      }, 
      { 
       "PartNumber": "i", 
       "JobNumber": "d", 
       "NumberofPart": "1", 
       "timestamp": "2014-09-25T04:00:00", 
       "value": { 
        "PM2.5": 79.64 
       } 
      }, 
      { 
       "PartNumber": "j", 
       "JobNumber": "e", 
       "NumberofPart": "2", 
       "timestamp": "2014-09-25T05:00:00", 
       "value": { 
        "PM2.5": 76.93 
       } 
      }, 
      { 
       "PartNumber": "k", 
       "JobNumber": "f", 
       "NumberofPart": "2", 
       "timestamp": "2014-09-30T06:00:00", 
       "value": { 
        "PM2.5": 106.45 
       } 
      } 
     ] 
    } 

這是我所有的代碼。它基於這個D3.js Example,並且對於我沒有刪除關於時間戳的額外代碼感到抱歉。

$(document).ready(function() { 
    //UI configuration 
    var itemSize = 18, 
     cellSize = itemSize - 1, 
     width = 800, 
     height = 800, 
     margin = { top: 20, right: 20, bottom: 20, left: 25 }; 

    //formats 
    var hourFormat = d3.time.format('%H'), 
     dayFormat = d3.time.format('%j'), 
     timeFormat = d3.time.format('%Y-%m-%dT%X'), 
     monthDayFormat = d3.time.format('%m.%d'); 

    //data vars for rendering 
    var dateExtent = null, 
     data = null, 
     dayOffset = 0, 
     colorCalibration = ['#00ff00', '#0033cc', '#ffcc00', '#ff6600', '#ff0000', '#9E0142'], 
     dailyValueExtent = {}; 

    data = jsondata.data; 

    //axises and scales 
    var axisWidth = 0, 
     axisHeight = itemSize * 24, 
     xAxisScale = d3.scale.ordinal() 
     .domain(data.map(function (d) { return d["JobNumber"]; })) 
     .rangePoints([0, width]); 
    xAxis = d3.svg.axis() 
      .scale(xAxisScale) 
     .orient('top'), 
     yAxisScale = d3.scale.ordinal() 
     .domain(data.map(function (d) { return d["PartNumber"]; })) 
     .rangePoints([0, width]), 
     yAxis = d3.svg.axis() 
     .scale(yAxisScale) 
     .orient('left'); 


    initCalibration(); 

    var svg = d3.select('[role="heatmap"]'); 
    var heatmap = svg 
     .attr('width', width) 
     .attr('height', height) 
    .append('g') 
     .attr('width', width - margin.left - margin.right) 
     .attr('height', height - margin.top - margin.bottom) 
     .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 
    var rect = null; 


     data.forEach(function (valueObj) { 
      valueObj['date'] = timeFormat.parse(valueObj['timestamp']); 


     }); 

     dateExtent = d3.extent(data, function (d) { 
      return d.date; 
     }); 

     svg.append('g') 
      .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') 
      .attr('class', 'x axis') 
      .call(xAxis) 
     .append('text') 
      .text('JobNumber') 
      .attr('transform', 'translate(' + axisWidth + ',-10)'); 

     svg.append('g') 
      .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') 
      .attr('class', 'y axis') 
      .call(yAxis) 
     .append('text') 
      .text('PartNumber') 
      .attr('transform', 'translate(-10,' + axisHeight + ') rotate(-90)'); 

    //render heatmap rects 


     Xcellscale = width/(xAxisScale.domain().length -1); 
     Ycellscale = height/(yAxisScale.domain().length - 1); 

     console.log(Xcellscale + " " + Ycellscale); 
     console.log(width + " " + height); 
     console.log(xAxisScale.domain().length + " " + data.map(function (d) { return d["PartNumber"]; }).length); 

     dayOffset = dayFormat(dateExtent[0]); 
     rect = heatmap.selectAll('rect') 
      .data(data) 
     .enter().append('rect') 
      .attr('width', function (d) { 
       return Xcellscale/d.NumberofPart; 
      }) 
      .attr('height', Ycellscale) 
      .attr('x', function (d) { 
       return xAxisScale(d.JobNumber); 
      }) 
      .attr('y', function (d) { 
       return yAxisScale(d.PartNumber); 
      }) 
      .attr('fill', '#000FF0'); 

     rect.filter(function (d) { return d.value['PM2.5'] > 0; }) 
      .append('title') 
      .text(function (d) { 
       return monthDayFormat(d.date) + ' ' + d.value['PM2.5']; 
      }); 

     //renderColor(); 


    function initCalibration() { 
     d3.select('[role="calibration"] [role="example"]').select('svg') 
      .selectAll('rect').data(colorCalibration).enter() 
     .append('rect') 
      .attr('width', cellSize) 
      .attr('height', cellSize) 
      .attr('x', function (d, i) { 
       return i * itemSize; 
      }) 
      .attr('fill', function (d) { 
       return d; 
      }); 

     //bind click event 
     d3.selectAll('[role="calibration"] [name="displayType"]').on('click', function() { 
      renderColor(); 
     }); 
    } 

    function renderColor() { 
     var renderByCount = document.getElementsByName('displayType')[0].checked; 

     rect 
      .attr('width', function (d) { 
       return Xcellscale/d.NumberofPart; 
      }) 
      .filter(function (d) { 
       return (d.value['PM2.5'] >= 0); 
      }) 
      .transition() 
      .delay(function (d) { 
       return 1; 
      }) 
      .duration(500) 
      .attrTween('fill', function (d, i, a) { 
       //choose color dynamicly  
       var colorIndex = d3.scale.quantize() 
       .range([0, 1, 2, 3, 4, 5]) 
       .domain(([0, 500])); 

       return d3.interpolate(a, '#00ff00'); 
      }) 

    } 

    //extend frame height in `http://bl.ocks.org/` 
    d3.select(self.frameElement).style("height", "600px"); 
}); 
+1

可以通過的LinearGradient填充兩種顏色一個矩形與4點停止,一個在開始,中間2個相同停止並且一個在末端。 –

+0

有沒有辦法增加線性漸變的停靠量? – Mark

+1

您可以根據需要添加它們,並通過DOM直接動態添加它們,或者使用d3 –

回答

0

好吧,所以我想通了。這並不是將數據導入熱圖的最有說服力的方式,但它工作得很好。而不是使用selectall()。data()。enter()我只是在我的數據集中使用了一個for循環,並通過這種方式添加了矩形。我通常會看到人們使用映射函數而不是for循環,所以如果有任何問題可能導致我知道,那麼我可以改進我的代碼。

這裏是我的代碼:

for (var i = 0; i < data.length; i++) { 

    numberofparts = data[i].NumberofPart; 
    for (var j = 0; j < data[i].NumberofPart; j++) { 

     heatmap.append('rect') 
     .attr('width', Xcellscale/numberofparts) 
     .attr('height', Ycellscale) 
     .attr('x', xAxisScale(data[i].JobNumber) + (j*(Xcellscale/numberofparts))) 
     .attr('y', yAxisScale(data[i].PartNumber)) 
     .attr('fill', function() { 
      if (j == 1) { 
       return '#000FF0'; 
      } 
      else 
       return '#00000' 
     }); 

    } 
} 

這裏是我的表/熱圖的截圖現在:

enter image description here

我希望這可以幫助一些人試圖做同樣的事情。

UPDATE 6/16/2016 感謝威爾和羅伯特在您的幫助我使用。每()函數,並希望這將有助於我避免可能發生的使用for循環的問題改善我的代碼。我也參考bl.ocks.org這個例子尋求幫助。

我更新的代碼(我改名爲一組變量從JSON以及):

$(document).ready(function() { 
    $.getJSON("/api/?????") 
    .done(function (materialdata) { 

     //UI configuration 
     var itemSize = 18, 
      cellSize = itemSize - 1, 
      width = 2500, 
      height = 15000, 
      margin = { top: 20, right: 20, bottom: 20, left: 100 }; 

     data = materialdata.ChartData; 


     //axises and scales 
     var axisWidth = 0, 
      axisHeight = itemSize * 24, 
      xAxisScale = d3.scale.ordinal() 
      .domain(data.map(function (d) { return d["JobOrderNumber"]; })) 
      .rangePoints([0, width]); 
     xAxis = d3.svg.axis() 
       .scale(xAxisScale) 
      .orient('top'), 
      yAxisScale = d3.scale.ordinal() 
      .domain(materialdata.Domain) 
      .rangePoints([0, height]), 
      yAxis = d3.svg.axis() 
      .scale(yAxisScale) 
      .orient('left'); 

     var svg = d3.select('[role="heatmap"]'); 
     var heatmap = svg 
      .attr('width', width) 
      .attr('height', height) 
     .append('g') 
      .attr('width', width - margin.left - margin.right) 
      .attr('height', height - margin.top - margin.bottom) 
      .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 
     var rect = null; 

     svg.append('g') 
      .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') 
      .attr('class', 'x axis') 
      .call(xAxis) 
     .append('text') 
      .text('') 
      .attr('transform', 'translate(' + axisWidth + ',-10)'); 

     svg.append('g') 
      .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') 
      .attr('class', 'y axis') 
      .call(yAxis) 
     .append('text') 
      .text('') 
      .attr('transform', 'translate(-10,' + axisHeight + ') rotate(-90)'); 

     Xcellscale = (width/(xAxisScale.domain().length - 1)) - 1; 
     Ycellscale = (height/(yAxisScale.domain().length - 1)) - 1; 

     rect = heatmap.selectAll('svg') 
      .data(data) 
     .enter(); 

     rect.append('svg').each(function (d, i) { 
      numberofparts = d.PartQuantity; 
      for (var j = 0; j < d.PartQuantity; j++) { 
       d3.select(this).append('rect') 
       .attr('width', Xcellscale/numberofparts) 
       .attr('height', Ycellscale) 
       .attr('x', xAxisScale(d.JobOrderNumber) + (j * (Xcellscale/numberofparts))) 
       .attr('y', yAxisScale(d.PartNumber)) 
       .attr('fill', function() { 
        return GetPartfromQuantities(materialdata, data[i].PartNumber); 
       }); 
      } 
     }); 

    }); 
}); 

這是現在的樣子。我添加了更多的數據,並添加了着色方案。

I added more data an colors to the chart

+1

我已經研究過類似的問題,並且已經成功地使用'.each()',然後''d3.select(this)'在函數中使用'.each'。我認爲它是「嵌套」d3。我對您的解決方案的擔憂是,如果不使用'.data()。enter()',那麼您不會將數據鏈接到節點,這可能會在稍後造成麻煩。現在沒有時間寫出完整的解決方案,但是如果您仍然對此感興趣,請告訴我們。 – wdickerson

+0

當然,我很想看看你會怎麼做嵌套。這聽起來像是一種更好的策略,可能對我處理可能更像樹的數據集有用。我會暫時擱置這個話題,因爲你的想法聽起來像是對我最好的答案。我也會同時查看.each()方法。 – Mark

+1

我只是遇到了Mike Bostock撰寫的這篇文章,可能會有所幫助:https://bost.ocks.org/mike/nest/ – wdickerson