How to place text inside each bar of the chart using d3 - javascript

I am trying to insert some text inside each bar of the given chart with it's values and I just can't figure it out. I'm still new to d3 and struggle with most of the things.
I also need to have different text inside each bar, dependent on the data.
I've looked up around and found that I need to use the following structure:
<g class="barGroup">
<rect />
<text />
</g>
This is how the chart looks:
This is how I want to achieve:
This is the function which creates the whole chart, the part where I attempt to insert the text is at the end:
function createBarChart(divChartId, receivedData, chartDimensions) {
// Set dimensions
var margin = chartDimensions["margin"];
var width = chartDimensions["width"];
var height = chartDimensions["height"];
// Create Svg and group
var svg = d3.select(divChartId)
.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 + ")");
// Create and append X axis
var xAxis = d3.scaleBand()
.range([0, width])
.domain(receivedData.map(function (d) { return d.answerText; }))
.padding(0.2);
svg.append("g")
.attr("class", "x-axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xAxis))
.selectAll("text")
.attr("class", "font-weight-bold")
.attr("transform", "translate(-10,0)rotate(-45)")
.style("text-anchor", "end")
.style("font-size", "1rem");
// Create and append Y axis
var yAxis = d3.scaleLinear()
.domain([0, Math.max.apply(Math, receivedData.map(function (d) { return d.answerCount; }))])
.range([height, 0]);
svg.append("g")
.attr("class", "y-axis")
.call(d3.axisLeft(yAxis)
.ticks(5)
.tickFormat(d3.format("d")));
// create a tooltip
var tooltip = d3.select("body")
.append("div")
.attr("class", "font-weight-bold text-dark px-2 py-2")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden")
.style("background", "lavender")
.style("border", "1px solid gray")
.style("border-radius", "12px")
.style("text-align", "center")
.style("visibility", "hidden")
.style("visibility", "hidden")
.style("visibility", "hidden")
// Generate random color
var colorObject = generateRandomColorObject();
// Create and append bars, set values to 0 for animation start
svg.selectAll("rect").data(receivedData)
.enter()
.append("g")
.attr("class", "barGroup")
.append("rect")
.attr("x", function (d) { return xAxis(d.answerText); })
.attr("y", function (d) { return yAxis(0); })
.attr("width", xAxis.bandwidth())
.attr("height", function (d) { return height - yAxis(0); })
.attr("fill", colorObject.color)
.style("stroke", colorObject.border)
.style("stroke-width", 1)
.on("mouseover", function (d) { return tooltip.style("visibility", "visible").html(d.answerText + ": " + d.answerCount); })
.on("mousemove", function () { return tooltip.style("top", (event.pageY - 45) + "px").style("left", (event.pageX) + 5 + "px"); })
.on("mouseout", function (d) { return tooltip.style("visibility", "hidden").html(""); });
// Set correct values for animation end
svg.selectAll("rect")
.transition().duration(1500)
.attr("y", function (d) { return yAxis(d.answerCount); })
.attr("height", function (d) { return height - yAxis(d.answerCount); })
.delay(function (d, i) { return (i * 100) });
// Append text to each bar
svg.selectAll("barGroup")
.append("text")
.attr("font-size", "2em")
.attr("color", "black")
.text("Test");
}

In this line you want your selector to look for a class: svg.selectAll("barGroup") should be svg.selectAll(".barGroup"). Then, once you see your text show up, you'll need to position them.
Consider positioning your g.barGroups's first, using .attr('transform', function(d){ return 'translate(...)' }, before you .append('rect') and .append('text'). That way the entire group will be in approximately the right position, and you can make small adjustments to the rect and text.

Related

d3js multi-line scatterplot zoom

