I am using the following example as base and want to make it dynamic word cloud https://github.com/jasondavies/d3-cloud
define(['scripts/d3.v3', 'scripts/elasticsearch'], function (d3, elasticsearch) {
"use strict";
var client = new elasticsearch.Client();
client.search({
index: 'nfl',
size: 5,
body: {
// Begin query.
query: {
// Boolean query for matching and excluding items.
bool: {
must: { match: { "description": "TOUCHDOWN" }},
must_not: { match: { "qtr": 5 }}
}
},
// Aggregate on the results
aggs: {
touchdowns: {
terms: {
field: "qtr",
order: { "_term" : "asc" }
}
}
}
// End query.
}
}).then(function (resp) {
console.log(resp);
// D3 code goes here.
var touchdowns = resp.aggregations.touchdowns.buckets;
// d3 donut chart
var width = 600,
height = 300,
radius = Math.min(width, height) / 2;
var color = ['#ff7f0e', '#d62728', '#2ca02c', '#1f77b4'];
var arc = d3.svg.arc()
.outerRadius(radius - 60)
.innerRadius(120);
var pie = d3.layout.pie()
.sort(null)
.value(function (d) { return d.doc_count; });
var svg = d3.select("#donut-chart").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width/1.4 + "," + height/2 + ")");
var g = svg.selectAll(".arc")
.data(pie(touchdowns))
.enter()
.append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function (d, i) {
return color[i];
});
g.append("text")
.attr("transform", function (d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.style("text-anchor", "middle")
.style("fill", "white")
.text(function (d) { return d.data.key; });
});
this is example code from elasticsearch website how to use with d3
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="../lib/d3/d3.js"></script>
<script src="../d3.layout.cloud.js"></script>
<script>
var fill = d3.scale.category20();
d3.layout.cloud().size([300, 300])
.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", 300)
.attr("height", 300)
.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>
This is code from d3 jason davies about wordcloud
How to make d3 wordcloud to listen data from elasticsearch ?
You'll need to get the elasticsearch response data into a format that's easy to pass into the wordcloud sample code, something like this:
var data = [{
"text": "How",
"size": 20
}, {
"text": "to",
"size": 30
}, ... ]
See: http://jsfiddle.net/henbox/RUTpJ/659/
The response you get back from your Elasticsearch aggregation will look something like this:
You'll also see this in a console when the ES response is logged:
}).then(function (resp) {
console.log(resp);
...
So to manipulate that data, add:
var data = resp.aggregations.myaggregation.buckets.map(function(d) {
return {
text: d.key,
size: d.doc_count
};
});
Note that myaggregation is a name you will define. In your NFL example code above it's actually called touchdowns
Pushing this data straight into the wordcloud will cause problems, however. In the wordcloud example, the font-size is determined directly from the size, but there's a good chance your doc_counts are much too high, and will need to be scaled down.
For that, try D3 linear scale. In this case, it will scale the range of input values down to a value between 15 and 100, which can be used for font size:
var fontsize = d3.scale.linear()
.domain(d3.extent(data, function (d) {return d.size}))
.range([15, 100]);
Then, instead of
.style("font-size", function (d) {
return d.size + "px";
})
use:
.style("font-size", function (d) {
return fontsize(d.size) + "px";
})
Related
I am trying to filter/update a bar chart with legend toggling. I am unsure how to set active states on the bars during initialization - then trying to deactivate - exclude the required datasets on toggle, but restore them when the active states come back.
http://jsfiddle.net/5ruhac83/5/
//legend toggling
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) {
return colores_google(i);
})
.on("click", function(name) {
var active = false;
newState = active ? "active" : "inactive";
// Hide or show the elements
d3.select(this).attr("class", newState);
//set active state
console.log("name", name)
toggleBar(name)
});
//animating the bars - with a pruned data set
function toggleBar(name) {
var hiddenClassName = 'hidden',
bar = chartHolder.selectAll('.bars'),
currentBars = bar.selectAll('[value="' + name + '"]')
currentBars.classed(hiddenClassName, !currentBars.classed(hiddenClassName))
var barData = data.map(item => {
item.valores = item.valores.map(valor => {
return Object.assign({}, valor, {
value: bar.selectAll('[value="' + valor.name + '"]').classed(hiddenClassName) ?
0 : item[valor.name]
})
})
return item;
})
var barData = [{
label: "a",
"Current Period": 20
}, {
label: "b",
"Current Period": 15
}, {
label: "c",
"Current Period": 25
}, {
label: "d",
"Current Period": 5
}];
var options = getOptions(barData);
barData = refactorData(barData, options);
console.log("barData", barData)
bar
.data(barData)
var rect = bar.selectAll("rect")
.data(function(d) {
return d.valores;
})
rect
.transition()
.duration(1000)
.delay(100)
.attr("width", x0.rangeBand() / 2)
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return height - y(d.value);
});
rect.exit().remove();
/*
var bar = bar.selectAll("rect")
bar.transition()
//.attr("id", function(d){ return 'tag'+d.state.replace(/\s|\(|\)|\'|\,+/g, '');})
.attr("x", function(d) { return x1(d.name); })
.attr("width", x0.rangeBand())
.attr("y", function(d) {
return 0;
//return y(d.value);
})
.attr("height", function(d) {
return 0;
//return height - y(d.value);
});
//bar.exit().remove();
*/
}
Here's a chart which resets the domain with new options based on the toggled legend:
JS Fiddle DEMO
function toggleBar(name, state) {
data.forEach(function(d) {
_.findWhere(d.valores, {name: name}).hidden = state;
});
var filteredOptions;
if(state) {
filteredOptions = options.filter(function(d) { return d !== name; });
} else {
filteredOptions = options;
}
x1.domain(filteredOptions).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) {
return d3.max(d.valores.filter(function(k) { return !k.hidden;}), function(d) {
return d.value;
});
})]);
Changes:
You don't need to reset the data on every toggle. I just added a hidden attribute to the "valores" and the while resetting the domain in the toggleBar function, filtered the data based on non-hidden options and set the domain accordingly.
I'd recommend to get used to d3's "enter, update and exit" methods. I hope the code helps you understand that as well.
drawBars() is a function that does that.
Changed the way the tooltip is rendered as well. Instead of using querySelector for hovered elements (that's definitely one way), you can just use the parent node's data using the datum() function.
Legends: I've added a stroke for every legend and to indicate whether the corresponding option is hidden or not, the fill-opacity is toggled on every click.
Used a separate color scale with an ordinal domain of options and range to be same as previous colors so that the colors are based on the names and not indices (as before)
Added simple transitions.
Used underscore.js in toggleBars() function. You could switch back to pure JS as well.
And to answer your question on active states, please check for toggling of the "clicked" classnames.
Please go through the code and let me know if you any part of it is unclear. I'll add some comments too.
:)
here is a solution for grouped bar chart legend toggling with animation.
//jsfiddle - http://jsfiddle.net/0ht35rpb/259/
var $this = this.$('.barChart');
var w = $this.data("width");
var h = $this.data("height");
//var configurations = $this.data("configurations");
var data = [{
"State": "a",
"AA": 100,
"BB": 200
}, {
"State": "b",
"AA": 454,
"BB": 344
},{
"State": "c",
"AA": 140,
"BB": 500
}, {
"State": "d",
"AA": 154,
"BB": 654
}];
var yLabel = "Count";
var svg = d3.select($this[0]).append("svg"),
margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = w - margin.left - margin.right,
height = h - margin.top - margin.bottom,
g = svg
.attr("width", w)
.attr("height", h)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// The scale spacing the groups:
var x0 = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.1);
// The scale for spacing each group's bar:
var x1 = d3.scaleBand()
.padding(0.05);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var z = d3.scaleOrdinal()
.range(["#f7b363", "#448875", "#c12f39", "#2b2d39", "#f8dd2f", "#8bf41b"]);
var keys = d3.keys(data[0]).slice(1);
x0.domain(data.map(function(d) {
return d.State;
}));
x1.domain(keys).rangeRound([0, x0.bandwidth()]);
y.domain([0, d3.max(data, function(d) {
return d3.max(keys, function(key) {
return d[key];
});
})]).nice();
g.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("class", "bar")
.attr("transform", function(d) {
return "translate(" + x0(d.State) + ",0)";
})
.selectAll("rect")
.data(function(d) {
return keys.map(function(key) {
return {
key: key,
value: d[key]
};
});
})
.enter().append("rect")
.attr("x", function(d) {
return x1(d.key);
})
.attr("y", function(d) {
return y(d.value);
})
.attr("width", x1.bandwidth())
.attr("height", function(d) {
return height - y(d.value);
})
.attr("fill", function(d, i) {
return z(d.key);
});
g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x0));
g.append("g")
.attr("class", "yaxis")
.call(d3.axisLeft(y).ticks(null, "s"))
.append("text")
.attr("x", 2)
.attr("y", y(y.ticks().pop()) + 0.5)
.attr("dy", "0.32em")
.attr("fill", "#000")
.attr("font-weight", "bold")
.attr("text-anchor", "start")
.text(yLabel);
var legend = g.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("text-anchor", "end")
.selectAll("g")
.data(keys.slice().reverse())
.enter().append("g")
.attr("transform", function(d, i) {
return "translate(0," + i * 20 + ")";
});
legend.append("rect")
.attr("x", width - 17)
.attr("width", 15)
.attr("height", 15)
.attr("fill", z)
.attr("stroke", z)
.attr("stroke-width", 2)
.on("click", function(d) {
update(d)
});
legend.append("text")
.attr("x", width - 24)
.attr("y", 9.5)
.attr("dy", "0.32em")
.text(function(d) {
return d;
});
var filtered = [];
////
//// Update and transition on click:
////
function update(d) {
//
// Update the array to filter the chart by:
//
// add the clicked key if not included:
if (filtered.indexOf(d) == -1) {
filtered.push(d);
// if all bars are un-checked, reset:
if (filtered.length == keys.length) filtered = [];
}
// otherwise remove it:
else {
filtered.splice(filtered.indexOf(d), 1);
}
//
// Update the scales for each group(/states)'s items:
//
var newKeys = [];
keys.forEach(function(d) {
if (filtered.indexOf(d) == -1) {
newKeys.push(d);
}
})
x1.domain(newKeys).rangeRound([0, x0.bandwidth()]);
y.domain([0, d3.max(data, function(d) {
return d3.max(keys, function(key) {
if (filtered.indexOf(key) == -1) return d[key];
});
})]).nice();
//g.select(".yaxis")
//.call(d3.axisLeft(y).ticks(null, "s"));
var t0 = svg.transition().duration(250);
var t1 = t0.transition();
t1.selectAll(".yaxis").call(d3.axisLeft(y).ticks(null, "s"));
//
// Filter out the bands that need to be hidden:
//
var bars = svg.selectAll(".bar").selectAll("rect")
.data(function(d) {
return keys.map(function(key) {
return {
key: key,
value: d[key]
};
});
})
bars.filter(function(d) {
return filtered.indexOf(d.key) > -1;
})
.transition()
.attr("x", function(d) {
return (+d3.select(this).attr("x")) + (+d3.select(this).attr("width")) / 2;
})
.attr("height", 0)
.attr("width", 0)
.attr("y", function(d) {
return height;
})
.duration(500);
//
// Adjust the remaining bars:
//
bars.filter(function(d) {
return filtered.indexOf(d.key) == -1;
})
.transition()
.attr("x", function(d) {
return x1(d.key);
})
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return height - y(d.value);
})
.attr("width", x1.bandwidth())
.attr("fill", function(d, i) {
return z(d.key);
})
.duration(500);
// update legend:
legend.selectAll("rect")
.transition()
.attr("fill", function(d, i) {
if (filtered.length) {
if (filtered.indexOf(d) == -1) {
return z(d);
} else {
return "white";
}
} else {
return z(d);
}
})
.duration(100);
}
I want to highlight a new array of words like "salmon" & "prey" that I want to provide to my word cloud, so how should I do it because I tried to use mark.js or Javascript with CSS but couldn't succeed, but now I think it is only possible here when I am drawing the word cloud. So can someone help me to provide me with a function or maybe some changes in my code to highlight the array (arrayToBeHighlight) of words:
var width = 750, height = 500;
var words = [["whales", 79], ["salmon", 56], ["Chinook", 30], ["book", 70],
["prey", 51]].map(function(d) {
return {text: d[0], size: d[1]};
});
var arrayToBeHighlight = [ ["salmon", 56], ["prey", 51] ];
**OR**
var arrayToBeHighlight = ["salmon", "prey"];
maxSize = d3.max(words, function(d) { return d.size; });
minSize = d3.min(words, function(d) { return d.size; });
var fontScale = d3.scale.linear().domain([minSize, maxSize]).range([10,70]);
var fill = d3.scale.category20();
d3.layout.cloud().size([width, height]).words(words).font("Impact")
.fontSize(function(d) { return fontScale(d.size) })
.on("end", drawCloud).start();
function drawCloud(words) {
d3.select("#wordCloud").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + (width / 2) + "," + (height / 2) +")")
.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; });
}
HTML Code
<div style="margin-left:20px" id="wordCloud"></div>
Things like mark.js work by creating a span around the word and setting a background color to mimic a highlighter. This doesn't work in SVG because text elements don't have a background color. Instead, you can fake it by inserting a rect before the text element:
texts.filter(function(d){
return arrayToBeHighlight.indexOf(d.text) != -1;
})
.each(function(d){
var bbox = this.getBBox(),
trans = d3.select(this).attr('transform');
g.insert("rect", "text")
.attr("transform", trans)
.attr("x", -bbox.width/2)
.attr("y", bbox.y)
.attr("width", bbox.width)
.attr("height", bbox.height)
.style("fill", "yellow");
});
Running code;
<!DOCTYPE html>
<html>
<head>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="https://rawgit.com/jasondavies/d3-cloud/master/build/d3.layout.cloud.js"></script>
</head>
<body>
<div style="margin-left:20px" id="wordCloud"></div>
<script>
var width = 750,
height = 500;
var words = [
["whales", 79],
["salmon", 56],
["Chinook", 30],
["book", 70],
["prey", 51]
].map(function(d) {
return {
text: d[0],
size: d[1]
};
});
var arrayToBeHighlight = ["salmon", "prey"];
maxSize = d3.max(words, function(d) {
return d.size;
});
minSize = d3.min(words, function(d) {
return d.size;
});
var fontScale = d3.scale.linear().domain([minSize, maxSize]).range([10, 70]);
var fill = d3.scale.category20();
d3.layout.cloud().size([width, height]).words(words).font("Impact")
.fontSize(function(d) {
return fontScale(d.size)
})
.on("end", drawCloud).start();
function drawCloud(words) {
var g = d3.select("#wordCloud").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")");
var texts = g.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;
});
texts.filter(function(d){
return arrayToBeHighlight.indexOf(d.text) != -1;
})
.each(function(d){
var bbox = this.getBBox(),
trans = d3.select(this).attr('transform');
g.insert("rect", "text")
.attr("transform", trans)
.attr("x", -bbox.width/2)
.attr("y", bbox.y)
.attr("width", bbox.width)
.attr("height", bbox.height)
.style("fill", "yellow");
});
}
</script>
</body>
</html>
The answer depends on your definition of highlight and how you want to highlight them.
One possibility is comparing the arrayToBeHighlight array with the datum when painting the words. For instance, turning them red:
.style("fill", function(d, i) {
return arrayToBeHighlight.indexOf(d.text) > -1 ? "red" : fill(i);
})
Here is the bl.ocks: http://bl.ocks.org/anonymous/d38d1fbb5919c04783934d430fb895c2/b42582053b03b178bb155c2bbaec5242374d051b
My last question was answered so quickly and smoothly I thought I'd return with another issue I'm failing to figure out on my own.
I used one of the examples to create this graph:
data = [{ "label": "1", "value": 20 },
{ "label": "2", "value": 50 },
{ "label": "3", "value": 30 },
{ "label": "4", "value": 45 }];
var width = 400,
height = 450;
var outerRadius = 200,
innerRadius = outerRadius / 3,
color = d3.scale.category20c();
var pie = d3.layout.pie()
.value(function (d) { return d.value; });
var pieData = pie(data);
var arc = d3.svg.arc()
.innerRadius(innerRadius);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + outerRadius + "," + (outerRadius + 50) + ")");
svg.append("text")
.attr("x", 0)
.attr("y", -(outerRadius + 10))
.style("text-anchor", "middle")
.text("Title[enter image description here][1]");
svg.selectAll("path")
.data(pieData)
.enter().append("path")
.each(function (d) { d.outerRadius = outerRadius - 20; })
.attr("d", arc)
.attr("fill", function (d, i) { return color(i); })
.on("mouseover", arcTween(outerRadius, 0))
.on("mouseout", arcTween(outerRadius - 20, 150));
svg.selectAll("path")
.append("text")
.attr("transform", function (d) {
d.innerRadius = 0;
d.outerRadius = outerRadius;
return "translate(" + arc.centroid(d) + ")";
})
.attr("fill", "white")
.attr("text-anchor", "middle")
.text(function (d, i) { return data[i].label; });
function arcTween(outerRadius, delay) {
return function () {
d3.select(this).transition().delay(delay).attrTween("d", function (d) {
var i = d3.interpolate(d.outerRadius, outerRadius);
return function (t) { d.outerRadius = i(t); return arc(d); };
});
};
}
The idea being that when you hover over a section on the pie chart (donut chart?) it expands. However, this made my labels dissapear and I can't manage to make them come back. I either get an error, or they just don't show up on the screen (even though I see the tag in the inspector). Any obvious thing I'm missing?
Thanks!
You cannot append a <text> element to a <path> element. It simply doesn't work in an SVG. Even not working, the <text> element will be appended.
That being said, a solution is creating a new "enter" selection for the texts:
svg.selectAll(null)
.data(pieData)
.enter()
.append("text")
.attr("transform", function(d) {
d.innerRadius = 0;
d.outerRadius = outerRadius;
return "translate(" + arc.centroid(d) + ")";
})
.attr("fill", "white")
.attr("text-anchor", "middle")
.text(function(d, i) {
return data[i].label;
});
Here is your updated code:
data = [{
"label": "1",
"value": 20
}, {
"label": "2",
"value": 50
}, {
"label": "3",
"value": 30
}, {
"label": "4",
"value": 45
}];
var width = 400,
height = 450;
var outerRadius = 200,
innerRadius = outerRadius / 3,
color = d3.scale.category20c();
var pie = d3.layout.pie()
.value(function(d) {
return d.value;
});
var pieData = pie(data);
var arc = d3.svg.arc()
.innerRadius(innerRadius);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + outerRadius + "," + (outerRadius + 50) + ")");
svg.append("text")
.attr("x", 0)
.attr("y", -(outerRadius + 10))
.style("text-anchor", "middle")
.text("Title[enter image description here][1]");
svg.selectAll("path")
.data(pieData)
.enter().append("path")
.each(function(d) {
d.outerRadius = outerRadius - 20;
})
.attr("d", arc)
.attr("fill", function(d, i) {
return color(i);
})
.on("mouseover", arcTween(outerRadius, 0))
.on("mouseout", arcTween(outerRadius - 20, 150));
svg.selectAll(null)
.data(pieData)
.enter()
.append("text")
.attr("transform", function(d) {
d.innerRadius = 0;
d.outerRadius = outerRadius;
return "translate(" + arc.centroid(d) + ")";
})
.attr("fill", "white")
.attr("text-anchor", "middle")
.text(function(d, i) {
return data[i].label;
});
function arcTween(outerRadius, delay) {
return function() {
d3.select(this).transition().delay(delay).attrTween("d", function(d) {
var i = d3.interpolate(d.outerRadius, outerRadius);
return function(t) {
d.outerRadius = i(t);
return arc(d);
};
});
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
I have tried this code .I am new to d3.
but able to directly reach to child node directly
def city ,xyz city and abc city are displayed on treemap
data.json
{
"name":"country",
"children":
[
{
"name": "Verizona State",
"value": 100,
"children": [
{
"name": "xyz city",
"value": 30
},
{
"name": "abc city",
"value": 40
}
]
},
{
"name": "New Jersey",
"value": 50,
"children": [
{
"name": "def city",
"value": 30
}
]
}
]
}
index.html
<html>
<script src="http://d3js.org/d3.v3.min.js"></script>
<body>
<div id="heatmap">
<script>
var color = d3.scale.category10();
var canvas = d3.select("#heatmap").append("svg")
.attr("width",500)
.attr("height",500);
d3.json("data.json" ,function(data){
var treemap=d3.layout.treemap()
.size([500,500])
.nodes(data);
console.log(treemap);
var cells = canvas.selectAll(".cell")
.style("position", "relative")
.data(treemap)
.enter()
.append("g")
.attr("class","cell")
.attr("stroke","#fff");
cells.append("rect")
.attr( "x" , function(d) { console.log(d); return d.x; })
.attr("y", function(d) { return d.y; })
.attr("width", function(d) { return d.dx; })
.attr("height", function(d) { return d.dy; })
.attr("fill", function(d){return d.children ? null:color(d.parent.name); })
cells.append("text")
.attr("x",function(d) {return d.x + d.dx/2})
.attr("y",function(d) {return d.y + d.dy/2})
.text(function(d){ return d.children? null :d.name;})
})
</script>
</div>
</body>
</html>
I want a way to display first
Verizona State and New Jersey on treemap
and on clicking verizona state to get xyz city and abc city on treemap
Please suggest changes.
I solved this problem using this example of mike
http://bost.ocks.org/mike/treemap/
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script></script>
<script>
var margin = {top: 20, right: 0, bottom: 0, left: 0},
width = 960,
height = 500 - margin.top - margin.bottom,
formatNumber = d3.format(",d"),
transitioning;
var x = d3.scale.linear()
.domain([0, width])
.range([0, width]);
var y = d3.scale.linear()
.domain([0, height])
.range([0, height]);
var treemap = d3.layout.treemap()
.children(function(d, depth) { return depth ? null : d._children; })
.sort(function(a, b) { return a.value - b.value; })
.ratio(height / width * 0.5 * (1 + Math.sqrt(5)))
.round(false);
var svg = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.bottom + margin.top)
.style("margin-left", -margin.left + "px")
.style("margin.right", -margin.right + "px")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.style("shape-rendering", "crispEdges");
var grandparent = svg.append("g")
.attr("class", "grandparent");
grandparent.append("rect")
.attr("y", -margin.top)
.attr("width", width)
.attr("height", margin.top);
grandparent.append("text")
.attr("x", 6)
.attr("y", 6 - margin.top)
.attr("dy", ".75em");
d3.json("flare.json", function(root) {
initialize(root);
accumulate(root);
layout(root);
display(root);
function initialize(root) {
root.x = root.y = 0;
root.dx = width;
root.dy = height;
root.depth = 0;
}
// Aggregate the values for internal nodes. This is normally done by the
// treemap layout, but not here because of our custom implementation.
// We also take a snapshot of the original children (_children) to avoid
// the children being overwritten when when layout is computed.
function accumulate(d) {
return (d._children = d.children)
? d.value = d.children.reduce(function(p, v) { return p + accumulate(v); }, 0)
: d.value;
}
// Compute the treemap layout recursively such that each group of siblings
// uses the same size (1×1) rather than the dimensions of the parent cell.
// This optimizes the layout for the current zoom state. Note that a wrapper
// object is created for the parent node for each group of siblings so that
// the parent’s dimensions are not discarded as we recurse. Since each group
// of sibling was laid out in 1×1, we must rescale to fit using absolute
// coordinates. This lets us use a viewport to zoom.
function layout(d) {
if (d._children) {
treemap.nodes({_children: d._children});
d._children.forEach(function(c) {
c.x = d.x + c.x * d.dx;
c.y = d.y + c.y * d.dy;
c.dx *= d.dx;
c.dy *= d.dy;
c.parent = d;
layout(c);
});
}
}
function display(d) {
grandparent
.datum(d.parent)
.on("click", transition)
.select("text")
.text(name(d));
var g1 = svg.insert("g", ".grandparent")
.datum(d)
.attr("class", "depth");
var g = g1.selectAll("g")
.data(d._children)
.enter().append("g");
g.filter(function(d) { return d._children; })
.classed("children", true)
.on("click", transition);
g.selectAll(".child")
.data(function(d) { return d._children || [d]; })
.enter().append("rect")
.attr("class", "child")
.call(rect);
g.append("rect")
.attr("class", "parent")
.call(rect)
.append("title")
.text(function(d) { return formatNumber(d.value); });
g.append("text")
.attr("dy", ".75em")
.text(function(d) { return d.name; })
.call(text);
function transition(d) {
if (transitioning || !d) return;
transitioning = true;
var g2 = display(d),
t1 = g1.transition().duration(750),
t2 = g2.transition().duration(750);
// Update the domain only after entering new elements.
x.domain([d.x, d.x + d.dx]);
y.domain([d.y, d.y + d.dy]);
// Enable anti-aliasing during the transition.
svg.style("shape-rendering", null);
// Draw child nodes on top of parent nodes.
svg.selectAll(".depth").sort(function(a, b) { return a.depth - b.depth; });
// Fade-in entering text.
g2.selectAll("text").style("fill-opacity", 0);
// Transition to the new view.
t1.selectAll("text").call(text).style("fill-opacity", 0);
t2.selectAll("text").call(text).style("fill-opacity", 1);
t1.selectAll("rect").call(rect);
t2.selectAll("rect").call(rect);
// Remove the old node when the transition is finished.
t1.remove().each("end", function() {
svg.style("shape-rendering", "crispEdges");
transitioning = false;
});
}
return g;
}
function text(text) {
text.attr("x", function(d) { return x(d.x) + 6; })
.attr("y", function(d) { return y(d.y) + 6; });
}
function rect(rect) {
rect.attr("x", function(d) { return x(d.x); })
.attr("y", function(d) { return y(d.y); })
.attr("width", function(d) { return x(d.x + d.dx) - x(d.x); })
.attr("height", function(d) { return y(d.y + d.dy) - y(d.y); });
}
function name(d) {
return d.parent
? name(d.parent) + "." + d.name
: d.name;
}
});
</script>
<script>
</script>
I am using D3 Cloud to build a word cloud. Here is the sample code:
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="../lib/d3/d3.js"></script>
<script src="../d3.layout.cloud.js"></script>
<script>
var fill = d3.scale.category20();
d3.layout.cloud().size([300, 300])
.words(["This", "is", "some", "random", "text"].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", 300)
.attr("height", 300)
.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>
I want to create a hyperlink on each of the words("This", "is", "some", "random", "text"). So in the word cloud, when I click on each of the words, it goes to the link.
1) How do I function on each word?
2) Bonus if you could tell me how to change the size of the cloud to 800*300 instead of 300*300. As I have tried to change it's size in line "d3.layout.cloud().size([300, 300])" but it doesn't help. The text goes out of the box.
Hope you understood my question.
Thanks.
To make the words clickable all you need to do is set an on.("click", function(...){...}) listener which opens a new tab. You can also add styling to the text to make it look like a link. Here is some code:
var words = [{"text":"This", "url":"http://google.com/"},
{"text":"is", "url":"http://bing.com/"},
{"text":"some", "url":"http://somewhere.com/"},
{"text":"random", "url":"http://random.org/"},
{"text":"text", "url":"http://text.com/"}]
for (var i = 0; i < words.length; i++) {
words[i].size = 10 + Math.random() * 90;
}
...
d3.layout.cloud()
...
.words(words)
...
.start();
function draw(words) {
...
d3.select("body")
.append("svg")
...
.enter()
.append("text")
...
.text(function(d) { return d.text; })
.on("click", function (d, i){
window.open(d.url, "_blank");
});
}
I changed the format to make the code more manageable.
To change the width and height of the image you need to change three values:
d3.layout.cloud()
.size([width, height])
d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
...
.attr("transform", "translate("+ width/2 +","+ height/2 +")")
These attributes should be controlled by two variables to to keep the code simple.
Here is a fiddle.