Hi, I am using dc.js.
I can not understand how can I get data for building the tooltip shown in the screenshot.
How do I get the selected items in the event handler brush.on ( 'brushend.foo', function () {}) ?
This is my handler to draw the tooltip:
var brush = this.chart.brush();
brush.on('brushend.foo', function() {
let selection = self.chart.select('.extent');
let tooltipValues = {
maxProbability: '-',
minProbability: '-',
minImpact: '-',
maxImpact: '-',
}
selection.on('mousemove', function(){
selection.on
div.transition()
.duration(200)
.style("opacity", 1);
div.html(
`
<div> Probability (percents) max: ${tooltipValues.maxProbability} <div>
<div> Probability (percents) min: ${tooltipValues.minProbability} <div>
<div> Impact max: ${tooltipValues.maxImpact} <div>
<div> Impact min ${tooltipValues.minImpact} <div>
`
)
.style("left", (event.pageX) + "px")
.style("top", (event.pageY - 28) + "px")
.style("class", "content")
})
.on("mouseout", function(d) {
div.transition()
.duration(300)
.style("opacity", 0);
});
});
Rather than trying to use the dots in the chart, I'd use the crossfilter objects to retrieve the data. (The model in our MVC is crossfilter, after all.)
So you can use self.chart.dimension().top(Infinity) to get all the raw rows of data which are currently filtered in. Note that dimension.top works differently from group.all in that it does observe its own filters. That's what you want here.
If you'd rather work with reduced (grouped) data, you'd have to create a separate dimension & group just to observe all the filters.
The important thing to notice here is that the selection is not particular to the brush or chart. Since the scatter plot already observes any filters on other charts, and you're interested in the brush on the scatter plot, the result is the same as the fully filtered set of rows in the crossfilter instance.
You use d as an argument to the funcion. Depending on what your data looks like d will have different properties. For example d.x and d.y, or d.impact and d.probability
selection.on('mousemove', function(d){
console.log('Hovering x at ' + d.x + ' and y at' + d.y);
console.log(d);
});
Related
I'm currently rendering a US map along with every district's border. The grabbing the features of the topojson, we have an array of ~13,000 rows.
I'm also joining data to the topojson file, and running through a csv of ~180,000 rows. I believe I've optimized this process of joining data by ID enough using memoization, where the CSV is essentially turned into a hash, and each ID is the key to it's row data.
This process^ is run 24 times in Next JS through SSG to further the user experience, and so all 24 versions of this map is calculated before the first visit of this deployment. I'm sadly timing out during deployment phase for this specific web page^.
I've inspected the program and seem to find that painting/filling each district may be what's causing the slowdown. Are there any tips you all use to optimize rendering an SVG map of many path elements? Currently the attributes to this map:
1: tooltip for each district styled in tailwind
2: around 5 properties turned to text from topojson file w/ joined data to display upon hover, displayed by tooltip
3: filled dynamically with this snippet which runs a function based on the district's property type
.attr('fill', function (d) {return figureOutColor(d['properties'].type);})
4: adding a mouseover, mousemove, and mouseout event handler to each district.
Edit: Code snippet of adding attrs to my map
export const drawMap = (svgRef: SVGSVGElement, allDistricts: any[]) => {
const svg = d3.select(svgRef);
svg.selectAll('*').remove();
const projection = d3.geoAlbersUsa().scale(900).translate([400, 255]);
const path = d3.geoPath().projection(projection);
const tooltip = d3
.select('body')
.append('div')
.attr(
'class',
'absolute z-10 invisible bg-white',
);
svg
.selectAll('.district')
.data(allDistricts)
.enter()
.append('path')
.attr('class', 'district stroke-current stroke-0.5')
.attr('transform', 'translate(0' + margin.left + ',' + margin.top + ')')
.attr('d', path)
.attr('fill', function (d) {
return figureOutColor(d['properties'].type);
})
.on('mouseover', function (d) {
return tooltip
.style('visibility', 'visible')
.text(`${d.properties.name});
})
.on('mousemove', function (data) {
return tooltip.style('top', d3.event.pageY - 40 + 'px').style('left', d3.event.pageX + 'px');
})
.on('mouseout', function (d) {
d3.select(this).classed('selected fill-current text-white', false);
return tooltip.style('visibility', 'hidden');
});
I have been going through some code I found online for creating and playing with directed graphs in D3 (http://bl.ocks.org/cjrd/6863459). I asked a question about this yesterday - Directed graph - node level CSS styles and that gave me a general idea of how to add CSS styles to SVG objects. However, I am still unable to do what I want. This is because, in the JS file, they seem to use the "nodes" to create "circles" and then render them all in one go instead of looping through them. In the updateGraph function, we have the lines -
// add new nodes
var newGs= thisGraph.circles.enter()
.append("g");
newGs.classed(consts.circleGClass, true)
.attr("transform", function(d){return "translate(" + d.x + "," + d.y + ")";})
.on("mouseover", function(d){
if (state.shiftNodeDrag){
d3.select(this).classed(consts.connectClass, true);
}
})
.on("mouseout", function(d){
d3.select(this).classed(consts.connectClass, false);
})
.on("mousedown", function(d){
thisGraph.circleMouseDown.call(thisGraph, d3.select(this), d);
})
.on("mouseup", function(d){
thisGraph.circleMouseUp.call(thisGraph, d3.select(this), d);
})
.call(thisGraph.drag);
First of all, I am not sure what the .append("g") means here. But more importantly, the line where the CSS class is applied,
newGs.classed(consts.circleGClass, true)
seems to apply the class to all "circles" in one line. Instead, I want to loop through each node and for the circle of that node, apply a CSS style based on attributes of the node (to keep things simple, lets say that it the "title" starts with a certain text, I want to make it a blue circle). I still have no idea how to do this. Can someone help here? Again, the answers to my previous question helped a lot in understanding CSS but this other issue is still blocking me from doing what I want.
Adding comments for more clarity.
// here thisGraph.circles is data selection
//so if the data array has 10 elements in array it will generate 10 g or groups.
var newGs= thisGraph.circles.enter()
.append("g");
//here we are adding classes to the g
newGs.classed(consts.circleGClass, true)
.attr("transform", function(d){return "translate(" + d.x + "," + d.y + ")";})
//attaching mouse event to the group
.on("mouseover", function(d){
if (state.shiftNodeDrag){
d3.select(this).classed(consts.connectClass, true);
}
})
.on("mouseout", function(d){
d3.select(this).classed(consts.connectClass, false);
})
.on("mousedown", function(d){
thisGraph.circleMouseDown.call(thisGraph, d3.select(this), d);
})
.on("mouseup", function(d){
thisGraph.circleMouseUp.call(thisGraph, d3.select(this), d);
})
.call(thisGraph.drag);//attaching drag behavior to the group
What does this line mean?
newGs.classed(consts.circleGClass, true)
This line means to add class to all the created g DOM element or group.
In the code you referring it means circleGClass: "conceptG"
Read this on how to add CSS to DOM in D3
In the code you are appending circle to the group like this
newGs.append("circle")
.attr("r", String(consts.nodeRadius));
So now each group will have a circle.
Next Question
I want to loop through each node and for the circle of that node, apply a CSS style based on attributes of the node
You can iterate through all the circles and add style depending on the data associated with the node like this.
newGs.append("circle")
.attr("r", String(consts.nodeRadius))
.style("fill", function(d){
if(d)//some condition on data
{
return "red";
}
else
return "blue";
});
Question:
if you could tell me how to add CSS classes instead of "red", "blue" it would be every thing I need.
To add class you can do like this.
newGs.append("circle")
.attr("r", String(consts.nodeRadius))
.attr("class", function(d){
function(d){
if(d)//some condition on data
{
return "red";//this will put class red in the node.
}
else
return "blue";//this will put class blue in the node.
});
Another way of doing the same:
newGs.append("circle")
.attr("r", String(consts.nodeRadius))
.classed({
'red': function(d) { return d.condition1 == "something"; },
'blue': function(d) { return d.condition1 != "something"; }
});
Hope this helps!
Please see
http://bl.ocks.org/rkirsling/5001347
It shows some nodes and the edges between them. Can you tell what code to add in that and where so that the edges have labels. You can assume any suitable location for the labels and you can also assume any label text.
Thank you.
You can add labels just as you add the paths for the links themselves. All you need to do is calculate the position according to the positions of the two nodes the link connects. The code would look something like this.
svg.selectAll("text").data(links).enter()
.append("text")
.attr("x", function(d) { return d.source.x + (d.target.x - d.source.x)/2; })
.attr("y", function(d) { return d.source.y + (d.target.y - d.source.y)/2; })
.text(function(d) { return d.something; });
Note that in your tick function, you would also need to update the position of the labels.
I know how to add text element to simple node (append text). The problem is when I would like to add text to path surrounding several nodes. I have created example on http://jsfiddle.net/FEM3e/5/ Please ignore nodes in upper left corner. So I have two groups of nodes. And I would like to add text for each group. Printscreen of desired output http://dopisna.bencin.si/screenshot.png.
I set path in
force.on("tick", function () {
node.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
});
vis.selectAll("path")
.data(groups)
.attr("d", singlePath)
.enter().insert("path", "g")
.style("fill", groupFill)
.style("stroke", groupFill)
.style("stroke-width", 57)
.style("stroke-linejoin", "round")
.style("opacity", .7);
});
I have tried appending text with no success. I am asking for some hints.
OK then. The problem is that you're using text instead of textPath. I've modified your fiddle and now there's some text, albeit some rather ugly text, appended to your path.
The only real change I've made is the addition of this snippet:
vis.selectAll("text")
.data(groups)
.enter()
.append("text")
.attr("x", 8)
.attr("dy", 28)
.append("textPath")
.attr("xlink:href", function (d,i) { return "#path_" + i; })
.text(function (d,i) { return "path_" + i; });
You can see that you go through the usual selection and data binding. You then append your text with the attributes you want (definitely change the ones I borrowed from Mikes Bl.ock) and then you append the text path linking it to a path element in the xlink:href attribute. Obviously you then create some text. One of the cool things about textPath is that it allows you append curved paths.
I think that there's a bit of overkill using the groups as data for the textPath, so you might want to select a more appropriate data selection to bind to this.
I'm new to this kind of forum and my English skills are not the best but I'll try to do my best :).
There is an example of a Line Chart with View Finder at nvd3 website. This is the one (examples\lineWithFocusChart.html, nvd3 zip package) which I have been working with during the last 2 days. I have made only one change to the example's format: I use dates in the X axis instead of normal numbers.
Here are my 2 questions:
1- How can i rotate all the ticks' labels in the x axis? My dates are too long (%x %X, day and time) and I want them rotated in oder to improve their viewing. I can only get 2 ticks rotated (the max and min, the edges, of the x axis). This is the code I modify inside the "switch (axis.orient())" block at nv.d3.js:
case 'bottom':
axisLabel.enter().append('text').attr('class', 'axislabel')
.attr('text-anchor', 'middle')
.attr('y', 25);
axisLabel
.attr('x', scale.range()[1] / 2);
if (showMaxMin) {
var axisMaxMin = wrap.selectAll('g.axisMaxMin')
.data(scale.domain());
axisMaxMin.enter().append('g').attr('class', 'axisMaxMin').append('text');
axisMaxMin.exit().remove();
axisMaxMin
.attr('transform', function(d,i) {
return 'translate(' + scale(d) + ',0)'
})
.select('text')
.attr('dy', '.71em')
.attr('y', axis.tickPadding())
.attr('text-anchor', 'middle')
.text(function(d,i) {
return ('' + axis.tickFormat()(d)).match('NaN') ? '' : axis.tickFormat()(d)
})
.attr('transform', 'rotate(45)')
;
d3.transition(axisMaxMin)
.attr('transform', function(d,i) {
return 'translate(' + scale.range()[i] + ',0)'
});
}
break;
As you can check i have placed .attr('transform', 'rotate(45)') as new attribute so the max and min ticks are rotated (axisMaxMin). I have tried this way (throughout the nv.d3.js file) with the other text elements that I think are associated with the x ticks but it doesnt work. Any idea? Where I have to put the transformation in order to show all the X labels rotated?
2- In the example, when you place the mouse over the line, no event is triggered to show the value (x,y) associated with the point. How can i show those values? I've tried to copy-paste the methods used in other examples where these values are showed but it doesnt work. Any idea?
Thanks for sharing your time and knowledge :D.
There was a recent update to nvd3 that makes rotating the x-axis tick labels really easy. There is now a function of the axis model called rotateLabels(degrees) that takes an integer and will rotate your xTick labels the specified number of degrees. To rotate all xTick labels 45 degrees back, you could use it like this:
var chart = nv.models.lineChart();
chart.xAxis.rotateLabels(-45);