I am working on a multi-line scatterplot with zoom using d3 v6. I am new to d3 and based on different examples, I could get the zoom function working for the images/points. The problem is that the lines aren't zooming. I looked at many similar questions, but none of those solutions are working for me.
The code I am using:
var margin = {
top: 50,
right: 30,
bottom: 30,
left: 210,
};
var svg = d3.select("svg"),
width = 1410 - margin.left - margin.right,
height = 620 - margin.top - margin.bottom;
svg
.append("defs")
.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
d3.csv("CSV_files/NSW_pathway.csv").then(function (data1) {
var groupData = d3.group(data1, (d) => d.pathway_name);
var xScale = d3.scaleLinear().domain([0, 1]).range([0, width]);
var yScale = d3.scaleLinear().domain([0, 1]).range([height, 0]);
var xAxis = d3.axisBottom(xScale).ticks(0).tickSize(-height);
var yAxis = d3.axisLeft(yScale).ticks(0).tickSize(-width);
var gX = svg
.append("g")
.attr(
"transform",
"translate(" + margin.left + "," + (margin.top + height) + ")"
)
.call(xAxis);
var gY = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(yAxis);
var focus = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("class", "line")
.attr("clip-path", "url(#clip)");
const color = d3
.scaleOrdinal()
.range(["#e41a1c", "#377eb8", "#4daf4a", "#984ea3"]);
var points_g = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("clip-path", "url(#clip)")
.classed("points_g", true);
var label = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("class", "label")
.attr("clip-path", "url(#clip)");
var div = d3
.select("body")
.append("div")
.attr("class", "tooltip")
.style("opacity", 0);
const mouseover = function (event, d) {
div.style("opacity", 1);
};
const mousemove = function (event, d) {
div
.html(function (d1) {
if (d.type != "learner")
return `The resource name is ${d.resource_name}`;
else return `This is ${d.name}`;
})
.style("position", "absolute")
.style("left", event.pageX + 15 + "px")
.style("top", event.pageY + 15 + "px");
};
const mouseleave = function (event, d) {
div.transition().duration(200).style("opacity", 0);
};
var points = points_g.selectAll("point").data(data1);
points = points
.enter()
.append("image")
.attr("xlink:href", function (d) {
if (d.type == "video") return "Images/3.jpg";
else if (d.type == "pdf") return "Images/4.png";
else if (d.type == "none") return "Images/5.png";
})
.attr("x", function (d) {
return xScale(+d.x) - 10;
})
.attr("y", function (d) {
return yScale(+d.y) - 10;
})
.attr("width", 20)
.attr("height", 20)
.on("mouseover", mouseover)
.on("mousemove", mousemove)
.on("mouseleave", mouseleave);
label
.selectAll(".text")
.data(data1)
.enter()
.append("text")
.text(function (d) {
return d.topic;
})
.attr("x", function (d) {
return xScale(+d.x) + 10;
})
.attr("y", function (d) {
return yScale(+d.y) + 10;
});
focus
.selectAll("line")
.data(groupData)
.enter()
.append("path")
.attr("fill", "none")
.attr("stroke", function (d) {
return color(d[0]);
})
.attr("stroke-width", 1)
.attr("d", function (d) {
return d3
.line()
.curve(d3.curveMonotoneX)
.x(function (d) {
return xScale(+d.x);
})
.y(function (d) {
return yScale(+d.y);
})(d[1]);
});
var zoom = d3
.zoom()
.scaleExtent([0.5, 20])
.extent([
[0, 0],
[width, height],
])
.on("zoom", zoomed);
svg
.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.lower();
svg.call(zoom).call(zoom.transform, d3.zoomIdentity);
function zoomed({ transform }) {
var new_xScale = transform.rescaleX(xScale);
var new_yScale = transform.rescaleY(yScale);
gX.call(xAxis.scale(new_xScale));
gY.call(yAxis.scale(new_yScale));
points
.data(data1)
.attr("x", function (d) {
return new_xScale(d.x) - 10;
})
.attr("y", function (d) {
return new_yScale(d.y) - 10;
});
label
.selectAll("text")
.data(data1)
.attr("x", function (d) {
return new_xScale(d.x) + 15;
})
.attr("y", function (d) {
return new_yScale(d.y) + 15;
});
focus.selectAll("line").attr("d", function (d) {
return d3
.line()
.curve(d3.curveMonotoneX)
.x(function (d) {
return xScale(+d.x);
})
.y(function (d) {
return yScale(+d.y);
})(d[1]);
});
}
});
A sample of the csv file:
x,y,name,type,topic,resource_name,pathway_name
0,0,start,none,Sponsored Search Markets,Networks Crowd and Markets_NCMch15.pdf,pathwayOne
0,0,start,none,Sponsored Search Markets,Networks Crowd and Markets_NCMch15.pdf,pathwayTwo
0.086511627906977,0.16,horse,pdf,Graphs,Networks Crowd and Markets_NCMch2.pdf,pathwayOne
0.12,0.283768436578171,choice,pdf,Network Centrality,Notes_CGT BASED network CENTRALITY - L2.pdf,pathwayTwo
0.32,0.27217943628424,plex,video,Network Models,Network Analysis_LNch13.pdf,pathwayOne
0.775398773006135,0.33,social,pdf,Clustering,Network Analysis_LNch8.pdf,pathwayTwo
1,1,end,none,Allocation in Networks,Notes_Allocation in networks with DON-L3.pdf,pathwayOne
1,1,end,none,Allocation in Networks,Notes_Allocation in networks with DON-L3.pdf,pathwayTwo
Thank you for your help.
It's not zooming the whole page, it's zooming the whole svg, your large margins extend beyond the charting area. One solution is to add the g element not on your svg but only on your chart area.
But using your code, there are 2 things preventing your lines from zooming.
1: your selection is empty - line is a d3 abstraction that returns a path
function zoomed() {
...
// empty selection
console.log(focus.selectAll('line'))
// try instead
console.log(focus.selectAll('path'))
}
2: Simple mistake - you're using the old scale not the new one
function zoomed() {
...
focus.selectAll('path').attr('d', d => {
return d3.line()
// using old scale
.x(di => xScale(+di.x))
// change to
.x(di => new_xScale(+di.x))
})
}
I don't have a sample of your csv file so this isn't tested, but if you want to zoom the whole chart just add a parent g after your svg and transform that..
...
svg
.append("defs")
.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
// NEW - add g
.append('g')
// NEW - adjust scaleExtent to your needs
const zoom = d3.zoom()
.scaleExtent([1, 8])
.on('zoom', updateChart)
svg.call(zoom)
function updateChart(event) {
svg.attr('transform', event.transform)
}
Note that this also adds pan, but if you only want zoom you can use:
let scale = 1
...
function updateChart(event) {
if(event.transform.k === scale) { return }
svg.attr('transform', event.transform)
scale = event.transform.k
}

