用多种颜色填充D3.js rect或将rect分割成多个rects

时间:2016-06-13 20:55:45

标签: javascript d3.js svg

目标:创建一个网格,其中X轴包含作业标题,Y轴具有每个作业的部件号。如果作业需要多个特定零件编号,它会将单元格垂直拆分为2/3/4/5 ...不同的单元格,但仍然适合同一行/列。

问题:我在为某些数据点添加多个rects时遇到问题。我尝试使用这个答案here,但没有运气。我尝试使用a for each循环也没有运气。有什么建议吗?

当前进展:

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 个答案:

答案 0 :(得分: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

我希望这可能有助于某些人尝试做同样的事情。

2016年6月16日更新 感谢Will和Robert的帮助,我使用.each()函数改进了我的代码,希望这可以帮助我避免使用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

相关问题