Related
I'm trying to implement this D3 code into an empty react project but the graphic is not showing on the browser. I've tried this below code. I also installed D3 version 3 using npm i d3#3
When I just try implement using vanilla html and vanilla JS, it still doesn't work on the local browser. I'm not sure why it's not working. The author's deployed project seem to work well.
import React from "react";
import * as d3 from "d3";
class Aroma extends React.Component {
componentDidMount(){
var margin = {top: 650, right: 650, bottom: 650, left: 650},
radius = Math.min(margin.top, margin.right, margin.bottom, margin.left) - 168;
function filter_min_arc_size_text(d, i) {return (d.dx*d.depth*radius/1)>14};
var hue = d3.scale.category10();
var luminance = d3.scale.sqrt()
.domain([0, 1e6])
.clamp(true)
.range([80, 20]);
var svg = d3.select("body").append("svg")
.attr("width", margin.left + margin.right)
.attr("height", margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var partition = d3.layout.partition()
.sort(function(a, b) { return d3.ascending(a.name, b.name); })
.size([2 * Math.PI, radius]);
var arc = d3.svg.arc()
.startAngle(function(d) { return d.x; })
.endAngle(function(d) { return d.x + d.dx - .01 / (d.depth + .5); })
.innerRadius(function(d) { return (radius + 6) / 3 * d.depth; })
.outerRadius(function(d) { return (radius + 6) / 3 * (d.depth + 1.) - 1; });
svg.append("image")
.attr("xlink:href", "images/grapes.png")
.attr("x", -650)
.attr("y", -650);
//Tooltip description
var tooltip = d3.select("body")
.append("div")
.attr("id", "tooltip")
.style("position", "absolute")
.style("z-index", "10")
.style("opacity", 0);
function format_number(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
function format_description(d) {
var description = d.description;
return '<b>' + d.name + '</b></br>'+ d.description + '<br> (' + format_number(d.value) + ')';
}
function computeTextRotation(d) {
var rotation = (d.x + d.dx / 2) * 180 / Math.PI - 90;
return {
global: rotation,
correction: rotation > 90 ? 180 : 0
};
}
function isRotated(d) {
var rotation = (d.x + d.dx / 2) * 180 / Math.PI - 90;
return rotation > 90 ? true : false
}
function mouseOverArc(d) {
d3.select(this).attr("stroke","black")
tooltip.html(format_description(d));
return tooltip.transition()
.duration(50)
.style("opacity", 0.9);
}
function mouseOutArc(){
d3.select(this).attr("stroke","")
return tooltip.style("opacity", 0);
}
function mouseMoveArc (d) {
return tooltip
.style("top", (d3.event.pageY-10)+"px")
.style("left", (d3.event.pageX+10)+"px");
}
var root_ = null;
d3.json("data/davis-aroma-wheel.json", function(error, root) {
if (error) return console.warn(error);
// Compute the initial layout on the entire tree to sum sizes.
// Also compute the full name and fill color for each node,
// and stash the children so they can be restored as we descend.
partition
.value(function(d) { return d.size; })
.nodes(root)
.forEach(function(d) {
d._children = d.children;
d.sum = d.value;
d.key = key(d);
d.fill = fill(d);
});
// Now redefine the value function to use the previously-computed sum.
partition
.children(function(d, depth) { return depth < 3 ? d._children : null; })
.value(function(d) { return d.sum; });
var center = svg.append("circle")
.attr("r", radius / 3)
.on("click", zoomOut);
center.append("title")
.text("Zoom Out");
var partitioned_data = partition.nodes(root).slice(1)
var path = svg.selectAll("path")
.data(partitioned_data)
.enter().append("path")
.attr("d", arc)
.style("fill", function(d) { return d.fill; })
.each(function(d) { this._current = updateArc(d); })
.on("click", zoomIn)
.on("mouseover", mouseOverArc)
.on("mousemove", mouseMoveArc)
.on("mouseout", mouseOutArc);
var texts = svg.selectAll("text")
.data(partitioned_data)
.enter().append("text")
.filter(filter_min_arc_size_text)
.attr("transform", function(d)
{
var r = computeTextRotation(d);
return "rotate(" + r.global + ")"
+ "translate(" + radius / 3. * d.depth + ")"
+ "rotate(" + -r.correction + ")";
})
.style("font-weight", "bold")
.style("text-anchor", "middle")
.attr("dx", function(d) {return isRotated(d) ? "-85" : "85"}) //margin
.attr("dy", ".35em") // vertical-align
.on("click", zoomIn)
.text(function(d,i) {return d.name})
function zoomIn(p) {
if (p.depth > 1) p = p.parent;
if (!p.children) return;
zoom(p, p);
}
function zoomOut(p) {
if (!p.parent) return;
zoom(p.parent, p);
}
// Zoom to the specified new root.
function zoom(root, p) {
if (document.documentElement.__transition__) return;
// Rescale outside angles to match the new layout.
var enterArc,
exitArc,
outsideAngle = d3.scale.linear().domain([0, 2 * Math.PI]);
function insideArc(d) {
return p.key > d.key
? {depth: d.depth - 1, x: 0, dx: 0} : p.key < d.key
? {depth: d.depth - 1, x: 2 * Math.PI, dx: 0}
: {depth: 0, x: 0, dx: 2 * Math.PI};
}
function outsideArc(d) {
return {depth: d.depth + 1, x: outsideAngle(d.x), dx: outsideAngle(d.x + d.dx) - outsideAngle(d.x)};
}
center.datum(root);
// When zooming in, arcs enter from the outside and exit to the inside.
// Entering outside arcs start from the old layout.
//commented //if (root === p) enterArc = outsideArc, exitArc = insideArc, outsideAngle.range([p.x, p.x + p.dx]);
var new_data=partition.nodes(root).slice(1)
path = path.data(new_data, function(d) { return d.key; });
// When zooming out, arcs enter from the inside and exit to the outside.
// Exiting outside arcs transition to the new layout.
//commented// if (root !== p) enterArc = insideArc, exitArc = outsideArc, outsideAngle.range([p.x, p.x + p.dx]);
d3.transition().duration(d3.event.altKey ? 7500 : 750).each(function() {
path.exit().transition()
.style("fill-opacity", function(d) { return d.depth === 1 + (root === p) ? 1 : 0; })
.attrTween("d", function(d) { return arcTween.call(this, exitArc(d)); })
.remove();
path.enter().append("path")
.style("fill-opacity", function(d) { return d.depth === 2 - (root === p) ? 1 : 0; })
.style("fill", function(d) { return d.fill; })
.on("click", zoomIn)
.on("mouseover", mouseOverArc)
.on("mousemove", mouseMoveArc)
.on("mouseout", mouseOutArc)
.each(function(d) { this._current = enterArc(d); });
path.transition()
.style("fill-opacity", 1)
.attrTween("d", function(d) { return arcTween.call(this, updateArc(d)); });
});
texts = texts.data(new_data, function(d) { return d.key; })
texts.exit()
.remove()
texts.enter()
.append("text")
texts.style("opacity", 0)
.attr("transform", function(d) {
var r = computeTextRotation(d);
return "rotate(" + r.global + ")"
+ "translate(" + radius / 3 * d.depth + ",0)"
+ "rotate(" + -r.correction + ")";
})
.style("font-weight", "bold")
.style("text-anchor", "middle")
.attr("dx", function(d) {return isRotated(d) ? "-85" : "85"}) //margin
.attr("dy", ".35em") // vertical-align
.filter(filter_min_arc_size_text)
.on("click", zoomIn)
.text(function(d,i) {return d.name})
.transition().delay(750).style("opacity", 1)
}
});
function key(d) {
var k = [], p = d;
//while (p.depth) k.push(p.name), p = p.parent;
return k.reverse().join(".");
}
function fill(d) {
var p = d;
while (p.depth > 1) p = p.parent;
var c = d3.lab(hue(p.name));
c.l = luminance(d.sum);
return c;
}
function arcTween(b) {
var i = d3.interpolate(this._current, b);
this._current = i(0);
return function(t) {
return arc(i(t));
};
}
function updateArc(d) {
return {depth: d.depth, x: d.x, dx: d.dx};
}
d3.select(this.frameElement).style("height", margin.top + margin.bottom + "px");
}
render(){
return <p id="aroma" />;
}
}
export default Aroma;
I emailed the original D3 repo author and he kindly got back to me, mentioning that there's an error with d3 version 3, that it needs longer investigation. He sent me all the resources that he used to build his project 3 years ago.
So I simply decided to follow this action.
Zoomable sunburst chart shows only two layers of the hierarchy at a time in React JS
I notice that in much of the D3 documentation the charts, graphs, margins and so on are often hard coded. The xAxis is 500px, etc.. This isn't very helpful for me. So I'm trying to think of how I can accomplish a dynamic approach to rendering D3 content in React.
For example, in the following code I am simply rendering a line based on some time-series stock-price data. I have some D3 code in componentDidMount but given the way D3 works it wants concrete width and height values. But in componentDidMount I don't have those values yet. Lets say that this single line plot is one of 100 other plots each in a div in a grid layout.
So how can I get the width/height of the div/svg and only then compute my D3 code and render svgs?
componentDidMount() {
console.log("componentDidMount")
const data = this.props.data;
const selectX = this.props.selectX;
const selectY = this.props.selectY;
console.log(data)
const xScale = d3ScaleTime()
.domain(d3ArrayExtent(data, selectX))
.range([0, 1]);
const yScale = d3ScaleTime()
.domain(d3ArrayExtent(data, selectY))
.range([1, 0]);
const xAxis = d3AxisBottom()
.scale(xScale)
.ticks(data.length / 8);
const yAxis = d3AxisLeft()
.scale(yScale)
.ticks(3);
const selectScaledX = datum => xScale(selectX(datum));
const selectScaledY = datum => yScale(selectY(datum));
const sparkLine = d3Line()
.x(selectScaledX)
.y(selectScaledY);
const linePath = sparkLine(data);
console.log(linePath);
this.setState({
linePath: linePath
});
}
I tried to imitate your issue in the demo below.
class Chart extends React.Component {
componentDidMount() {
var data = this.props.data;
var containerDOMElementWidth = ReactDOM.findDOMNode(this).getBoundingClientRect().width
var chartHeight = containerDOMElementWidth / 2;
this.drawChart(data, containerDOMElementWidth, chartHeight);
}
drawChart(data, chartWidth, chartHeight) {
var margin = { top: 30, right: 20, bottom: 30, left: 50 };
var width = chartWidth - margin.left - margin.right;
var height = chartHeight - margin.top - margin.bottom;
var parseDate = d3.timeParse("%d-%b-%y");
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var xAxis = d3.axisBottom().scale(x)
.ticks(2);
var yAxis = d3.axisLeft().scale(y)
.ticks(2);
var valueline = d3.line()
.x(function (d) {
return x(d.date);
})
.y(function (d) {
return y(d.close);
});
var svg = d3.select("body")
.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 + ")");
data.forEach(function (d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
// Scale the range of the data
x.domain(d3.extent(data, function (d) {
return d.date;
}));
y.domain([0, d3.max(data, function (d) {
return d.close;
})]);
svg.append("path").attr('class', 'line-chart') // Add the valueline path.
.attr("d", valueline(data));
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g") // Add the Y Axis
.attr("class", "y axis")
.call(yAxis);
}
render() {
return <div></div>;
}
}
function getRandomData() {
return [{
date: "1-May-12",
close: Math.random() * 90
}, {
date: "30-Apr-12",
close: Math.random() * 90
}, {
date: "27-Apr-12",
close: Math.random() * 90
}, {
date: "26-Apr-12",
close: Math.random() * 90
}, {
date: "25-Apr-12",
close: Math.random() * 90
}];
}
ReactDOM.render(
<div className="charts-container">
<div className="chart-wrapper">
<Chart data={getRandomData()} />
</div>
<div className="chart-wrapper">
<Chart data={getRandomData()} />
</div>
</div>,
document.getElementById('container')
);
.line-chart {
fill: none;
stroke: blue
}
.charts-container {
display: flex;
}
.chart-wrapper {
width: 100%;
}
<script src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.11.0/d3.min.js"></script>
<div id="container">
</div>
Here we draw two charts one by one and calculate their width and height this way:
componentDidMount() {
var data = this.props.data;
// gets the width of container div element with ReactDOM.findDOMNode
var containerDOMElementWidth = ReactDOM.findDOMNode(this).getBoundingClientRect().width
// chart height have to be a half of width
var chartHeight = containerDOMElementWidth / 2;
// pass width and height as an arguments
this.drawChart(data, containerDOMElementWidth, chartHeight);
}
Our drawChart method look like:
drawChart(data, chartWidth, chartHeight) {
var margin = { top: 30, right: 20, bottom: 30, left: 50 };
var width = chartWidth - margin.left - margin.right;
var height = chartHeight - margin.top - margin.bottom;
... // code for the chart drawing
render:
ReactDOM.render(
<div className="charts-container">
<div className="chart-wrapper">
<Chart data={getRandomData()} />
</div>
<div className="chart-wrapper">
<Chart data={getRandomData()} />
</div>
</div>,
document.getElementById('container')
);
If we will render only one chart it also works fine without any code changing because of we calculate the width of the chart as width of parent div element:
class Chart extends React.Component {
componentDidMount() {
var data = this.props.data;
var containerDOMElementWidth = ReactDOM.findDOMNode(this).getBoundingClientRect().width
var chartHeight = containerDOMElementWidth / 2;
this.drawChart(data, containerDOMElementWidth, chartHeight);
}
drawChart(data, chartWidth, chartHeight) {
var margin = { top: 30, right: 20, bottom: 30, left: 50 };
var width = chartWidth - margin.left - margin.right;
var height = chartHeight - margin.top - margin.bottom;
var parseDate = d3.timeParse("%d-%b-%y");
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var xAxis = d3.axisBottom().scale(x)
.ticks(2);
var yAxis = d3.axisLeft().scale(y)
.ticks(2);
var valueline = d3.line()
.x(function (d) {
return x(d.date);
})
.y(function (d) {
return y(d.close);
});
var svg = d3.select("body")
.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 + ")");
data.forEach(function (d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
// Scale the range of the data
x.domain(d3.extent(data, function (d) {
return d.date;
}));
y.domain([0, d3.max(data, function (d) {
return d.close;
})]);
svg.append("path").attr('class', 'line-chart') // Add the valueline path.
.attr("d", valueline(data));
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g") // Add the Y Axis
.attr("class", "y axis")
.call(yAxis);
}
render() {
return <div></div>;
}
}
function getRandomData() {
return [{
date: "1-May-12",
close: Math.random() * 90
}, {
date: "30-Apr-12",
close: Math.random() * 90
}, {
date: "27-Apr-12",
close: Math.random() * 90
}, {
date: "26-Apr-12",
close: Math.random() * 90
}, {
date: "25-Apr-12",
close: Math.random() * 90
}];
}
ReactDOM.render(
<div className="charts-container">
<div className="chart-wrapper">
<Chart data={getRandomData()} />
</div>
</div>,
document.getElementById('container')
);
.line-chart {
fill: none;
stroke: blue
}
.charts-container {
display: flex;
}
.chart-wrapper {
width: 100%;
}
<script src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.11.0/d3.min.js"></script>
<div id="container">
</div>
The same thing for three charts:
class Chart extends React.Component {
componentDidMount() {
var data = this.props.data;
var containerDOMElementWidth = ReactDOM.findDOMNode(this).getBoundingClientRect().width
var chartHeight = containerDOMElementWidth / 2;
this.drawChart(data, containerDOMElementWidth, chartHeight);
}
drawChart(data, chartWidth, chartHeight) {
var margin = { top: 30, right: 20, bottom: 30, left: 50 };
var width = chartWidth - margin.left - margin.right;
var height = chartHeight - margin.top - margin.bottom;
var parseDate = d3.timeParse("%d-%b-%y");
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var xAxis = d3.axisBottom().scale(x)
.ticks(2);
var yAxis = d3.axisLeft().scale(y)
.ticks(2);
var valueline = d3.line()
.x(function (d) {
return x(d.date);
})
.y(function (d) {
return y(d.close);
});
var svg = d3.select("body")
.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 + ")");
data.forEach(function (d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
// Scale the range of the data
x.domain(d3.extent(data, function (d) {
return d.date;
}));
y.domain([0, d3.max(data, function (d) {
return d.close;
})]);
svg.append("path").attr('class', 'line-chart') // Add the valueline path.
.attr("d", valueline(data));
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g") // Add the Y Axis
.attr("class", "y axis")
.call(yAxis);
}
render() {
return <div></div>;
}
}
function getRandomData() {
return [{
date: "1-May-12",
close: Math.random() * 90
}, {
date: "30-Apr-12",
close: Math.random() * 90
}, {
date: "27-Apr-12",
close: Math.random() * 90
}, {
date: "26-Apr-12",
close: Math.random() * 90
}, {
date: "25-Apr-12",
close: Math.random() * 90
}];
}
ReactDOM.render(
<div className="charts-container">
<div className="chart-wrapper">
<Chart data={getRandomData()} />
</div>
<div className="chart-wrapper">
<Chart data={getRandomData()} />
</div>
<div className="chart-wrapper">
<Chart data={getRandomData()} />
</div>
</div>,
document.getElementById('container')
);
.line-chart {
fill: none;
stroke: blue
}
.charts-container {
display: flex;
}
.chart-wrapper {
width: 100%;
}
<script src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.11.0/d3.min.js"></script>
<div id="container">
</div>
Here is my code snippet for implementing the ellipsis on x axis labels in d3 v3.
Can someone help me figure out the changes needed to my code in v4??
//function for adding '...' if length is more than 50 px
var wrap = function () {
var self = d3.select(this),
textLength = self.node().getComputedTextLength(),
text = self.text();
while (textLength > (50) && text.length > 0) {
text = text.slice(0, -1);
self.text(text + '...');
textLength = self.node().getComputedTextLength();
}
};
//calling the function
svg.select('.x.axis')
.call(xAxis)
.selectAll('text')
.style('text-anchor', 'end')
.attr('fill', '#8a9299')
.attr('transform', 'rotate(-60)')
.each(wrap);
You just need to save the group element containing x-axis into a variable and use as shown below.
var wrap = function() {
var self = d3.select(this),
textLength = self.node().getComputedTextLength(),
text = self.text();
while (textLength > (50) && text.length > 0) {
text = text.slice(0, -1);
self.text(text + '...');
textLength = self.node().getComputedTextLength();
}
};
//calling the function
xAxis.selectAll('text')
.style('text-anchor', 'end')
.attr('fill', '#8a9299')
.attr('transform', 'rotate(-60)')
.each(wrap);
// set the dimensions and margins of the graph
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = 250 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom;
// set the ranges
var x = d3.scaleBand()
.range([0, width])
.padding(0.1);
var y = d3.scaleLinear()
.range([height, 0]);
// append the svg object to the body of the page
// append a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("body").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 + ")");
var data = [{
"salesperson": "Bob Patrickson",
"sales": 33
},
{
"salesperson": "Robin Dearden",
"sales": 12
},
{
"salesperson": "Anne Hathaway",
"sales": 41
},
{
"salesperson": "Mark Wahlberg",
"sales": 16
}
];
// format the data
data.forEach(function(d) {
d.sales = +d.sales;
});
// Scale the range of the data in the domains
x.domain(data.map(function(d) {
return d.salesperson;
}));
y.domain([0, d3.max(data, function(d) {
return d.sales;
})]);
// append the rectangles for the bar chart
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) {
return x(d.salesperson);
})
.attr("width", x.bandwidth())
.attr("y", function(d) {
return y(d.sales);
})
.attr("height", function(d) {
return height - y(d.sales);
});
// add the x Axis
var xAxis = svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// add the y Axis
svg.append("g")
.call(d3.axisLeft(y));
var wrap = function() {
var self = d3.select(this),
textLength = self.node().getComputedTextLength(),
text = self.text();
while (textLength > (50) && text.length > 0) {
text = text.slice(0, -1);
self.text(text + '...');
textLength = self.node().getComputedTextLength();
}
};
//calling the function
xAxis.selectAll('text')
.style('text-anchor', 'end')
.attr('fill', '#8a9299')
.attr('transform', 'rotate(-60)')
.each(wrap);
.bar {
fill: steelblue;
}
<script src="//d3js.org/d3.v4.min.js"></script>
I'm new to D3. Trying to show "outside" labels with lines, in D3, similar to Bostock's pie chart here http://bl.ocks.org/dbuezas/9306799 I can't get the labels or lines to show up like Bostock's pie. The pie is working well though! Any help with my code, mainly the directive, would be appreciated!
HTML:
<div ng-controller="myControl">
<d3-pie-dir data="d3Data"></d3-pie-dir>
</div>
CSS:
path.slice{
stroke-width:2px;
}
polyline{
opacity: .3;
stroke: black;
stroke-width: 2px;
fill: none;
}
Controller:
inAng.controller('myControl', function ( $scope, $http ) {
var stack = [];
$http.get("/getAPIData").
success(function (data, status, headers, config) {
// formatting for easier D3 consumption
for(var i in data)
stack.push(data [i]);
$scope.d3Data = stack;
// looks something like $scope.d3Data = [{ name: 'Bill', score: 25}, { name: 'Pete', score: 50}]
}).
error(function (data, status, headers, config) {
$scope.stack = 'Error!';
});
});
And the problem is in the directive:
inAng.directive('d3PieDir', function () {
return {
restrict: 'E',
scope: {
data: '='
},
link: function (scope, element, attrs) {
scope.$watch('data', function(values) {
if(values) {
console.log('values from directive: ', values);
var width = 960,
height = 500,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.score;
});
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
values.forEach(function(d) {
d.score = +d.score;
});
var g = svg.selectAll(".arc")
.data(pie(values))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return color(d.data.name); });
// **** Below is where it stops working! ****
var key = function(d){ return d.data.name; };
/* ------- PIE SLICES -------*/
var slice = svg.select(".slices").selectAll("path.slice")
.data(pie(values), key);
slice.enter()
.insert("path")
.style("fill", function(d) { return color(d.data.name); })
.attr("class", "slice");
slice
.transition().duration(1000)
.attrTween("d", function(d) {
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
return arc(interpolate(t));
};
})
slice.exit()
.remove();
/* ------- TEXT LABELS -------*/
var text = svg.select(".labels").selectAll("text")
.data(pie(values), key);
text.enter()
.append("text")
.attr("dy", ".35em")
.text(function(d) {
return d.data.name;
});
function midAngle(d){
return d.startAngle + (d.endAngle - d.startAngle)/2;
}
text.transition().duration(1000)
.attrTween("transform", function(d) {
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
var d2 = interpolate(t);
var pos = outerArc.centroid(d2);
pos[0] = radius * (midAngle(d2) < Math.PI ? 1 : -1);
return "translate("+ pos +")";
};
})
.styleTween("text-anchor", function(d){
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
var d2 = interpolate(t);
return midAngle(d2) < Math.PI ? "start":"end";
};
});
text.exit()
.remove();
/* ------- SLICE TO TEXT POLYLINES -------*/
var polyline = svg.select(".lines").selectAll("polyline")
.data(pie(values), key);
polyline.enter()
.append("polyline");
polyline.transition().duration(1000)
.attrTween("points", function(d){
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
var d2 = interpolate(t);
var pos = outerArc.centroid(d2);
pos[0] = radius * 0.95 * (midAngle(d2) < Math.PI ? 1 : -1);
return [arc.centroid(d2), outerArc.centroid(d2), pos];
};
});
polyline.exit()
.remove();
}
})}
}});
You have two problems the first one is that you forgot to append these elements to your svg
svg.append("g")
.attr("class", "slices");
svg.append("g")
.attr("class", "labels");
svg.append("g")
.attr("class", "lines");
these are the g elements that d3 draws lines and labels on , so you need to put them after
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
,, the second issue is that you forgot to define outer arc ,,
var outerArc = d3.svg.arc()
.innerRadius(radius * 0.9)
.outerRadius(radius * 0.9);
which you use in your calculation for creating lines
How to use angular directives to load the d3.js graph using scope json data instead of using graph.html file.I had referred this urlurl.But unable to do it for line chart.
Can anyone please help me out regarding this issue ...
My graph.html:
function getDate(d) {
var dt = new Date(d.date);
dt.setHours(0);
dt.setMinutes(0);
dt.setSeconds(0);
dt.setMilliseconds(0);
return dt;
}
function showData(obj, d) {
var coord = d3.mouse(obj);
var infobox = d3.select(".infobox");
// now we just position the infobox roughly where our mouse is
infobox.style("left", (coord[0] + 100) + "px" );
infobox.style("top", (coord[1] - 175) + "px");
$(".infobox").html(d);
$(".infobox").show();
}
function hideData() {
$(".infobox").hide();
}
var drawChart = function(data) {
// define dimensions of graph
var m = [10, 20, 10, 50]; // margins
var w = 250 - m[1] - m[3]; // width
var h = 100 - m[0] - m[2]; // height
data.sort(function(a, b) {
var d1 = getDate(a);
var d2 = getDate(b);
if (d1 == d2) return 0;
if (d1 > d2) return 1;
return -1;
});
var minDate = getDate(data[0]),
maxDate = getDate(data[data.length-1]);
var x = d3.time.scale().domain([minDate, maxDate]).range([0, w]);
var y = d3.scale.linear().domain([0, d3.max(data, function(d) { return d.trendingValue; } )]).range([h, 0]);
var line = d3.svg.line()
.x(function(d, i) {
return x(getDate(d)); //x(i);
})
.y(function(d) {
return y(d.trendingValue);
});
function xx(e) { return x(getDate(e)); };
function yy(e) { return y(e.trendingValue); };
var graph = d3.select("#chart").append("svg:svg")
.attr("width", w + m[1] + m[3])
.attr("height", h + m[0] + m[2])
.append("svg:g")
.attr("transform", "translate(" + m[3] + "," + m[0] + ")");
var xAxis = d3.svg.axis().scale(x).ticks(d3.time.months, 1).tickSize(-h).tickSubdivide(true);
var yAxisLeft = d3.svg.axis().scale(y).ticks(10).orient("left"); //.tickFormat(formalLabel);
graph
.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("fill", "steelblue")
.attr("r", 5)
.attr("cx", xx)
.attr("cy", yy)
.on("mouseover", function(d) { showData(this, d.trendingValue);})
.on("mouseout", function(){ hideData();});
graph.append("svg:path").attr("d", line(data));
$("#chart").append("<div class='infobox' style='display:none;'>Test</div>");
}
My directive:(which I had tried but unable to draw a graph)
angular.module( 'chart').directive( 'crD3Bars', [
function () {
return {
restrict: 'E',
scope: {
data: '='
},
link: function (scope, element) {
function getDate(d) {
var dt = new Date(d.date);
dt.setHours(0);
dt.setMinutes(0);
dt.setSeconds(0);
dt.setMilliseconds(0);
return dt;
}
function showData(obj, d) {
var coord = d3.mouse(obj);
var infobox = d3.select(".infobox");
// now we just position the infobox roughly where our mouse is
infobox.style("left", (coord[0] + 100) + "px" );
infobox.style("top", (coord[1] - 175) + "px");
$(".infobox").html(d);
$(".infobox").show();
}
function hideData() {
$(".infobox").hide();
}
var drawChart = function(data) {
// define dimensions of graph
var m = [10, 20, 10, 50]; // margins
var w = 250 - m[1] - m[3]; // width
var h = 100 - m[0] - m[2]; // height
data.sort(function(a, b) {
var d1 = getDate(a);
var d2 = getDate(b);
if (d1 == d2) return 0;
if (d1 > d2) return 1;
return -1;
});
var minDate = getDate(data[0]),
maxDate = getDate(data[data.length-1]);
var x = d3.time.scale().domain([minDate, maxDate]).range([0, w]);
var y = d3.scale.linear().domain([0, d3.max(data, function(d) { return d.trendingValue; } )]).range([h, 0]);
var line = d3.svg.line()
.x(function(d, i) {
return x(getDate(d)); //x(i);
})
.y(function(d) {
return y(d.trendingValue);
});
function xx(e) { return x(getDate(e)); };
function yy(e) { return y(e.trendingValue); };
var graph = d3.select("#chart").append("svg:svg")
.attr("width", w + m[1] + m[3])
.attr("height", h + m[0] + m[2])
.append("svg:g")
.attr("transform", "translate(" + m[3] + "," + m[0] + ")");
var xAxis = d3.svg.axis().scale(x).ticks(d3.time.months, 1).tickSize(-h).tickSubdivide(true);
var yAxisLeft = d3.svg.axis().scale(y).ticks(10).orient("left"); //.tickFormat(formalLabel);
graph
.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("fill", "steelblue")
.attr("r", 5)
.attr("cx", xx)
.attr("cy", yy)
.on("mouseover", function(d) { showData(this, d.trendingValue);})
.on("mouseout", function(){ hideData();});
graph.append("svg:path").attr("d", line(data));
$("#graphDiv3").append("<div class='infobox' style='display:none;'>Test</div>");
}
drawchart(data);
}
};
}
]);
Instead of mashing up two blocks of code and hoping it works, I'd recommend you follow some simple angular tutorials to get a better grasp of the basics. Also, some simple debugging would have gone a long way here.
You do not declare your module.
You do not seem to pass in your data anywhere (like in a controller which also doesn't exist).
The function is drawChart, you are calling drawchart
In your directive, you select a div with id of chart, this doesn't exist. Since it's a directive and they act on elements use d3.select(element[0])
All that said once you work through these relatively simple mistakes, you get some working code:
var myAppModule = angular.module('chart', []);
angular.module('chart').controller('chartCtrl', function ($scope) {
$scope.myData = [{
"date": "2015-10-01",
"trendingValue": "244"
},
{
"date": "2015-07-01",
"trendingValue": "0"
},
{
"date": "2015-06-01",
"trendingValue": "117"
},
{
"date": "2015-05-01",
"trendingValue": "5353"
},
{
"date": "2015-04-01",
"trendingValue": "11159"
},
{
"date": "2015-03-01",
"trendingValue": "7511"
},
{
"date": "2015-02-01",
"trendingValue": "6906"
},
{
"date": "2015-01-01",
"trendingValue": "10816"
},
{
"date": "2014-12-01",
"trendingValue": "3481"
},
{
"date": "2014-11-01",
"trendingValue": "1619"
},
{
"date": "2014-10-01",
"trendingValue": "4084"
},
{
"date": "2014-09-01",
"trendingValue": "1114"
}];
});
angular.module('chart').directive('crD3Bars', [
function() {
return {
restrict: 'E',
scope: {
data: '='
},
link: function(scope, element) {
function getDate(d) {
var dt = new Date(d.date);
dt.setHours(0);
dt.setMinutes(0);
dt.setSeconds(0);
dt.setMilliseconds(0);
return dt;
}
function showData(obj, d) {
var coord = d3.mouse(obj);
var infobox = d3.select(".infobox");
// now we just position the infobox roughly where our mouse is
infobox.style("left", (coord[0] + 100) + "px");
infobox.style("top", (coord[1] - 175) + "px");
$(".infobox").html(d);
$(".infobox").show();
}
function hideData() {
$(".infobox").hide();
}
var drawChart = function(data) {
// define dimensions of graph
var m = [10, 20, 10, 50]; // margins
var w = 250 - m[1] - m[3]; // width
var h = 100 - m[0] - m[2]; // height
data.sort(function(a, b) {
var d1 = getDate(a);
var d2 = getDate(b);
if (d1 == d2) return 0;
if (d1 > d2) return 1;
return -1;
});
var minDate = getDate(data[0]),
maxDate = getDate(data[data.length - 1]);
var x = d3.time.scale().domain([minDate, maxDate]).range([0, w]);
var y = d3.scale.linear().domain([0, d3.max(data, function(d) {
return d.trendingValue;
})]).range([h, 0]);
var line = d3.svg.line()
.x(function(d, i) {
return x(getDate(d)); //x(i);
})
.y(function(d) {
return y(d.trendingValue);
});
function xx(e) {
return x(getDate(e));
}
function yy(e) {
return y(e.trendingValue);
}
var graph = d3.select(element[0]).append("svg:svg")
.attr("width", w + m[1] + m[3])
.attr("height", h + m[0] + m[2])
.append("svg:g")
.attr("transform", "translate(" + m[3] + "," + m[0] + ")");
var xAxis = d3.svg.axis().scale(x).ticks(d3.time.months, 1).tickSize(-h).tickSubdivide(true);
var yAxisLeft = d3.svg.axis().scale(y).ticks(10).orient("left"); //.tickFormat(formalLabel);
graph
.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("fill", "steelblue")
.attr("r", 5)
.attr("cx", xx)
.attr("cy", yy)
.on("mouseover", function(d) {
showData(this, d.trendingValue);
})
.on("mouseout", function() {
hideData();
});
graph.append("svg:path").attr("d", line(data));
$("#graphDiv3").append("<div class='infobox' style='display:none;'>Test</div>");
};
drawChart(scope.data);
}
};
}
]);
<!DOCTYPE html>
<html ng-app="chart">
<head>
<script data-require="angular.js#1.4.8" data-semver="1.4.8" src="https://code.angularjs.org/1.4.8/angular.js"></script>
<script data-require="d3#3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
<script data-require="jquery#2.1.4" data-semver="2.1.4" src="https://code.jquery.com/jquery-2.1.4.js"></script>
<script src="script.js"></script>
</head>
<body>
<div id="graphDiv3" ng-controller="chartCtrl">
<cr-d3-bars data="myData"></cr-d3-bars>
</div>
</body>
</html>