D3: Why tooltip near cursor never shows?

I'm trying to make a d3 tooltip that can show near my cursor when I hover over a dot. I tried lots of methods available online, but none of them works. The tooltip doesn't show, and there is no error messages in console. Could anyone help me with this? My code is as follows:
I suspect there might be some issue with svg, but not sure how to solve it.
var svg = d3.select("#linechart").append("svg").attr('id','cases')
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var xScale = d3.scaleTime()
.domain(d3.extent(getCovidDate(data,state), function(d){return new Date(d)}))
.range([0, width]);
var yScale = d3.scaleLinear()
.domain(d3.extent(getCases(data,state), function(d){return d}))
.range([height, 0]);
var line = d3.line()
.x(function(d) { return xScale(new Date(d[0])); }) // set the x values for the line generator
.y(function(d) { return yScale(d[1]); }) // set the y values for the line generator
.curve(d3.curveMonotoneX) // apply smoothing to the line
var dataset = d3.zip(getCovidDate(data,state),getCases(data,state))
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale)); // Create an axis component with d3.axisBottom
svg.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(yScale)); // Create an axis component with d3.axisLeft
var div = d3.select("#cases").append("div")
.attr("class", "tooltip")
.style("display", "none");
svg.append("path")
.datum(dataset) // Binds data to the line
.attr("class", "line") // Assign a class for styling
.attr("d", line) // Calls the line generator
.style('fill-opacity', 0)
.style('stroke','cadetblue')
.on('mouseover', function (d, i) {
d3.select(this).transition()
.duration('50')
.attr('opacity', '.55')})
.on('mouseout', function (d, i) {
d3.select(this).transition()
.duration('50')
.attr('opacity', '1')});
svg.selectAll(".dot")
.data(dataset)
.enter().append("circle")
.attr("class", "dot") // Assign a class for styling
.attr("cx", function(d) { return xScale(new Date(d[0])) })
.attr("cy", function(d) { return yScale(d[1]) })
.attr("r", 5)
.style("fill",'cadetblue')
.on('mouseover', function (d, i) {
d3.select(this).transition()
.duration('50')
.attr('opacity', '.55')
div.style("display", "inline");
})
.on('mousemove',function(d){
div.text(d[1])
.style("left", (d3.event.pageX + 10) + "px")
.style("top", (d3.event.pageY - 15) + "px")
})
.on('mouseout', function (d, i) {
d3.select(this).transition()
.duration('50')
.attr('opacity', '1')
div.style("display", "none");
});
svg.append("line")
.attr("x1", xScale(new Date(inputValue)))
.attr("y1", 0)
.attr("x2", xScale(new Date(inputValue)))
.attr("y2", height)
.style("stroke-width", 2)
.style("stroke", "red")
.style("fill", "none");
d3.select('#cases').remove()

