I am trying to implement the FishEye lens (Cartesian) in my scatterplot.
I am trying to follow this approach, but apparently my selector already fails.
I have my FishEye defined as
var fisheye = d3.fisheye.circular().radius(120);
svg.on("mousemove", function() {
fisheye.focus(d3.mouse(this));
console.log("here " + points.selectAll("circle").length);
points.selectAll("circle").each(function(d) {
console.log("aaa");
d.fisheye = fisheye(d);
/*points.selectAll("circle")
.each(function(d) {
console.log("???");
this.attr("cx", function(d) { return d.fisheye.x; })
this.attr("cy", function(d) { return d.fisheye.y; })
this.attr("r", function(d) { console.log("hype"); return 10; });
}); */
});
});
and my points is defined as
points = svg.append("g")
.attr("class", "point")
.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) { // Set the x position using the x-scale
return x(d.x);
})
.attr("cy", function(d) { // Set the y position using the y-scale
return y(d.y);
})
.attr("r", 5) // Set the radius of every point to 5
.on("mouseover", function(d) { // On mouse over show and set the tooltip
if(!isBrushing){
tooltip.transition()
.duration(200)
.style("opacity", 0.9);
tooltip.html(d.symbol + "<br/> (" + parseFloat(x(d.x)).toFixed(4)
+ ", " + parseFloat(y(d.y)).toFixed(4) + ")")
.style("left", (d3.event.pageX + 5) + "px")
.style("top", (d3.event.pageY - 28) + "px");
}
})
.on("mouseout", function(d) { // on mouseout, hide the tooltip.
tooltip.transition()
.duration(500)
.style("opacity", 0);
});
The console.log with "here" is spamming when I am moving the mouse, and shows the correct amount. Hwoever, the each loop is never executed as I do not see "aaa". I have also tried to just use selectAll("circle") but that doesn't work either.
What am I doing wrong and how can I get my FishEye to work?
Related
'''
#Parent svg
svg.append('g')
.selectAll("dot")
.data(storage)
.join("circle")
.attr("cx", function (d) { return x(d.x); })
.attr("cy", function (d) { return y(d.y); })
.attr("r", .5)
.style("fill", "green")
.on("mouseover", function (e, d) {
d3.select(this).transition()
.duration(200)
div.style("left", (e.pageX) + "px")
.style("top", (e.pageY - 28) + "px");
console.log(e, d);
div.transition()
.duration(500)
.style("opacity", 0);
div.transition()
.duration(200)
.style("opacity", .9);
div.html(
'X, Y: ' + d.x + ", "+ d.y +
"<br>Pin: " + d.pinName +
"<br>Net: " + d.netName);
s_data = storage.filter(a => a.netName ===d.netName);
#Child svg
svg.append('g')
.selectAll("dot")
.data(s_data)
.join("circle")
.attr("cx", function (d) { return x(d.x); })
.attr("cy", function (d) { return y(d.y); })
.attr("r", .5)
.style("fill", "red")
})
.on('mouseout', function(d,i){
d3.select(this).transition()
.duration('200')
.style('fill', 'green');
})
'''
Initially all dots are green when I mouseover any one of the dots, the set of dots with common netName will highlight, after this I can't go back to normal, i,e I want all dots to be green again so that I can highlight other dots and check for there grouping. The data is basically a JSON object
Take a look at this example: https://codepen.io/ccasenove/pen/RwyzZrV
I have a unique id key on each dot and a netId attribute shared by some dots. If you specify a key function when you bind the data to a d3 selection, then you can obtain a sub selection of dots with the same netId like this:
const _data = data.filter(da => da.netId == d.netId);
svg.selectAll("circle")
.data(_data, d => d.id)
.style("fill", "red")
the d3 data function will return the update selection, i.e. dots that already exist with the ids in the filtered data. So you can change the color of this update selection in mouseover and mouseout functions.
I'm newbie with D3.js and I'm trying to put a text inside a circle but I am only able to do it with one of them and not with all the circles.
You can find all the code in this snipet
And the function where I create the circles and I try to put the text inside of is "setPointsToCanvas"
setPointsToCanvas(canvas, data, scales, x_label, y_label, lang) {
canvas
.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("class", "dot")
.attr("r", 20) //Radius size, could map to another dimension
.attr("cx", function(d) {
return scales.xScale(parseFloat(d.value_x));
}) //x position
.attr("cy", function(d) {
return scales.yScale(parseFloat(d.value_y));
}) //y position
.style("fill", "#FFC107")
.on("mouseover", tipMouseOver)
.on("mouseout", tipMouseOut);
//Ad label for each circle
canvas
.data(data)
//.enter()
.append("text")
.attr("x", function(d) {
return scales.xScale(parseFloat(d.value_x));
})
.attr("y", function(d) {
return scales.yScale(parseFloat(d.value_y) - 0.9);
})
.text(function(d) {
return d.name.substring(0, 3);
})
.style("text-anchor", "middle")
.style("font-weight", "bold")
.style("font-size", "10pt")
.style("fill", "#344761");
let tooltip = d3
//.select("#" + this.props.idContainer)
.select("body")
.append("div")
.attr("class", "tooltip-player")
.style("opacity", 0);
/**
* We define this function inside of setPointsToCanvas to get access to canvas, data, scales and tooltip
* #param {*} d
* #param {*} iter
*/
function tipMouseOver(d, iter) {
let players = data.filter(p => {
if (p.value_x === d.value_x && p.value_y === d.value_y) {
return p;
}
});
let html = "";
for (let i = 0; i < players.length; i++) {
let text_x =
lang === "es"
? String(parseFloat(players[i].value_x).toFixed(2)).replace(
".",
","
)
: parseFloat(players[i].value_x).toFixed(2);
let text_y =
lang === "es"
? String(parseFloat(players[i].value_y).toFixed(2)).replace(
".",
","
)
: parseFloat(players[i].value_y).toFixed(2);
if (i > 0) html += "<hr>";
html +=
players[i].name +
"<br><b>" +
x_label +
": </b>" +
text_x +
"%<br/>" +
"<b>" +
y_label +
": </b>" +
text_y +
"%";
}
tooltip
.html(html)
.style("left", d3.event.pageX + 15 + "px")
.style("top", d3.event.pageY - 28 + "px")
.transition()
.duration(200) // ms
.style("opacity", 0.9); // started as 0!
// Use D3 to select element, change color and size
d3.select(this)
//.attr("r", 10)
.style("cursor", "pointer");
}
/**
* We create this function inside of setPointsToCanvas to get access to tooltip
*/
function tipMouseOut() {
tooltip
.transition()
.duration(500) // ms
.style("opacity", 0); // don't care about position!
//d3.select(this).attr("r", 5);
}
}
And here you can see how I'm only able to get one text inside of one circle and not the text inside all of them.
What am I doing wrong?
Following the advice of #Pablo EM and thanks to #Andrew Reid for your appreciated help I publish the solution to my problem.
How #Andrew Reid said if I have problems with selectAll("text") I have to change it for another text grouper. How I had it, I changed by selectAll("textCircle") and everything works fine to me.
This is the code which writes the text inside each circle. This piede of code you can find it inside of "setPointsToCanvas" method.
//Ad label for each circle
canvas
.selectAll("textCircle")
.data(data)
.enter()
.append("text")
.attr("x", function(d) {
return scales.xScale(parseFloat(d.value_x));
})
.attr("y", function(d) {
return scales.yScale(parseFloat(d.value_y) - 0.9);
})
.text(function(d) {
return d.name.substring(0, 3);
})
.style("text-anchor", "middle")
.style("font-weight", "bold")
.style("font-size", "10pt")
.style("fill", "#344761");
Now, here you've got an image of the final result:
If you access to the code through CodeSandBox posted before you can access to all the code and check how it works perfectly.
I'm using the following example as a template to create a Bubble Chart (https://bl.ocks.org/john-guerra/0d81ccfd24578d5d563c55e785b3b40a).
I'm attempting to display a tooltip every time the mouse hovers a specific circle but for some reason it doesn't seem to work. I would also like to change the text inside the circles to white but I have been unsuccessful so far.
Here is a sample of the JSON file:
{
"name": "POR",
"children": [{
"name": "Clyde Drexler",
"size": 18040,
"color": "#D00328"
},
{
"name": "Damian Lillard",
"size": 12909,
"color": "#D00328"
},
$(document).ready(function() {
let diameter = 750;
let format = d3.format(",d");
let color = d3.scaleOrdinal(d3.schemeCategory20c);
let bubble = d3.pack()
.size([diameter, diameter])
.padding(1.5);
let svgContainer = d3.select("#data-visualisation");
// Append <svg> to body
let svg = svgContainer.append('svg')
.attr('width', diameter)
.attr('height', diameter)
.attr("align", "center")
.attr('class', 'bubble');
// Read the data
d3.json("data/flare.json", function(error, data) {
// error scenario
if (error) throw error;
let root = d3.hierarchy(classes(data))
.sum(function(d) {
return d.value;
})
.sort(function(a, b) {
return b.value - a.value;
});
bubble(root);
//////////////
// tooltip
//////////////
//Create a tooltip div that is hidden by default:
let tooltip = svgContainer
.append("div")
.style("opacity", 0)
.attr("class", "tooltip")
.style("background-color", "black")
.style("border-radius", "5px")
.style("padding", "10px")
.style("color", "white");
// Create 3 functions to show / update (when mouse move but stay on same circle) / hide the tooltip
let showTooltip = function(d) {
tooltip
.transition()
.duration(200)
tooltip
.style("opacity", 1)
.html("Player: " + d.data.className + "<br> Points with franchise: " + d.data.value)
.style("left", (d3.mouse(this)[0] + 30) + "px")
.style("top", (d3.mouse(this)[1] + 30) + "px");
}
let moveTooltip = function(d) {
tooltip
.style("left", (d3.mouse(this)[0] + 30) + "px")
.style("top", (d3.mouse(this)[1] + 30) + "px");
}
let hideTooltip = function(d) {
tooltip
.transition()
.duration(200)
.style("opacity", 0);
}
//////////////
let node = svg.selectAll(".node")
.data(root.children)
.enter()
.append("g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
node.append("title")
.text(function(d) {
return d.data.className + ": " + format(d.data.value);
});
node.append("circle")
.attr("r", function(d) {
return d.r;
})
.style("fill", function(d) {
return d.data.color;
})
.style("stroke", "none")
// trigger tooltip functions
.on("mouseover", showTooltip)
.on("mousemove", moveTooltip)
.on("mouseleave", hideTooltip);
node.append("text")
.attr("dy", "0.3em")
.style("text-anchor", "middle")
.text(function(d) {
return d.data.className.substring(0, d.r / 3.8);
});
});
function classes(root) {
let classes = [];
function recurse(name, node) {
if (node.children) {
node.children.forEach(function(child) {
recurse(node.name, child);
});
} else {
classes.push({
packageName: name,
className: node.name,
value: node.size,
color: node.color
});
}
}
recurse(null, root);
return {
children: classes
};
}
d3.select(self.frameElement)
.style("height", diameter + "px");
});
Here is a fiddle that I just made using the code from the blocks and the tooltip.
There were a couple of errors in the code that you entered.
The tooltip div was being appended to the SVG and that is incorrect, an SVG can't contain a `div', changing it to:
var tooltip = d3.select('body')
.append("div")
.style("opacity", 0)
made the tooltip working.
Then, there was missing the position: absolute in the tooltip style
And finally, the left and top styles in the tooltip, were based only on the bubble, so I added the translation to those coordinates doing something like:
.style("left", (d.x + (d3.mouse(this)[0] + 30)) + "px")
.style("top", (d.y + (d3.mouse(this)[1] + 30)) + "px");
I'm splitting data into categories e.g. rich poor and all. Using dropdown to get those values to display on a scatterplot. First transition happens, everything works as expected. Text labels are correctly displayed too, however when another option is selected and second transition happened half of circles are disappearing and every other transition is messed up. Only works if option all selected than again, first transition works, after that it is all messed up.
Codepen
function render(someData) {
xScale
.domain([
d3.min(someData, function(d) {
return +d.poorToys;
}),
d3.max(someData, function(d) {
return +d.poorToys;
})
]);
yScale
.domain([
d3.min(someData, function(d) {
return +d.richToys;
}),
d3.max(someData, function(d) {
return +d.richToys;
})+ 20
]);
//Adding circles
var circles = svg.selectAll("circle")
.data(someData, function(d) {
return d.country;
});
I believe a problem starts here.
circles
.enter()
.append("circle")
.attr("cx", function(d) {
if (currentSelection === "rich") {
return width - margin.right;
} else if (currentSelection === "poor") {
return margin.left;
} else if (currentSelection === "all") {}
return xScale(+d.poorToys);
})
.attr("cy", function(d) {
if (currentSelection === "rich") {
return margin.top;
} else if (currentSelection === "poor") {
return height - margin.bottom;
} else if (currentSelection === "all") {}
return yScale(+d.richToys);
})
.attr("r", function(d) {
if (currentSelection === "all") {
return rad;
}
})
.style("fill", "red")
.append("title")
.text(function(d) {
return d.country + " reports books for " + d.poorToys + "% in poor areas and " + d.richToys + "% in rich areas.";
});
circles
.transition()
.duration(2000)
.attr("cx", function(d) {
return xScale(+d.poorToys);
})
.attr("cy", function(d) {
return yScale(+d.richToys);
})
.attr("r", function() {
if (currentSelection !== "all") {
return rad * 1.5;
} else {
return rad;
}
});
circles
.exit()
.transition()
.duration(1000)
.style("opacity", 0)
.remove();
//Update x axis
svg.select(".x.axis")
.transition()
.duration(1000)
.call(xAxis);
//Update y axis
svg.select(".y.axis")
.transition()
.duration(1000)
.call(yAxis);
if (currentSelection !== "all"){
var labels = svg.selectAll("text.labels")
.data(someData, function(d){
return d.country;
});
labels
.enter()
.append("text")
.attr("transform", function(d){
return "translate(" + xScale(+d.poorToys) + "," + yScale(+d.richToys) + ")";
})
.attr("dx", 2)
.attr("dy", 1)
.attr("class", "labels")
.style("fill", "white")
.style("font-size", "5px")
.text(function(d){
return d.country;
});
labels
.transition()
.duration(2000)
.style("opacity", 1);
labels
.exit()
.remove();
} else {
svg.selectAll("text.labels")
.transition()
.duration(1000)
.style("opacity", 0)
.remove();
}
}
You incorrectly give your x axis a class of x_axis on line 57 then later try to select it as x.axis in your render function on line 179.
Once you fix that up, I think it should work as expected.
svg
.append("g")
.attr("class", "x axis")
.attr("transform", "translate(" + -14 + "," + (height + 30) + ")")
.call(xAxis);
Updated Pen
Apart from the axis problem found by #ksav your main problem is that you don't position the labels. Many labels are present in rich and poor.
var labels = svg.selectAll("text.labels")
.data(someData, function(d){ return d.country; });
labels
.enter()
.append("text")
.attr("x", function(d){ return xScale(+d.poorToys); })
.attr("y", function(d){ return yScale(+d.richToys); })
.attr("dx", 2)
.attr("dy", 1)
.attr("class", "labels")
.attr("opacity", 0)
.style("fill", "white")
.style("font-size", "8px")
.text(function(d){ return d.country; })
.merge(labels)
.transition()
.duration(2000)
.attr("x", function(d){ return xScale(+d.poorToys); })
.attr("y", function(d){ return yScale(+d.richToys); })
.attr("opacity", 1);
Also don't position the circles based on the selection
circles
.enter()
.append("circle")
.attr("cx", function(d) { return xScale(+d.poorToys); })
.attr("cy", function(d) { return yScale(+d.richToys); })
.attr("r", function(d) { return rad; })
.style("fill", "red")
.append("title")
.text(function(d) {
return d.country + " reports books for " + d.poorToys + "% in poor areas and " + d.richToys + "% in rich areas.";
});
I have a moving <div> object. I want to show/update this tooltip info, let say every 2s. It works fine but the position doesn't change even if the object had moved, the tooltip keeps the initial position and stay static (sorry i didn't provide a working example).
How can I update its position to follow the <div> (not the mouse) ?
event.pageX and event.pageY
The description below is for event.pageX
Description: The mouse position relative to the left edge of the document.
Below is the code i had used in one of my application.
You can show the div below on a mouseover event.
date.selectAll("rect")
.data(function(d) {
return d.mattel_data;
})
.enter().append("rect")
.attr("width", x.rangeBand())
.attr("y", function(d) {
return y(d.y1);
})
.attr("height", function(d) {
return y(d.y0) - y(d.y1);
})
.style("fill", function(d) {
return color(d.name);
})
.on("mouseover", function(d) {
div.transition()
.duration(100)
.style("opacity", .9);
var hoverVal = (d.y1 - d.y0);
var formatter = new Intl.NumberFormat('en-US',{
style : 'currency',
currency : 'USD',
minimumFractionDigits : 2,
});
var pop = formatter.format(hoverVal);
// console.log(pop);
div.html("Value:" + pop + "<br/>" + d.name)
.style("left", (d3.event.pageX - 140) + "px")
.style("top", (d3.event.pageY - 80) + "px")
.style('position', 'absolute')
.style('font-size', '15px')
.style('background', function() {
return color(d.name);
})
.style('color', function() {
return getContrastYIQ(color(d.name));
})
})
.on("mouseout", function(d) {
div.transition()
.duration(200)
.style("opacity", 0);
});
See if that helps you out.
Also if you post your code here that will help to understand your problem more clearly