I am wondering why doing the following is not possible:
div.text(function(d) {return " bought " + d.USD;})
It works fine for the either red or green circles earlier in the code:
.style("fill", function(d) {if (d.USD <= 0) {return "green"}
else { return "red" };})
Here is the extract of my code responsible for drawing the circles and adding a tooltip:
// Draw circle around values
svg.selectAll("dot")
.data(bitstamp_data)
.enter().append("circle")
.attr("r", function(d) { return Math.sqrt(Math.abs(d.USD)) - 5; })
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.Price); })
.style("fill-opacity", 0.7)
.attr("stroke", "black")
// Show different colour depending on buying or selling USD based on positive or negative d.USD
.style("fill", function(d) {
if (d.USD <= 0) {return "green"}
else { return "red" }
;})
// Tooltip section
.on("mouseover", function(d) {
div.transition()
.duration(400)
.style("opacity", .9);
div.text(function(d) {return " bought " + d.USD
;})
.style("left", (d3.event.pageX + 15) + "px")
.attr("stroke", "black")
.style("top", (d3.event.pageY - 28) + "px");
})
Related
I'm trying to color the circles per a .csv data, column "COLOR". The "COLOR" includes "red", "yellow" , "green" -but right now the colors are not transferring... just the default black...
var col = function(d) {return d.COLOR};
svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 15)
.attr("cx", xMap)
.attr("cy", yMap)
.style("fill", col)
.style("opacity", ".5")
.on("mouseover", function(d) {
tooltip.transition()
.duration(200)
.style("opacity", .9);
tooltip.html(d["Cereal Name"] + "<br/> (" + xValue(d)
+ ", " + yValue(d) + ")")
.style("left", (d3.event.pageX + 5) + "px")
.style("top", (d3.event.pageY - 28) + "px");
data.forEach(function(d) {
d.POPULATION = +d.POPULATION;
d.REVENUE = +d.REVENUE;
d.COLOR = +d.COLOR;
Your COLOR values in your CSV contain quotation marks " which will be part of the parsed strings in data. Therefore, you end up with attribute values like fill=""yellow"" which is not valid. Hence, the black default color.
One way around this might be to get rid of the quotation marks in the CSV itself. If this is not feasible, you might adjust your color accessor function col to something like the following:
var col = function(d) {return d.COLOR.substring(1, d.COLOR.length - 1)};
Have a look at this working demo:
var data = [{ COLOR: '"yellow"'}];
var col = function(d) { return d.COLOR.substring(1, d.COLOR.length - 1); };
d3.select("body").append("svg")
.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("cx", 100)
.attr("cy", 100)
.attr("r", 10)
.attr("fill", col);
<script src="https://d3js.org/d3.v4.js"></script>
I want to add tooltip to this chart.
I am referring to this and this example
The issue is that their are no unique points on the line in the SVG it just has the path tag.
svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 5)
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.close); })
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div .html(formatTime(d.date) + "<br/>" + d.close)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
Like the above code selects the dot on the SVG but I dont have any specific element to bind the tooltip.
Can any one help me in this as I am new to d3.js.
you should take d.value for y:
.attr("cy", function(d) { return y(d.value); })
and now append new element on mouseover:
.on("mouseover", function(d) {
svg.append("text")
.text(d.value)
.attr('class', 'tooltip').style("font-size","10px")
.attr("x", x(d.date))
.attr("y", y(d.value))
.attr('fill', 'red');
})
.on("mouseout", function(d) {
d3.selectAll('.tooltip').remove();
});
I was wondering if there is a way to change the Scatter plots refresh speed?
As you can see in this link the scatter plots gets updated but the time gap between the appearance and disappearance is unreasonable, it look like they are flashing dots.... I tried moving the circle.remove() function right above the circle.transition but it makes no difference.
Below is the relevant code of the refresh function. Thanks!
function updateData() {
// Get the data again
data = d3.json("2301data.php", function(error, data) {
data.forEach(function(d) {
d.dtg = parseDate(d.dtg);
d.temperature = +d.temperature;
// d.hum = +d.hum; // Addon 9 part 3
});
// Scale the range of the data again
x.domain(d3.extent(data, function(d) { return d.dtg; }));
y.domain([0, 60]);
var svg = d3.select("#chart1").select("svg").select("g");
svg.select(".x.axis") // change the x axis
.transition()
.duration(750)
.call(xAxis);
svg.select(".y.axis") // change the y axis
.transition()
.duration(750)
.call(yAxis);
svg.select(".line") // change the line
.transition()
.duration(750)
.attr("d", valueline(data));
var circle = svg.selectAll("circle").data(data);
circle.remove() //remove old dots
// enter new circles
circle.enter()
.append("circle")
.filter(function(d) { return d.temperature > 35 })
.style("fill", "red")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d.dtg); })
.attr("cy", function(d) { return y(d.temperature); })
// Tooltip stuff after this
.on("mouseover", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
div.transition()
.duration(200)
.style("opacity", .9);
div .html(
d.temperature + "C" + "<br>" +
formatTime(d.dtg))
.style("left", (d3.event.pageX + 8) + "px")
.style("top", (d3.event.pageY - 18) + "px");})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
circle.transition().attr("cx", function(d) { return x(d.dtg); });
// exit
circle.exit();
});
}
Looking at your example as it runs, you appear to have loads more circles in the dom than are visible. This is because you add circles for all the data, but then only give positions to those that meet the filter criteria you set.
There was a related question the other day about data filtering versus d3 filtering - Filtering data to conditionally render elements . Use data filtering if you don't want to add something full stop, use d3.filter if you want to isolate some elements for special treatment (transitions, different styling etc).
At the moment you're filtering the d3 selection once all the circles are added, but in your case I'd suggest filtering the data before it gets to that stage is best (and as suggested by others in that other question). This may make it run faster (but you're also at the mercy of db updates by the look of your example?)
data = data.filter (function(d) { return d.temperature > 35; }); // do filtering here
var circle = svg.selectAll("circle").data(data);
circle.exit().remove() //remove old dots
// enter new circles
circle.enter()
.append("circle")
.style("fill", "red")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d.dtg); })
.attr("cy", function(d) { return y(d.temperature); })
...
PS. It's a bit confusing what you're trying to do with the circle.remove() and circle.exit(). circle.remove() will remove all existing circles (even ones that exist and have new data), circle.exit() at the end will then have no effect. I'd just have circle.exit().remove() to replace the two calls you make.
Also, without a key function - https://bost.ocks.org/mike/constancy/ - on your .data() call, you may find dots move around a bit. If your data points have ids, use them.
var circle = svg.selectAll("circle").data(data, function(d) { return d.id; /* or d.dtg+" "+d.temperature; if no id property */});
Thanks to mgraham the problem was solved.! Below is the revised code in case someone else needs it.
function updateData() {
// Get the data again
data = d3.json("data.php", function(error, data) {
data.forEach(function(d) {
d.dtg = parseDate(d.dtg);
d.temperature = +d.temperature;
});
// Scale the range of the data again
x.domain(d3.extent(data, function(d) { return d.dtg; }));
y.domain([0, 60]); // Addon 9 part 4
var svg = d3.select("#chart1").select("svg").select("g");
svg.select(".x.axis") // change the x axis
.transition()
.duration(750)
.call(xAxis);
svg.select(".y.axis") // change the y axis
.transition()
.duration(750)
.call(yAxis);
svg.select(".line") // change the line
.transition()
.duration(750)
.attr("d", valueline(data));
data = data.filter (function(d) { return d.temperature > 35; });
var circle = svg.selectAll("circle").data(data, function(d) { return d.dtg+" "+d.temperature;});
circle.exit().remove() //remove old dots
// enter new circles
circle.enter()
.append("circle")
.style("fill", "red")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d.dtg); })
.attr("cy", function(d) { return y(d.temperature); })
// Tooltip stuff after this
.on("mouseover", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
div.transition()
.duration(200)
.style("opacity", .9);
div .html(
d.temperature + "C" + "<br>" +
formatTime(d.dtg))
.style("left", (d3.event.pageX + 8) + "px")
.style("top", (d3.event.pageY - 18) + "px");})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
circle.transition().attr("cx", function(d) { return x(d.dtg); });
});
}
</script>
I have a scatter plot similar to: http://plnkr.co/edit/MkZcXJPS7hrcWh3M0MZ1?p=preview
I want to give a tooltip on mouse hover for every combination. The tooltip code that i have currently does like:
var tooltip = d3.select("body").append("div") // tooltip code
.attr("class", "tooltip")
.style("opacity", 0);
var circles = svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d.petalWidth); })
.attr("cy", function(d) { return y(d.petalLength); })
.style("fill", function(d) { return color(d.species); })
.on("mouseover", function(d) {
tooltip.transition()
.duration(200)
.style("opacity", 1.0);
tooltip.html(d.petalLength+", "+d.petalWidth)
.style("left", (d3.event.pageX + 5) + "px")
.style("top", (d3.event.pageY - 18) + "px");
})
.on("mouseout", function(d) {
tooltip.transition()
.duration(500)
.style("opacity", 0);
});
This will fail to return the correct tooltip for combinations other than petalWidth and d.petalLength.
Is there any way of knowing which combination has been selected and the associated numerical value for the combination?
To do this:
First store the tool-tip info in a new variable(displayX/displayY) like this:
.attr("cx", function(d) {
d.displayX = d.petalWidth;//so displayX holds the x info
return x(d.petalWidth);
})
.attr("cy", function(d) {
d.displayY = d.petalLength;//so displayY holds the y info
return y(d.petalLength);
})
When you set the combo reset the variables accordingly.
svg.selectAll(".dot").transition().attr("cy", function(d) {
d.displayY = d[yAxy];//reset the variable displayY
return y(d[yAxy]);
});
Same for
svg.selectAll(".dot").transition().attr("cx", function(d) {
d.displayX = d[xAxy];//reset the variable displayX
return x(d[xAxy]);
});
Now in the tool tip mouse hover use variable(displayX/displayY)
.on("mouseover", function(d) {
tooltip.transition()
.duration(200)
.style("opacity", 1.0);
tooltip.html(d.displayY + ", " + d.displayX)//use displayX and displayY
.style("left", (d3.event.pageX + 5) + "px")
.style("top", (d3.event.pageY - 18) + "px");
})
.on("mouseout", function(d) {
tooltip.transition()
.duration(500)
.style("opacity", 0);
});
working code here
Hope this helps!
I'm making a line chart using d3.js. A tooltip at a point is displayed on mouseover,and disappears on mouseout. I want to display all the tooltips together permanently when the chart is created. Is there a way to do it?
My javascript-
var x = d3.time.scale().range([0, w]);
var y = d3.scale.linear().range([h, 0]);
x.domain(d3.extent(data, function(d) { return d.x; }));
y.domain(d3.extent(data, function(d) { return d.y; }));
var line = d3.svg.line()
.x(function(d) {
return x(d.x);
})
.y(function(d) {
return y(d.y);
})
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var graph = d3.select("#graph").append("svg:svg")
.attr("width", 900)
.attr("height", 600)
.append("svg:g")
.attr("transform", "translate(" + 80 + "," + 80 + ")");
var xAxis = d3.svg.axis().scale(x).ticks(10).orient("bottom");
var yAxisLeft = d3.svg.axis().scale(y).ticks(10).orient("left");
var area = d3.svg.area()
.x(function(d) { return x(d.x); })
.y0(h)
.y1(function(d) { return y(d.y); });
graph.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("class", "circle")
.attr("cx", function (d) { return x(d.x); })
.attr("cy", function (d) { return y(d.y); })
.attr("r", 4.5)
.style("fill", "black")
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div
.html(d.y) + "<br/>" + d.x)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 30) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
To label each data point, you can add text elements at the appropriate positions. The code would look something like this.
graph.selectAll("text").data(data).enter()
.append("text")
.attr("x", function (d) { return x(d.x); })
.attr("y", function (d) { return y(d.y) - 10; })
.text(function(d) { return d.value; });