Using d3.js to create a heatmap, having issues with color

Hi i have a heatmap here that im trying to give color to. Right now its all over red but I want to use the d3.interpolateRdYlBu, i want my values that are lower to be the blue and the higher be the red so i would like it to gel nicely. I know that its reading correctly since i get the red and no other errors in my console but Im not doing something right that it doesnt take my value into account and do accordingly. Any help would be appreciated!
<!DOCTYPE html>
<meta charset="utf-8">
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
<!-- Load color palettes -->
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script>
// set the dimensions and margins of the graph
var margin = {top: 80, right: 25, bottom: 30, left: 40},
width = 1000 - margin.left - margin.right,
height = 1000 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.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 + ")");
//Read the data
d3.csv("https://raw.githubusercontent.com/Nataliemcg18/Data/master/NASA_Surface_Temperature.csv", function(data) {
// Labels of row and columns -> unique identifier of the column called 'group' and 'variable'
var myGroups = d3.map(data, function(d){return d.group;}).keys()
var myVars = d3.map(data, function(d){return d.variable;}).keys()
// Build X scales and axis:
var x = d3.scaleBand()
.range([ 0, width ])
.domain(myGroups)
.padding(0.05);
svg.append("g")
.style("font-size", 15)
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).tickSize(0))
.select(".domain").remove()
// Build Y scales and axis:
var y = d3.scaleBand()
.range([ height, 0 ])
.domain(myVars)
.padding(0.05);
svg.append("g")
.style("font-size", 15)
.call(d3.axisLeft(y).tickSize(0))
.select(".domain").remove()
// Build color scale
var myColor = d3.scaleSequential()
.interpolator( d3.interpolateRdYlBu)
.domain([1,100])
// create a tooltip
var tooltip = d3.select("#my_dataviz")
.append("div")
.style("opacity", 0)
.attr("class", "tooltip")
.style("background-color", "white")
.style("border", "solid")
.style("border-width", "2px")
.style("border-radius", "5px")
.style("padding", "5px")
// Three function that change the tooltip when user hover / move / leave a cell
var mouseover = function(d) {
tooltip
.style("opacity", 1)
d3.select(this)
.style("stroke", "green")
.style("opacity", 1)
}
var mousemove = function(d) {
tooltip
.html("The exact value of this cell is: " + d.value, )
.style("left", (d3.mouse(this)[0]+70) + "px")
.style("top", (d3.mouse(this)[1]) + "px")
}
var mouseleave = function(d) {
tooltip
.style("opacity", 0)
d3.select(this)
.style("stroke", "none")
.style("opacity", 0.8)
}
// add the squares
svg.selectAll()
.data(data, function(d) {return d.group+':'+d.variable;})
.enter()
.append("rect")
.attr("x", function(d) { return x(d.group) })
.attr("y", function(d) { return y(d.variable) })
.attr("rx", 4)
.attr("ry", 4)
.attr("width", x.bandwidth() )
.attr("height", y.bandwidth() )
.style("fill", function(d) { return myColor(d.value)} )
.style("stroke-width", 4)
.style("stroke", "none")
.style("opacity", 0.8)
.on("mouseover", mouseover)
.on("mousemove", mousemove)
.on("mouseleave", mouseleave)
})
// Add title to graph
svg.append("text")
.attr("x", 0)
.attr("y", -50)
.attr("text-anchor", "left")
.style("font-size", "22px")
.text("A d3.js heatmap");
// Add subtitle to graph
svg.append("text")
.attr("x", 0)
.attr("y", -20)
.attr("text-anchor", "left")
.style("font-size", "14px")
.style("fill", "grey")
.style("max-width", 400)
.text("A short description of the take-away message of this chart.");
</script>
In your colour scale, you've set the domain as [0,100]. Your values however are between 0 and 1.5, and you want them reversed
so, this should fix it:
// Build color scale
var myColor = d3.scaleSequential()
.interpolator( d3.interpolateRdYlBu)
.domain([1.3,0])
To be even more thorough, you can use the d3.max and d3.min functions to work out the max in min for you:
var myColor = d3.scaleSequential()
.interpolator( d3.interpolateRdYlBu)
.domain([d3.max(data, d=>d.value),d3.min(data, d=>d.value)])
Heres a jsFiddle with this working: https://jsfiddle.net/x8zyud5t/

