I am recieving this error:
*Uncaught TypeError: d3__WEBPACK_IMPORTED_MODULE_4__.drag(...).origin is not a function*
113 | .data(targetPoints)
114 | .enter().append("circle")
115 | .attr("class", "handle")
> 116 | .attr("transform", function(d) { return "translate(" + d + ")"; })
| ^ 117 | .attr("r", 7)
118 | .call(d3.drag()
119 | .origin(function() {
I am trying to make a React app where I can upload a picture, which then later can be dragged and prespective transformed using SVG. I am building it off this code https://bl.ocks.org/mbostock/10571478Z.
...
const DoorPreviewer = ({doorHook}) => {
const {background} = doorHook;
useEffect(() => {
var margin = {top: 50, right: 280, bottom: 50, left: 280},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var transform = ["", "-webkit-", "-moz-", "-ms-", "-o-"].reduce(
function(p, v) { return v + "transform" in document.body.style ? v : p; }
) + "transform";
var sourcePoints = [[0, 0], [width, 0], [width, height], [0, height]],
targetPoints = [[0, 0], [width, 0], [width, height], [0, height]];
d3.select("body").selectAll("svg")
.data(["transform", "flat"])
.enter().append("svg")
.attr("id", function(d) { return d; })
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var svgTransform = d3.select("#transform")
.style(transform + "-origin", margin.left + "px " + margin.top + "px 0");
var svgFlat = d3.select("#flat");
svgTransform.select("g").append("image")
.attr("xlink:href", "sailboat.png")
.attr("width", width)
.attr("height", height);
svgTransform.select("g").selectAll(".line--x")
.data(d3.range(0, width + 1, 40))
.enter().append("line")
.attr("class", "line line--x")
.attr("x1", function(d) { return d; })
.attr("x2", function(d) { return d; })
.attr("y1", 0)
.attr("y2", height);
svgTransform.select("g").selectAll(".line--y")
.data(d3.range(0, height + 1, 40))
.enter().append("line")
.attr("class", "line line--y")
.attr("x1", 0)
.attr("x2", width)
.attr("y1", function(d) { return d; })
.attr("y2", function(d) { return d; });
var handle = svgFlat.select("g").selectAll(".handle")
.data(targetPoints)
.enter().append("circle")
.attr("class", "handle")
.attr("transform", function(d) { return "translate(" + d + ")"; })
.attr("r", 7)
.call(d3.drag()
.origin(function() {
var d = d3.select(this)
return {x: d[0], y: d[1]}; })
.on("drag", dragged));
function dragged(evt, d) {
d3.select(this).attr("transform", "translate(" + (d[0] = evt.x) + "," + (d[1] = evt.y) + ")");
transformed();
}
function transformed() {
for (var a = [], b = [], i = 0, n = sourcePoints.length; i < n; ++i) {
var s = sourcePoints[i], t = targetPoints[i];
a.push([s[0], s[1], 1, 0, 0, 0, -s[0] * t[0], -s[1] * t[0]]);
b.push(t[0]);
a.push([0, 0, 0, s[0], s[1], 1, -s[0] * t[1], -s[1] * t[1]]);
b.push(t[1]);
}
var X = solve(a, b, true), matrix = [
X[0], X[3], 0, X[6],
X[1], X[4], 0, X[7],
0, 0, 1, 0,
X[2], X[5], 0, 1
].map(function(x) {
return d3.format(x)(6);
});
svgTransform.style(transform, "matrix3d(" + matrix + ")");
}
}, [])
return (
<DoorPreviewBackground bg={background}>
</DoorPreviewBackground>
);
}
export {Display};
Does anyone know why this happens?
Related
I have created a graph here https://imgur.com/4GKmANr, but am having issues displaying the JSON values along the end of the bars. They print - as you can see - at the top, but not in the correct place. How would I make them align to their corresponding bars?
const margin2 = {top: 20, right: 30, bottom: 60, left: 250}
const width2 = 1000 - margin2.left - margin2.right
const graphWidth2 = 600-margin2.left - margin2.right
const graphHeight2 = 900 - margin2.top - margin2.bottom
const height2 = 370 - margin2.top - margin2.bottom
const svg2 = d3.select(".line").append("svg").attr("width", width2 + margin2.left + margin2.right)
.attr("height", height2 + margin2.top + margin2.bottom)
.append("g").attr("transform", `translate(${margin.left+148},${margin.top})`)
d3.json("./data/linedata.json").then(function(data) {
const x2 = d3.scaleLinear().domain([0, 100]).range([ 0, width2])
svg2.append("g").attr("transform", "translate(0," + height2 + ")")
.call(d3.axisBottom(x2)
.tickFormat(d => d + "%"))
.selectAll("text")
.attr('class', 'xaxis')
.attr("transform", "translate(12,0)")
.style("text-anchor", "end")
const y2 = d3.scaleBand().range([ 0, height2 ])
.domain(data.map(function(d) { return d.desc; })).padding(.2)
svg2.append("g").attr('class', 'xaxis')
.call(d3.axisLeft(y2)).style("font-family","Pluto Sans").style("font-size","16").style("fill","#4F5552")
const deleteline = svg2.selectAll("path,line").remove();
svg2.selectAll(".line1").data(data).enter().append("rect")
.attr("x",x2(0))
.attr("y", function(d) { return y2(d.desc)})
.attr("width", function(d) { return x2(d.total)})
.attr("height", y2.bandwidth())
svg2.selectAll(".line1").data(data).enter().append("text")
.text((d) => { return d.total + "%"})
.attr("x", (d) => x2(d.total + 15) +9)
.attr("y", (d) => y2(d.desc + y2.bandwidth()))
.style("font-size",30)
})
When you do this:
.attr("y", (d) => y2(d.desc + y2.bandwidth()))
You are just concatenating a categorical variable with a number (like "foo" + 42 is "foo42"). For a band scale, that will return undefined, and your text will be at the top of the <g>.
What you want is:
.attr("y", d => y2(d.desc) + y2.bandwidth())
I am trying to create a chart like below:
While I've created the skeleton of it but stuck on how to fill those areas with multiple colors. I'm using d3 to complete this chart.
My code for reference:
var line = d3.line()
.x(function(d) { return x(d.ind); })
.y(function(d) { return y(d.tot_cases); })
x.domain(data.map(function(d) { return d.ind; }));
y.domain([0, d3.max(data, function(d) { return d.tot_cases; })]);
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
var lineAndDots = g.append("g")
.attr("class", "line-and-dots")
.attr("transform", "translate(" + ((margin.left + margin.right) / 3) + "," + 0 + ")")
// Data line
lineAndDots.append("path")
.datum(data)
.attr("class", "data-line")
.attr("d", line);
// Data dots
lineAndDots.selectAll("line-circle")
.data(data)
.enter().append("circle")
.attr("class", "data-circle")
.attr("r", 5)
.attr("cx", function(d) { return x(d.ind); })
.attr("cy", function(d) { return y(d.tot_cases); });
g.selectAll("lines-ax")
.data(data)
.enter().append("line")
.attr("class", "line")
.attr("x1", function(d) { return x(d.ind) + margin.right; })
.attr("y1", function(d) { return height - margin.bottom})
.attr("x2", function(d) { return x(d.ind) + margin.right ; })
.attr("y2", function(d) { return y(d.tot_cases); })
.attr("fill", "black")
.attr("stroke", "gray")
.attr("stroke-width", 3);
You can find my entire code in this link.
Any ideas on how to do this is very helpful. Thanks in advance!
Not a clean implementation, since I did not implement select, enter, append pattern of D3, also I add a random color generator, I will leave it to you to clean it up and add your color scheme to the chart
You might notice that the last element missing its color and polygon item since there is no indication on how to add it.
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 40},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
var x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
y = d3.scaleLinear().rangeRound([height, 0]);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var data = [
{"ind": "Examined", tot_cases: 25000},
{"ind": "Notified", tot_cases: 17000},
{"ind": "Initiated", tot_cases: 10000},
{"ind": "HIV Stat. Confirmed", tot_cases: 14000},
{"ind": "HIV Unknown", tot_cases:15000}
]
var stroke_colors = {
"Examined": '#2B597C',
"Notified": "#6D5C7E",
"Initiated": '#BF6D84',
"HIV Stat. Confirmed": '#F1737E',
"HIV Unknown": '#F9B294'
}
var color = ["#2B597C","#6D5C7E","#BF6D84","#F1737E","#F9B294"]
var line = d3.line()
.x(function(d) { return x(d.ind); })
.y(function(d) { return y(d.tot_cases); })
x.domain(data.map(function(d) { return d.ind; }));
y.domain([0, d3.max(data, function(d) { return d.tot_cases; })]);
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
var lineAndDots = g.append("g")
.attr("class", "line-and-dots")
.attr("transform", "translate(" + ((margin.left + margin.right) / 3) + "," + 0 + ")")
// Data line
lineAndDots.append("path")
.datum(data)
.attr("class", "data-line")
.attr("d", line)
.style('stroke', function(d){
for(i = 0; i < 5; i++){
return stroke_colors[d[i].ind];}})
.style('fill', 'none')
// Data dots
lineAndDots.selectAll("line-circle")
.data(data)
.enter().append("circle")
.attr("class", "data-circle")
.attr("r", 5)
.attr("cx", function(d) { return x(d.ind); })
.attr("cy", function(d) { return y(d.tot_cases); })
g.selectAll("lines-ax")
.data(data)
.enter().append("line")
.attr("class", "line")
.attr("x1", function(d) { return x(d.ind) + margin.right; })
.attr("y1", function(d) { return height})
.attr("x2", function(d) { return x(d.ind) + margin.right ; })
.attr("fill", "black")
.attr("stroke", "#F2F2F2")
.attr("stroke-width", 3);
for(let i=1; i< data.length; i++) {
let points = [];
points[0] = {x: x(data[i-1].ind) + margin.right, y: y(data[i-1].tot_cases)};
points[1] = {x: x(data[i].ind) + + margin.right, y: y(data[i].tot_cases)};
points[2] = {x: x(data[i].ind) + + margin.right, y: height};
points[3] = {x: x(data[i-1].ind) + margin.right, y: height};
points[4] = {x: x(data[i-1].ind) + margin.right, y: y(data[i-1].tot_cases)};
g.append("polygon")
.attr("points", points.map(function(d) { return [d.x,d.y].join(","); }).join(" "))
.attr("stroke","red")
.attr("stroke-width", 2)
.attr('fill', getRandomRgb());
}
let points = [];
points[0] = {x: x(data[data.length - 1].ind) + margin.right, y: y(data[data.length - 1].tot_cases)};
points[1] = {x: x(data[data.length - 1].ind) + margin.right + 150, y: y(data[data.length - 1].tot_cases)};
points[2] = {x: x(data[data.length - 1].ind) + margin.right + 150, y: height};
points[3] = {x: x(data[data.length - 1].ind) + margin.right, y: height};
points[4] = {x: x(data[data.length - 1].ind) + margin.right, y: y(data[data.length - 1].tot_cases)};
g.append("polygon")
.attr("points", points.map(function(d) { return [d.x,d.y].join(","); }).join(" "))
.attr("stroke","red")
.attr("stroke-width", 2)
.attr('fill', getRandomRgb());
function getRandomRgb() {
const num = Math.round(0xffffff * Math.random());
const r = num >> 16;
const g = num >> 8 & 255;
const b = num & 255;
return 'rgb(' + r + ', ' + g + ', ' + b + ')';
}
body {
font-family: 'Droid Sans', sans-serif;
}
.axis {
font-size: 14px;
font-weight: bold;
}
text {
fill: #727075;
stroke: none;
}
.axis path,
.axis line {
fill: none;
stroke: none;
stroke-width: 2px;
shape-rendering: crispEdges;
}
.grid path {
stroke: none;
}
.grid line {
stroke: #E0E0E0;
shape-rendering: crispEdges;
}
.data-line {
fill: none;
stroke: url(#line-gradient);
stroke-width: 1px;
}
.data-circle {
fill: #3C92BA;
}
.axis-title {
text-anchor: end;
fill: #5D6971;
font-weight: normal;
}
.axis-tspan {
font-size: 12px;
}
<!DOCTYPE html>
<html>
<head>
<style>
</style>
</head>
<body>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v5.min.js"></script>
</body>
</html>
I am trying to do a bar chart with a brush component (based on https://bl.ocks.org/SevenChan07/495cd567e0ede0deeb14bb3599dce685) and using the enter-merge-exit pattern but I cant make the brush working properly. When I move the brush the chart goes beyond its bounds. I has probably to do with clipping but I dont know how to fix it.
var defs = focus.append('defs');
// use clipPath
defs.append('clipPath')
.attr('id', 'my-clip-path')
.append('rect')
.attr('width', width)
.attr('height', height);
Any help would be greatly appreciated.
Here is the fiddle and below is a snippet:
let barData = []
for(let i = 0;i < 100; i++){
barData.push({
Prob: Math.random()*10,
labels: 'test' + i
})
}
barchart(barData)
function barchart(data) {
var ordinals = data.map(function (d) {
return d.labels;
});
var svg = d3.select("#myPlot").select("svg");
var margin = {
top: 20,
right: 20,
bottom: 0.3 * svg.attr("height"),
left: 40
},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
margin2 = {
top: 20 + margin.top + height,
right: 20,
bottom: 30,
left: 40
},
height2 = height / 5;
// the scale
var scale = {
x: d3.scaleLinear().range([0, width]).nice(),
x2: d3.scaleLinear().range([0, width]).nice(),
y: d3.scaleLinear().range([height, 0]).nice(),
y2: d3.scaleLinear().range([height2, 0]).nice()
};
let xBand = d3.scaleBand().domain(d3.range(-1, ordinals.length)).range([0, width])
var axis = {
x: d3.axisBottom(scale.x).tickFormat((d, e) => ordinals[d]),
y: d3.axisLeft(scale.y)
};
var brush = d3.brushX()
.extent([[0, 0], [width, height2]])
.on("brush", brushed)
var focus = svg.select('.focus')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
focus.select(".axis").attr("transform", "translate(0," + height +")");
var context = svg.select('.context')
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
var defs = focus.append('defs');
// use clipPath
defs.append('clipPath')
.attr('id', 'my-clip-path')
.append('rect')
.attr('width', width)
.attr('height', height);
function updateScales(data) {
scale.x.domain([-1, ordinals.length])
scale.y.domain([0, d3.max(data, d => d.Prob)])
scale.x2.domain(scale.x.domain())
scale.y2.domain([0, d3.max(data, d => d.Prob)])
}
svg.call(renderPlot, data)
function renderPlot(selection, data) {
updateScales(data);
selection.select(".context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")")
.select('.brush')
.call(brush)
.call(brush.move, scale.x.range())
selection.select(".axis2")
.attr("transform", "translate(0," + height2 +")");
selection.select(".focus").select(".axis").call(axis.x);
selection.select(".focus").select(".axis.axis--y").call(axis.y);
selection
.call(renderPoints, data);
}
function renderPoints(selection, data) {
var points = selection.select('.focus')
.selectAll('.bar').data(data);
var newPoints = points.enter().append('rect')
.attr('class', 'bar')
.attr('x', (d, i) => {
return scale.x(i) - xBand.bandwidth() * 0.9 / 2
})
.attr('y', (d, i) => {
return scale.y(d.Prob)
})
.attr('width', xBand.bandwidth() * 0.9)
.attr('height', d => {
return height - scale.y(d.Prob)
});
points.merge(newPoints)
.transition().duration(1000)
.attr('x', (d, i) => {
return scale.x(i) - xBand.bandwidth() * 0.9 / 2
})
.attr('y', (d, i) => {
return scale.y(d.Prob)
})
.attr('width', xBand.bandwidth() * 0.9)
.attr('height', d => {
return height - scale.y(d.Prob)
});
points.exit()
.transition().duration(1000)
.remove();
var sPoints = selection.select('.context').selectAll('.bar').data(data);
var newsPoints = sPoints.enter().append('rect')
.attr('class', 'bar')
.attr('x', (d, i) => {
return scale.x2(i) - xBand.bandwidth() * 0.9 / 2
})
.attr('y', (d, i) => scale.y2(d.Prob))
.attr('width', xBand.bandwidth() * 0.9)
.attr('height', d => {
return height2 - scale.y2(d.Prob)
});
sPoints.merge(newsPoints)
.transition().duration(1000)
.attr('x', (d, i) => {
return scale.x2(i) - xBand.bandwidth() * 0.9 / 2
})
.attr('y', (d, i) => scale.y2(d.Prob))
.attr('width', xBand.bandwidth() * 0.9)
.attr('height', d => {
return height2 - scale.y2(d.Prob)
});
sPoints.exit()
.transition().duration(1000)
.remove();
}
function brushed() {
var s = d3.event.selection || scale.x2.range()
scale.x.domain(s.map(scale.x2.invert, scale.x2))
focus.select('.axis').call(axis.x)
focus.selectAll('.bar')
.attr('x', (d, i) => {
return scale.x(i) - xBand.bandwidth() * 0.9 / 2
})
}
}
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<style type="text/css">
.bar { fill: steelblue; }
</style>
<script src='https://d3js.org/d3.v4.min.js' type='text/javascript'></script>
</head>
<body>
<div class='chart span4' id='myPlot'>
<svg width="700" height="500">
<g class="focus">
<g class="axis"></g>
<g class="axis axis--y"></g>
</g>
<g class="context">
<g class="axis2"></g>
<g class="brush"></g>
</g>
</svg>
</div>
</body>
</html>
You've almost got it, you just need to apply the clip path to something. We can easily do this with your bars (you could use a g containing only the bars too):
var newPoints = points.enter().append('rect')
.attr('class', 'bar')
..... // other attributes
.attr("clip-path","url(#my-clip-path)");
We only need to do it on enter, as the clip path doesn't need to be updated (we aren't changing it). Here's a snippet below:
let barData = []
for(let i = 0;i < 100; i++){
barData.push({
Prob: Math.random()*10,
labels: 'test' + i
})
}
barchart(barData)
function barchart(data) {
var ordinals = data.map(function (d) {
return d.labels;
});
var svg = d3.select("#myPlot").select("svg");
var margin = {
top: 20,
right: 20,
bottom: 0.3 * svg.attr("height"),
left: 40
},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
margin2 = {
top: 20 + margin.top + height,
right: 20,
bottom: 30,
left: 40
},
height2 = height / 5;
// the scale
var scale = {
x: d3.scaleLinear().range([0, width]).nice(),
x2: d3.scaleLinear().range([0, width]).nice(),
y: d3.scaleLinear().range([height, 0]).nice(),
y2: d3.scaleLinear().range([height2, 0]).nice()
};
let xBand = d3.scaleBand().domain(d3.range(-1, ordinals.length)).range([0, width])
var axis = {
x: d3.axisBottom(scale.x).tickFormat((d, e) => ordinals[d]),
y: d3.axisLeft(scale.y)
};
var brush = d3.brushX()
.extent([[0, 0], [width, height2]])
.on("brush", brushed)
var focus = svg.select('.focus')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
focus.select(".axis").attr("transform", "translate(0," + height +")");
var context = svg.select('.context')
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
var defs = focus.append('defs');
// use clipPath
defs.append('clipPath')
.attr('id', 'my-clip-path')
.append('rect')
.attr('width', width)
.attr('height', height);
function updateScales(data) {
scale.x.domain([-1, ordinals.length])
scale.y.domain([0, d3.max(data, d => d.Prob)])
scale.x2.domain(scale.x.domain())
scale.y2.domain([0, d3.max(data, d => d.Prob)])
}
svg.call(renderPlot, data)
function renderPlot(selection, data) {
updateScales(data);
selection.select(".context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")")
.select('.brush')
.call(brush)
.call(brush.move, scale.x.range())
selection.select(".axis2")
.attr("transform", "translate(0," + height2 +")");
selection.select(".focus").select(".axis").call(axis.x);
selection.select(".focus").select(".axis.axis--y").call(axis.y);
selection
.call(renderPoints, data);
}
function renderPoints(selection, data) {
var points = selection.select('.focus')
.selectAll('.bar').data(data);
var newPoints = points.enter().append('rect')
.attr('class', 'bar')
.attr('x', (d, i) => {
return scale.x(i) - xBand.bandwidth() * 0.9 / 2
})
.attr('y', (d, i) => {
return scale.y(d.Prob)
})
.attr('width', xBand.bandwidth() * 0.9)
.attr('height', d => {
return height - scale.y(d.Prob)
})
.attr("clip-path","url(#my-clip-path)");
points.merge(newPoints)
.transition().duration(1000)
.attr('x', (d, i) => {
return scale.x(i) - xBand.bandwidth() * 0.9 / 2
})
.attr('y', (d, i) => {
return scale.y(d.Prob)
})
.attr('width', xBand.bandwidth() * 0.9)
.attr('height', d => {
return height - scale.y(d.Prob)
})
points.exit()
.transition().duration(1000)
.remove();
var sPoints = selection.select('.context').selectAll('.bar').data(data);
var newsPoints = sPoints.enter().append('rect')
.attr('class', 'bar')
.attr('x', (d, i) => {
return scale.x2(i) - xBand.bandwidth() * 0.9 / 2
})
.attr('y', (d, i) => scale.y2(d.Prob))
.attr('width', xBand.bandwidth() * 0.9)
.attr('height', d => {
return height2 - scale.y2(d.Prob)
});
sPoints.merge(newsPoints)
.transition().duration(1000)
.attr('x', (d, i) => {
return scale.x2(i) - xBand.bandwidth() * 0.9 / 2
})
.attr('y', (d, i) => scale.y2(d.Prob))
.attr('width', xBand.bandwidth() * 0.9)
.attr('height', d => {
return height2 - scale.y2(d.Prob)
});
sPoints.exit()
.transition().duration(1000)
.remove();
}
function brushed() {
var s = d3.event.selection || scale.x2.range()
scale.x.domain(s.map(scale.x2.invert, scale.x2))
focus.select('.axis').call(axis.x)
focus.selectAll('.bar')
.attr('x', (d, i) => {
return scale.x(i) - xBand.bandwidth() * 0.9 / 2
})
}
}
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<style type="text/css">
.bar { fill: steelblue; }
</style>
<script src='https://d3js.org/d3.v4.min.js' type='text/javascript'></script>
</head>
<body>
<div class='chart span4' id='myPlot'>
<svg width="700" height="500">
<g class="focus">
<g class="axis"></g>
<g class="axis axis--y"></g>
</g>
<g class="context">
<g class="axis2"></g>
<g class="brush"></g>
</g>
</svg>
</div>
</body>
</html>
I am trying to port a d3 chart (which I found online) to v4 but for some reason only the axes are shown and nothing else. The code runs without any errors in the console of Chrome's developer tools and I have hit the wall as I am not an advanced d3 user. Any ideas are highly appreciated.
Here are two jsfiddles, in v3 and v4
https://jsfiddle.net/54mp286c/31/
https://jsfiddle.net/54mp286c/36/
and this is the d3 v4 code
var data = [{
"Cell_Num": 0,
"y": 3,
"x": 2
}, {
"Cell_Num": 1,
"y": 3,
"x": 6
}, {
"Cell_Num": 2,
"y": 7,
"x": 1
}, {
"Cell_Num": 3,
"y": 5,
"x": 5
}]
var margin = {
top: 10,
left: 50,
bottom: 30,
right: 10
},
width = 460 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var scale = {
x: d3.scaleLinear().range([0, width]).nice(),
y: d3.scaleLinear().range([height, 0]).nice()
};
var access = {
x: function(d) {
return d.x;
},
y: function(d) {
return d.y;
}
};
var value = {
x: function(d) {
return scale.x(access.x(d));
},
y: function(d) {
return scale.y(access.y(d));
}
};
var axis = {
x: d3.axisBottom(scale.x),
y: d3.axisLeft(scale.y)
};
var svg = d3.select(".chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g").attr("class", "x axis");
svg.append("g").attr("class", "y axis");
svg.call(renderPlot, data);
function renderPlot(selection, data) {
updateScales(data);
selection.select(".x.axis").call(axis.x)
.attr("transform", "translate(0," + height + ")");
selection.select(".y.axis").call(axis.y);
selection
.call(renderVoronoi, data)
.call(renderPoints, data);
}
function renderVoronoi(selection, data) {
var voronoi = d3.voronoi()
.x(value.x)
.y(value.y)
.extent([
[0, 0],
[width, height]
]);
var polygons = selection.selectAll(".voronoi")
.data(voronoi(data));
polygons.enter().append("path")
.attr("class", "voronoi")
.on("mouseenter", function(d, i) {
var datum = selection.selectAll(".point").data()[i];
selection.call(renderCrosshair, datum);
})
.on("mouseleave", function(d, i) {
selection.selectAll(".crosshair").remove();
});
polygons
.attr("d", d3.line());
polygons.exit()
.remove();
}
function renderCrosshair(selection, datum) {
var lineData = [
// vertical line
[
[value.x(datum), height],
[value.x(datum), 0]
],
// horizontal line
[
[0, value.y(datum)],
[width, value.y(datum)]
]
];
var crosshairs = selection.selectAll(".crosshair.line").data(lineData);
crosshairs.enter().append("path")
.attr("class", "crosshair line");
crosshairs
.attr("d", d3.svg.line());
crosshairs.exit()
.remove();
var labelData = [{
x: -6,
y: value.y(datum) + 4,
text: Math.round(access.y(datum)),
orient: "left"
},
{
x: value.x(datum),
y: height + 16,
text: Math.round(access.x(datum)),
orient: "bottom"
}
];
var labels = selection.selectAll(".crosshair.label").data(labelData);
labels.enter().append("text")
.attr("class", "crosshair label");
labels
.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y;
})
.style("text-anchor", function(d) {
return d.orient === "left" ? "end" : "middle";
})
.text(function(d) {
return d.text;
});
labels.exit().remove();
}
function renderPoints(selection, data) {
var points = selection.selectAll(".point").data(data);
points.enter().append("circle")
.attr("class", "point")
.attr("cx", value.x)
.attr("cy", value.y)
.attr("r", 0)
.style("opacity", 0);
points
.transition().duration(1000)
.attr("cx", value.x)
.attr("cy", value.y)
.attr("r", 2)
.style("opacity", 1);
points.exit()
.transition().duration(1000)
.attr("r", 0)
.style("opacity", 0)
.remove();
}
function updateScales(data) {
var extent = {
x: d3.extent(data, access.x),
y: d3.extent(data, access.y)
};
scale.x.domain([extent.x[0] - 0.5, extent.x[1] + 0.5]);
scale.y.domain([extent.y[0] - 0.5, extent.y[1] + 0.5]);
}
Many thanks!
i know this maybe super duper simple to you guyz but this is the last piece of my project and i am done so i already want to finish this. here is my problem when i first load my gantt chart i draw first the axis and the gridlines before the chart it self so it appears as i desire that the gridlines is the background of my chart but when i redraw my chart there is the problem exist the gantt draw first before the gridlines so the output is the gridlines is covering the gantt chart which looks so bad. i will post my codes so you can check it. I can't pin point where i put first the gridlines. I think i need and extra eye here..........
d3.gantt = function() {
var FIT_TIME_DOMAIN_MODE = "fit";
var FIXED_TIME_DOMAIN_MODE = "fixed";
var margin = {
top : 50,
right : 40,
bottom : 20,
left : 120
};
var timeDomainStart = d3.time.day.offset(new Date(),-3);
var timeDomainEnd = d3.time.hour.offset(new Date(),+3);
var timeDomainMode = FIT_TIME_DOMAIN_MODE;// fixed or fit
var taskTypes = [];
var height = 500 - margin.top - margin.bottom-5;
var width = 1200 - margin.right - margin.left-5;
var tickFormat = "%H:%M";
var keyFunction = function(d) {
return d.startDate + d.taskName + d.endDate;
};
var rectTransform = function(d) {
return "translate(" + x(d.startDate) + "," + y(d.taskName) + ")";
};
var x = d3.time.scale().domain([ timeDomainStart, timeDomainEnd ]).range([ 0, width ]).clamp(true);
var y = d3.scale.ordinal().domain(taskTypes).rangeRoundBands([ 0, height - margin.top - margin.bottom ], .1);
var xAxis = d3.svg.axis().scale(x).orient("bottom").tickFormat(d3.time.format(tickFormat)).tickSubdivide(true).tickSize(8).tickPadding(8);
var yAxis = d3.svg.axis().scale(y).orient("left").tickSize(0);
var initTimeDomain = function(tasks) {
if (timeDomainMode === FIT_TIME_DOMAIN_MODE) {
if (tasks === undefined || tasks.length < 1) {
timeDomainStart = d3.time.day.offset(new Date(), -3);
timeDomainEnd = d3.time.hour.offset(new Date(), +3);
return;
}
tasks.sort(function(a, b) {
return a.endDate - b.endDate;
});
timeDomainEnd = tasks[tasks.length - 1].endDate;
tasks.sort(function(a, b) {
return a.startDate - b.startDate;
});
timeDomainStart = tasks[0].startDate;
}
};
var xAxisGrid = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickSize(-height + margin.top + margin.bottom, 0, 0)
.tickFormat("");
var initAxis = function() {
x = d3.time.scale().domain([ timeDomainStart, timeDomainEnd ]).range([ 0, width ]).clamp(true);
y = d3.scale.ordinal().domain(taskTypes).rangeRoundBands([ 0, height - margin.top - margin.bottom ], .1);
xAxis = d3.svg.axis().scale(x).orient("bottom").tickFormat(d3.time.format(tickFormat)).tickSubdivide(true)
.tickSize(8).tickPadding(8);
yAxis = d3.svg.axis().scale(y).orient("left").tickSize(0);
xAxisGrid = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickSize(-height + margin.top + margin.bottom, 0, 0)
.tickFormat("");
};
/////////////////////////////////
//Creating the chart
////////////////////////////
function gantt(tasks) {
initTimeDomain(tasks);
initAxis();
var dateFormat = d3.time.format("%Y-%m-%d");
var svg = d3.select("#gantt_chart")
.append("svg")
.attr("class", "chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("class", "gantt-chart")
.attr("width", width + margin.left + margin.right)
.attr("height", (height + margin.top + margin.bottom) / tasks[tasks.length - 1].endDate)
.attr("transform", "translate(" + margin.left + ", " + margin.top + ")");
var div = d3.select("body").append("div")
.attr("class","tooltip")
.style("opacity",0);
//this is the x-axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0, " + (height - margin.top - margin.bottom) + ")")
.transition()
.call(xAxis)
.selectAll("text")
.style("text-anchor","end")
.attr("dx", 35)
.attr("dy", 5);
//.attr("dx", "-.8em")
//.attr("dy", -10)
//.attr("transform", function(d){return "rotate(-90)"});
//this is the y-axis
svg.append("g").attr("class", "y axis").transition().call(yAxis);
// title of the gantt
svg.append("g")
.append("text")
.attr("x", 300)
.attr("y", -30)
.attr("class","title")
.style("font-size",20)
.text("MINI-PM SCHEDULER GANTT CHART");
// y title
svg.append("g")
.append("text")
.attr("transform", "rotate(-90)")
.attr("dx", -220)
.attr("dy", -100)
.style("font-size",16)
.text("HANDLER ID");
//this is the legend part
var colors = [["RUNNING", "#669900"],["WARNING", "#ffbb33"],["DOWN", "#FF0000"]];
var legend = svg.append("g")
.attr("class", "legend");
var circle = legend.selectAll('circle')
.data(colors)
.enter()
.append("circle")
.attr("cx", function (d, i) {
return (i * 80)+(width/2)-125;
})
.attr("cy", 405)
.attr("r", 5)
.style("fill", function (d) {
return d[1];
});
var legendText = legend.selectAll('text')
.data(colors)
.enter()
.append("text")
.attr("x", function (d, i) {
return (i * 80)+(width/2)-115;
})
.attr("y", 410)
.text(function (d) {
return d[0];
});
// Add X Axis grid lines
svg.append("g")
.attr("class", "grid")
.attr("transform", "translate(0, " + (height - margin.top - margin.bottom) + ")")
.call(xAxisGrid);
//this is the actual gantt
svg.selectAll(".chart")
.data(tasks, keyFunction).enter()
.append("rect")
.attr("rx", 0)
.attr("ry", 0)
.attr("class", function(d){
if(d.status > 75)
{
return "bar-failed";
}
else if (d.status >= 55 && d.status <= 75){
return "bar-killed";
}
else{
return "bar-running";
}
})
.attr("y", 0)
.attr("transform", rectTransform)
.attr("height", function(d) { return y.rangeBand(); })
.attr("width", function(d) {
return (x(d.endDate) - x(d.startDate));
})
.on("mouseover", function(d){
div.transition()
.duration(200)
.style("opacity", .9);
div.html("HandlerID: " + d.taskName + "<br>" + "startDate: " + dateFormat(d.startDate) + "<br/>" + "endDate: " + dateFormat(d.endDate) + "<br/>" + "% Insertions: " + d3.round(d.status,2) + "%")
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout",function(d){
div.transition()
.duration(500)
.style("opacity", 0);
});
/*
svg.append("g")
.append("text")
.attr("x", width / 2)
.attr("y", 380)
.style("font-size",12)
.text("PERIOD"); */
return gantt;
};
gantt.redraw = function(tasks) {
initTimeDomain(tasks);
initAxis();
var dateFormat = d3.time.format("%Y-%m-%d");
var div = d3.select("body").append("div")
.attr("class","tooltip")
.style("opacity",0);
var svg = d3.select("#gantt_chart");
var ganttChartGroup = svg.select(".gantt-chart");
var rect = ganttChartGroup.selectAll("rect").data(tasks, keyFunction);
rect.enter()
.insert("rect",":first-child")
.attr("rx", 0)
.attr("ry", 0)
.attr("class", function(d){
if(d.status > 75)
{
return "bar-failed";
}
else if (d.status >= 55 && d.status <= 75){
return "bar-killed";
}
else{
return "bar-running";
}
})
.transition()
.attr("y", 0)
.attr("transform", rectTransform)
.attr("height", function(d) { return y.rangeBand(); })
.attr("width", function(d) {
return (x(d.endDate) - x(d.startDate));
});
rect.transition()
.attr("transform", rectTransform)
.attr("height", function(d) { return y.rangeBand(); })
.attr("width", function(d) {
return (x(d.endDate) - x(d.startDate));
});
rect.exit().remove();
rect
.on("mouseover", function(d){
div.transition()
.duration(200)
.style("opacity", .9);
div.html("HandlerID: " + d.taskName + "<br>" + "startDate: " + dateFormat(d.startDate) + "<br/>" + "endDate: " + dateFormat(d.endDate) + "<br/>" + "% Insertions: " + d3.round(d.status,2) + "%")
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout",function(d){
div.transition()
.duration(500)
.style("opacity", 0);
});
svg.select(".grid").transition().call(xAxisGrid);
svg.select(".x").transition().call(xAxis);
svg.select(".y").transition().call(yAxis);
return gantt;
}; // end of redraw
gantt.margin = function(value) {
if (!arguments.length)
return margin;
margin = value;
return gantt;
};
gantt.timeDomain = function(value) {
if (!arguments.length)
return [ timeDomainStart, timeDomainEnd ];
timeDomainStart = +value[0], timeDomainEnd = +value[1];
return gantt;
};
/**
* #param {string}
* vale The value can be "fit" - the domain fits the data or
* "fixed" - fixed domain.
*/
gantt.timeDomainMode = function(value) {
if (!arguments.length)
return timeDomainMode;
timeDomainMode = value;
return gantt;
};
gantt.taskTypes = function(value) {
if (!arguments.length)
return taskTypes;
taskTypes = value;
return gantt;
};
gantt.taskStatus = function(value) {
if (!arguments.length)
return taskStatus;
taskStatus = value;
return gantt;
};
gantt.width = function(value) {
if (!arguments.length)
return width;
width = +value;
return gantt;
};
gantt.height = function(value) {
if (!arguments.length)
return height;
height = +value;
return gantt;
};
gantt.tickFormat = function(value) {
if (!arguments.length)
return tickFormat;
tickFormat = value;
return gantt;
};
return gantt;
};