目标:创建一个网格,其中X轴包含作业标题,Y轴具有每个作业的部件号。如果作业需要多个特定零件编号,它会将单元格垂直拆分为2/3/4/5 ...不同的单元格,但仍然适合同一行/列。
问题:我在为某些数据点添加多个rects时遇到问题。我尝试使用这个答案here,但没有运气。我尝试使用a for each循环也没有运气。有什么建议吗?
当前进展:
目标:
有问题的代码段:
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");
});
答案 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'
});
}
}
以下是我的图表/热图的截图:
我希望这可能有助于某些人尝试做同样的事情。
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);
});
}
});
});
});
以下是它现在的样子。我在集合中添加了更多数据并添加了着色方案。