How to make a moving transition of points from a to b

I have a scatterplot and I have two different sets of datapoints I am visualizing from the dataset. I want to animate the path from "red" to "blue" dots and show them like the blue point is moving from the red and getting its position. Is that possible with d3, and if so how can I do this?
The scatterplot I have currently with the plotted points is here.
this is how I draw both sets of datapoints in the scatterplot:
// blue dots
svg.append('g')
.selectAll("dot")
.data(data)
.enter()
.append("circle")
.attr("cx", function (d) { return x(d.x); } )
.attr("cy", function (d) { return y(d.y); } )
.attr("r", 4.1)
.transition()
.style("fill", "blue")
// red dots
svg.append('g')
.selectAll("dot")
.data(data)
.enter()
.append("circle")
.attr("cx", function (d) { return x(d.x1); } )
.attr("cy", function (d) { return y(d.y1); } )
.attr("r", 4.1)
.style("fill", "red")
}
Thank you for any kind of help in advance!
Yes it's possible.
Using the property transition and combining with duration in milliseconds. Look below:
https://jsfiddle.net/mathyaku/L5bpaxwv/1/
function drawScatterplot(data, selector) {
// set the dimensions and margins of the graph
var margin = { top: 10, right: 30, bottom: 30, left: 60 },
width = 700 - margin.left - margin.right,
height = 700 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select(selector)
.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 + ")");
//Read the data
// Add X axis
var x = d3.scaleLinear()
.domain([0, 1])
.range([0, width]);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Add Y axis
var y = d3.scaleLinear()
.domain([0, 1])
.range([height, 0]);
svg.append("g")
.call(d3.axisLeft(y));
// Add red dots
svg.append('g')
.selectAll("dot")
.data(data)
.enter()
.append("circle")
.attr("cx", function (d) { return x(d.x1); })
.attr("cy", function (d) { return y(d.y1); })
.attr("r", 4.1)
.style("fill", "red")
svg.selectAll("circle")
.transition()
.duration(2000)
.attr("cx", function (d) { return x(d.x); })
.attr("cy", function (d) { return y(d.y); })
.style("fill", "blue")
}
drawScatterplot(data, '#Scatterplot');

Draw wordcloud for each point in scatterplot

