I'm trying to use d3 and d3-cloud to display a keyword cloud. It works, but my only problem is that the words overlap, And I dont understand why. I think it comes from the fontSize, but I can't find what exactly. screen here
Below are my different methods :
private _populate(keyword: any) {
const dataset = kw.map(function (d: any) { return { text: d.text, size: d.value }; });
console.log(dataset);
d3.layout.cloud()
.size([this._width, this._height])
.words(dataset)
.padding(1)
.rotate(0)
.fontSize((d: any) => d.size / 100)
.on('end', () => {
this._drawWordCloud(dataset);
})
.start();
}
private _drawWordCloud(words: any) {
this._svg
.append("g")
.attr("transform", "translate(" + this._width / 2 + "," + this._height / 2 + ")")
.selectAll("text")
.data(words)
.enter().append("text")
.style("font-size", (d: any) => d.size + "px")
.style("fill", "#69b3a2")
.style("font-family", "Impact")
.attr("text-anchor", "middle")
.attr("transform", (d: any) => "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")")
.text((d: any) => d.text);
}
.fontSize((d: any) => d.size / 100) because my values are very high (~ 10000).
Someone can help me ? The various solutions (very few) that I have found do not work.
Thanks !
I finally found the solution. You have to add the font you want to use in the first method "_populate". In my case :
.font("Impact")
And we don't change the line in the second method :
.style("font-family", "Impact")
That's all ! I hope it can help other people
Related
I'm developing a Bar Chart with D3.js and I'm using Observable to fetch the value from API.
ngOnInit() {
this.dataCollectionService
.getAllCurrencyData()
.subscribe(
(currData) => {
this.currencyData = currData;
});
this.initSvg();
this.initAxis();
this.drawAxis();
this.drawBars();
}
private initSvg() {
this.svg = d3.select("svg");
this.width = +this.svg.attr("width") - this.margin.left - this.margin.right;
this.height = +this.svg.attr("height") - this.margin.top - this.margin.bottom;
this.g = this.svg.append("g")
.attr("transform", "translate(" + this.margin.left + "," + this.margin.top + ")");
}
private initAxis() {
this.x = d3Scale.scaleBand().rangeRound([0, this.width]).padding(0.1);
this.y = d3Scale.scaleLinear().rangeRound([this.height, 0]);
this.x.domain(this.currencyData.map((d) => d.symbol));
this.y.domain([0, d3Array.max(this.currencyData, (d) => d.price_usd)]);
}
private drawAxis() {
this.g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + this.height + ")")
.call(d3Axis.axisBottom(this.x));
this.g.append("g")
.attr("class", "axis axis--y")
.call(d3Axis.axisLeft(this.y).ticks(5))
.append("text")
.attr("class", "axis-title")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("Dollar Value");
}
private drawBars() {
this.g.selectAll(".bar")
.data(this.currencyData)
.enter().append("rect")
.attr("class", "bar")
.attr("x", (d) => this.x(d.symbol) )
.attr("y", (d) => this.y(d.price_usd) )
.attr("width", this.x.bandwidth())
.attr("height", (d) => this.height - this.y(d.price_usd) );
}
Problem I'm facing is, all the chart methods are getting executed before this.currencyData subscribes to all the values. Hence no chat is getting displayed I put all chart methods inside subscribe but it dint help. I also tried with putting the service call inside constructor but no luck.
Any solution for this issue please?
I am trying to do three transitions in onClick event of a Pie Chart. The first transition works but the second and the third one fail. I understood from Mike Bostocks comment on a similar question that "This means you are trying to modify a transition that has already started, or derive a transition from one that has ended. Please read the API Reference section on transition life cycles."
I still cant seem to understand the reason why this is happening.
Here is the relevant code:
self.primaryLabelText = self.arc.append("text")
.on("click", function (d: any) {
console.log("About to send::::" + getStudyLabel(d.index));
self.selectedIndustryTypeService.sendMessage(getStudyLabel(d.index));
self.showDialog();
// The amount we need to rotate:
var rotate = 180-(d.startAngle + d.endAngle)/2 / Math.PI * 180;
// Transition the pie chart
g.transition()
.attr("transform", "translate(" + self.width / 2 + "," + self.height / 2 + ") rotate(" + rotate + ")")
.duration(1000);
// Ī¤ransition the labels:
self.primaryLabelText.transition()
.attr("transform", function(dd: any) {
return "translate(" + label.centroid(dd) + ") rotate(" + (-rotate) + ")"; })
.duration(1000);
self.secondaryLabelText.transition()
.attr("transform", function(dd: any) {
return "translate(" + label.centroid(dd) + ") rotate(" + (-rotate) + ")"; })
.duration(1000);
})
.transition()
.duration(750)
.attr("transform", function (d: any) {
return "translate(" + label.centroid(d) + ")";
})
.attr("dy", "-0.75em")
.attr("font-family", "sans-serif")
.attr("font-size", "15px")
.attr("text-anchor", "middle")
.attr("class", "sponsor-pie-widget-label")
.text(function (d: any) {
if (d.endAngle - d.startAngle < 0.3) {
return "";
} else {
return getStudyLabel(d.index);
}
});
What is the issue here?
The problem was that the primaryLabel and secondaryLabel I was trying to do transition on were not assigned correctly. I had the following assignment:
self.primaryLabelText = self.arc.append("text")
.on("click", function (d: any) {
self.rotateChart(d);
}).transition()
.duration(750)
.attr("transform", function (d: any) {
return "translate(" + self.label.centroid(d) + ")";
})
.attr("dy", "-0.75em")
.attr("font-family", "sans-serif")
.attr("font-size", "15px")
.attr("text-anchor", "middle")
.attr("class", "sponsor-pie-widget-label")
.text(function (d: any) {
if (d.endAngle - d.startAngle < 0.3) {
return "";
} else {
return getStudyLabel(d.index);
}
});
As a result of this, the variables contained the reference to the transitions and not the labels themselves as when you chain transitions it gets changed to a transition type object. So I changed that to this:
self.primaryLabelText = self.arc.append("text")
.on("click", function (d: any) {
self.rotateChart(d);
});
self.primaryLabelText.transition()
.duration(750)
.attr("transform", function (d: any) {
return "translate(" + self.label.centroid(d) + ")";
})
.attr("dy", "-0.75em")
.attr("font-family", "sans-serif")
.attr("font-size", "15px")
.attr("text-anchor", "middle")
.attr("class", "sponsor-pie-widget-label")
.text(function (d: any) {
if (d.endAngle - d.startAngle < 0.3) {
return "";
} else {
return getStudyLabel(d.index);
}
});
It all works now! :)
I'm building a wordcloud using Jason Davies d3.layout.cloud() https://github.com/jasondavies/d3-cloud.git
I decided to add a transition and duration attributes to make the wordcloud look better when it arrives on the screen . However, I can't use on("click") attribute anymore since it sends me an error :
Uncaught TypeError: undefined is not a function
It refers to the line .on("click", function (d) {alert('ok');})
When I remove the transition and duration, the on click works correctly .
Here is the js code :
var fill = d3.scale.category20();
var layout = d3.layout.cloud().size([1500, 800])
.words(frequency_list)
.padding(5)
.rotate(function() {return ~~(Math.random() -0.5) * 120;})
.font("Impact")
.fontSize(function(d) { return d.size; })
.on("end", draw);
layout.start();
function draw(words) {
d3.select("svg").remove();
d3.select("body").append("svg")
.attr("width", layout.size()[0])
.attr("height", layout.size()[1])
.append("g")
.attr("transform", "translate(" + layout.size()[0] / 2 + "," + layout.size()[1] / 2 + ")")
.selectAll("text")
.data(words)
.enter().append("text")
.transition()
.duration(function(d) { return d.time} )
.attr('opacity', 1)
.style("font-size", function(d) { return d.size + "px"; })
.style("font-family", "Impact")
.style("fill", function(d, i) { return fill(i); })
.attr("text-anchor", "middle")
.attr("transform", function(d) {
return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
})
.style("cursor", "pointer")
.text(function(d) { return d.text; })
.on("click", function (d) {alert('ok');});
frequency_list is a list whose elements contain the attributes "text", "size", and "time" .
I don't know how to solve this .
Any help would be appraciated :)
Taken from my answer to a similar question: Since transitions are a special kind of selections, you cannot use all methods available to a selection on a transition. In your case this means, you are not allowed to use on() to register event handlers on a transition. Instead, use transition.each() to bind your handler to the elements in the transition.
.transition()
// rest of your code
.each(function () {
d3.select(this).on("click", function (d) {alert('ok');});
});
I want to create word tag cloud using d3 .. I found a good example here but the problem that the words go outside the borders especially in case of large fonts and this screenshot for the problem
and here's my code
<script src="../lib/d3/d3.js"></script>
<script src="../d3.layout.cloud.js"></script>
<script>
var fill = d3.scale.category20();
d3.layout.cloud().size([600, 600])
.words([
"Hello", "world", "normally", "you", "want", "more", "words",
"than", "this"
].map(function(d) {
return {
text: d,
size: 10 + Math.random() * 90
};
}))
.padding(5)
.rotate(function() {
return ~~(Math.random() * 2) * 90;
})
.font("Impact")
.fontSize(function(d) {
return d.size;
})
.on("end", draw)
.start();
function draw(words) {
d3.select("body").append("svg")
.attr("width", 700)
.attr("height", 700)
.append("g")
.attr("transform", "translate(150,150)")
.selectAll("text")
.data(words)
.enter().append("text")
.style("font-size", function(d) {
return d.size + "px";
})
.style("font-family", "Impact")
.style("fill", function(d, i) {
return fill(i);
})
.attr("text-anchor", "middle")
.attr("transform", function(d) {
return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
})
.text(function(d) {
return d.text;
});
}
</script>
anyone know a solution for this?
Just change the following line in draw():
.attr("transform", "translate(150,150)")
to:
.attr("transform", "translate(350,350)")
Since your SVG's size is 700x700, you want the g transform to be in the middle of the SVG as the text elements are anchored in the middle.
I am using D3 Cloud to build a word cloud (https://github.com/jasondavies/d3-cloud) and I would like to add an hover effect similar to http://www.jasondavies.com/wordcloud.
Here is the sample code. Complete code is at http://plnkr.co/edit/gNtHZ0lMRTP98mptm3W8?p=preview
var sizeScale = d3.scale.linear()
.domain([0, d3.max(frequency_list, function(d) { return d.freq} )]).range([10, 95]);
layout = d3.layout.cloud().size([w, h])
.words(frequency_list)
.padding(5)
.rotate(function() { return ~~(Math.random() * 2) * 90; })
.font("Impact")
.fontSize(function(d) { return sizeScale(d.freq); })
.on("end",draw)
.start();
}
function draw(words) {
var fill = d3.scale.category20();
d3.select(container).remove();
d3.select("body").append(container)
.attr("width", w)
.attr("height", h)
.append("g")
.attr("transform", "translate(" + [w/2, h/2] + ")")
.selectAll("text")
.data(words)
.enter().append("text")
.transition()
.duration(function(d) { return d.time} )
.attr('opacity', 1)
.style("font-size", function(d) { return d.size + "px"; })
.style("font-family", "Impact")
.style("fill", function(d, i) { return fill(i); })
.attr("text-anchor", "middle")
.attr("transform", function(d) {
return "rotate(" + d.rotate + ")";
})
.attr("transform", function(d) {
return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
})
.text(function(d) { return d.text; });
}
On the page you link to, he's using a :hover pseudo-class.
<style>
text:hover { opacity: .7 !important; }
</style>
Updated code here.