I create a scatterplot which is defined on the following data (note that only first two fields are currently using for plotting):
var data = [[5,3,"{'text':'word1',size:4},{'text':'word2','size':1}"],
[3,5,"{'text':'word3',size:5},{'text':'word4','size':4}"],
[1,4,"{'text':'word1',size:3},{'text':'word2','size':5},{'text':'word3','size':2}"],
[2,3,"{'text':'word2',size:1},{'text':'word3','size':5}"]];
Next, when we click on each particular point in the scatterplot the application should attach a wordcloud which is defined from words stored in the 3rd field of the data variable. I use Jason Davies's implementation of wordcloud. Currently (for demo purposes), the wordcloud is generating onlyfrom the static data stored in variable frequency_list. The current code is also stored on JSFiddle.
Any idea how to proceed?
var data = [[5,3,"{'text':'word1',size:4},{'text':'word2','size':1}"],
[3,5,"{'text':'word3',size:5},{'text':'word4','size':4}"],
[1,4,"{'text':'word1',size:3},{'text':'word2','size':5},{'text':'word3','size':2}"],
[2,3,"{'text':'word2',size:1},{'text':'word3','size':5}"]];
var margin = {top: 20, right: 15, bottom: 60, left: 60},
width = 500 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom;
var x = d3.scale.linear()
.domain([0, d3.max(data, function(d) { return d[0]; })])
.range([ 0, width ]);
var y = d3.scale.linear()
.domain([0, d3.max(data, function(d) { return d[1]; })])
.range([ height, 0 ]);
var chart = d3.select('body')
.append('svg:svg')
.attr('width', width + margin.right + margin.left)
.attr('height', height + margin.top + margin.bottom)
.attr('class', 'chart')
var main = chart.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
.attr('width', width)
.attr('height', height)
.attr('class', 'main')
// Draw the x axis
var xAxis = d3.svg.axis()
.scale(x)
.orient('bottom');
main.append('g')
.attr('transform', 'translate(0,' + height + ')')
.attr('class', 'main axis date')
.call(xAxis);
// draw the y axis
var yAxis = d3.svg.axis()
.scale(y)
.orient('left');
main.append('g')
.attr('transform', 'translate(0,0)')
.attr('class', 'main axis date')
.call(yAxis);
var g = main.append("svg:g");
g.selectAll("scatter-dots")
.data(data)
.enter().append("svg:circle")
.attr("cx", function (d,i) { return x(d[0]); } )
.attr("cy", function (d) { return y(d[1]); } )
.attr("r", 5)
.on("mouseover", function(){d3.select(this).style("fill", "red")})
.on("mouseout", function(){d3.select(this).style("fill", "black")});
// FUNCTION TO DISPLAY CIRCLE
g.on('mouseover', function(){
div.style("display", "block")
d3.select("krog").style("fill", "orange");
generate();
});
g.on('mouseout', function(){
//div.style("display", "none")
div.select("svg").remove();
});
var div = d3.select("body")
.append("div")
.attr("class", "tooltip")
.style("display", "none");
// Functions to draw wordcloud
var frequency_list = [{"text":"study","size":40},{"text":"motion","size":15},{"text":"forces","size":10},{"text":"electricity","size":15},{"text":"movement","size":10},{"text":"relation","size":5},{"text":"things","size":10},{"text":"force","size":5},{"text":"ad","size":5}];
var color = d3.scale.linear()
.domain([0,1,2,3,4,5,6,10,15,20,100])
.range(["#ddd", "#ccc", "#bbb", "#aaa", "#999", "#888", "#777", "#666", "#555", "#444", "#333", "#222"]);
// Generates wordcloud
function generate(){
d3.layout.cloud().size([800, 300])
.words(frequency_list)
.rotate(0)
.fontSize(function(d) { return d.size; })
.on("end", draw)
.start();
}
function draw(words) {
d3.select("div").append("svg")
.attr("width", 850)
.attr("height", 350)
.attr("class", "wordcloud")
.append("g")
// without the transform, words words would get cutoff to the left and top, they would
// appear outside of the SVG area
.attr("transform", "translate(320,200)")
.selectAll("text")
.data(words)
.enter().append("text")
.style("font-size", function(d) { return d.size + "px"; })
.style("fill", function(d, i) { return color(i); })
.attr("transform", function(d) {
return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
})
.text(function(d) { return d.text; });
}
You have a couple of problems here.
First, your data has strings for the words. I changed that for an array of objects:
var data = [[5,3,[{'text':'word1',size:4},{'text':'word2','size':1}]],
[3,5,[{'text':'word3',size:5},{'text':'word4','size':4}]],
[1,4,[{'text':'word1',size:3},{'text':'word2','size':5},{'text':'word3','size':2}]],
[2,3,[{'text':'word2',size:1},{'text':'word3','size':5}]]];
After that, I changed the function draw: instead of appending a new div every time you hover a circle, it just change the div content:
div.append("svg")
.attr("width", 300)
.attr("height", 300)
.attr("class", "wordcloud")
.append("g")
But now comes the most important change:
You are displaying the wordcloud every time the user hover a circle, but you're calling the mouseover for the group element. That way, we cannot access the data bound to each specific circle.
Instead of that, we'll set a variable for the circles:
var circle = g.selectAll("scatter-dots")
.data(data)
.enter()
.append("svg:circle");
Thus, we can get the data for each hovered circle, which is the third element in the array:
circle.on('mouseover', function(d){
div.style("display", "block")
d3.select("krog").style("fill", "orange");
generate(d[2]);//here, d[2] is the third element in the data array
});
And we pass this third element (d[2]) to the function generate as a parameter named thisWords:
function generate(thisWords){
d3.layout.cloud().size([800, 300])
.words(thisWords)
.rotate(0)
.fontSize(function(d) { return d.size; })
.on("end", draw)
.start();
}
here is your fiddle: https://jsfiddle.net/jwrbps4j/
PS: you'll have to improve the translate for that words.

Categories