I've created a scatter graph using the following code as a basis:
http://bl.ocks.org/peterssonjonas/4a0e7cb8d23231243e0e
However I'd like to change the background of the tooltip either based upon the colour of the selected element, or by adding a data column related to colour (i.e. d.colour).
The code currently generates tooltip text based upon the selected element via the following lines:
var tip = d3.tip()
.attr("class", "d3-tip")
.offset([-10, 0])
.html(function(d) {
return xCat + ": " + d[xCat] + "<br>" + yCat + ": " + d[yCat];
});
I was hoping that by adding something like:
.style("background", function(d) { return d.colour; })
I'd be able to achieve this. However when I do this I find that d is undefined (by adding a console.log before returning).
I'm a super novice when it comes to this kind of thing, so any advice anyone could give me would be super helpful.
Thanks!
Here is a positive criticism: don't use d3-tip or any other plugin to create your tooltips. Create them yourself. That way, you can have better control over them and customise them the way you want.
Back to the question: without even looking at that plugin's documentation, you can select the element (in this case, a <div>) by class:
d3.select(".d3-tip").style("background-color", color(d[colorCat]));
Here is the updated bl.ocks: http://bl.ocks.org/GerardoFurtado/70f2608e455b61514cc96dff6fe41ea6/65c940cb987ae1cbda5dc352cda54a382a945ae8
Regarding the undefined: the tip.style is not receiving the datum when the event is fired, apparently only tip.html does. To be sure about that you have to check their source code.
Related
With assistance, I've uncovered the way to change elements in a radial dendrogram.
The lines below perform that function.
However, I'm trying to guess at what I need to bold text with the same mouse over. Can someone tell me what I'm missing?
// responsible for changing the style and type of the nodes when mousing over them
d3.selectAll('g.node').attr("id", function(d,i){ return "node"+i});
d3.selectAll('path.link').attr("id", function(d,i){ return "link"+i}); //my guess is on the line
below
d3.selectAll('text').attr("id", function(d,i){ return "text"+i});
// still trying to figure out how to make the text bold on mouse over
d3.selectAll('g.node').each(function(d, i) {
d3.select('#node'+i).on("mouseover", function() {
d3.select('#link'+(i-1))
.attr('style','stroke-width: 4px','style','font-weight: bold'); // my 2nd guess is on the next
line
d3.select('text').attr("font-weight",function(d,i) {return i*800+800;});
}).on("mouseout", function() {
d3.select('#link'+(i-1)).attr('style', 'stroke-width: 1.5px','stroke-opacity: 0.4','stroke:
#555');
});
});
In order to set font-weight - which is a CSS property -, .style should be used instead of .attr:
d3.select('#link'+(i-1))
.style('font-weight','bold');
Useful reference: modifying elements with d3-selection.
(I'm still figuring how best to use this platform)
But my answer was to create a CSS class called, .node text:hover, then increase the font weight within that class.
d3.select('#' + linkId[0]).transition().duration(2500).attr('stroke', 'green');
d3.select('#' + nodeId[0]).transition().duration(5000).attr('fill', 'blue');
I have the above code that is animating a graph traversal. I want the second transition to only activate (and preferably remove the duration) once the link has been transitioned. How would i accomplish this? I have tried putting the whole second line of code within a timeout like so:
setTimeout(() => { d3 transition here }, 2500);
However this completely messed up the timing of events. Im basically looking for something similar to python where you can call .sleep(milliseconds) to specify code execution wait a certain amount of time.
There are two quick options available:
transition.delay()
You could use transition.delay(time) which allows you to specify a delay before a transition starts. This would look like:
d3.select('#' + linkId[0]).transition().duration(2500).attr('stroke', 'green');
d3.select('#' + nodeId[0]).transition().delay(2500).duration(5000).attr('fill', 'blue');
While simple, I'd suggest using the next approach instead.
transition.on("end", ... )
Another option is to use transition.on("end", function() { /* set up next transition */ }). Now .on("end",callbackFunction) will trigger on the end of each transition (if transitioning many elements, it'll trigger when each element finishes its transition), but you are transitioning single elements (as IDs are unique), so you could use something like this:
d3.select('#' + linkId[0]).transition()
.duration(2500)
.attr('stroke', 'green')
.on("end", function() {
d3.select('#' + nodeId[0]).transition().duration(5000).attr('fill', 'blue');
})
If you had many elements transitioning simultaneously you'd need to modify this slightly to check to see if any transitions were still in progress before starting the next transition.
I have a composite chart of 2 line charts however I need to add a third chart to it.
This third chart will have these unique properties:
The data will come in via an ajax call and be available as a two dimensional array [[timestamp,value],[timestamp,value]...]
Every new ajax call needs to replace the values of the previous one
It does not need to respect any of the filters and will not be used on any other charts
It will however need to use a differently scaled Y axis.. (and labeled so on the right)
This is how the chart currently looks with only two of the charts
This is my code with the start of a third line graph... Assuming I have the array of new data available i'm at a little loss of the best/simplest way to handle this.
timeChart
.width(width).height(width*.333)
.dimension(dim)
.renderHorizontalGridLines(true)
.x(d3.time.scale().domain([minDate,maxDate]))
.xUnits(d3.time.months)
.elasticY(true)
.brushOn(true)
.legend(dc.legend().x(60).y(10).itemHeight(13).gap(5))
.yAxisLabel(displayName)
.compose([
dc.lineChart(timeChart)
.colors(['blue'])
.group(metric, "actual" + displayName)
.valueAccessor (d) -> d.value.avg
.interpolate('basis-open')
.dimension(dim),
dc.lineChart(timeChart)
.colors(['red'])
.group(metric, "Normal " + displayName)
.valueAccessor (d) -> d.value.avg_avg
.interpolate('basis-open'),
dc.lineChart(timeChart)
.colors(['#666'])
.y()#This needs to be scaled and labeled on the right side of the chart
.group() #I just want to feed a simple array of values into here
])
Also side note: I've noticed what I might be a small bug with the legend rendering. As you can see in the legend both have the same label but i've used different strings in the second .group() argument.
I believe you are asking a few questions here. I will try to answer the main question: how do you add data to a dc chart.
I created an example here: http://jsfiddle.net/djmartin_umich/qBr7y/
In this example I simply add random data to the crossfilter, though this could easily be adapted to pull data from the server:
function AddData(){
var q = Math.floor(Math.random() * 6) + 1;
currDate = currDate.add('month', 1);
cf.add( [{date: currDate.clone().toDate(), quantity: q}]);
$("#log").append(q + ", ");
}
I call this method once a second. Once it completes, I reset the x domain and redraw the chart.
window.setInterval(function(){
AddData();
lineChart.x(d3.time.scale().domain([startDate, currDate]));
dc.redrawAll();
}, 1000);
I recommend trying to get this working in isolation before trying to add the complexity of multiple y-axis scales.
Currently your best bet is to create a fake group. Really the .data method on the charts is supposed to do this, but it doesn't work for charts that derive from the stack mixin.
https://github.com/dc-js/dc.js/wiki/FAQ#filter-the-data-before-its-charted
I'm trying to draw a network topology using D3sj force directed layout. I did a basic topology with link and node. However, now i want to show the interface name for each link on each node as the following picture. Could you guys guide me how i can do that?
Thank you in advance!
P/S I attached my topology here!1
Ideally you should present the javascript that you have already written, and explained what it does do, and what is missing. Having said that, I recently finished work on a similar project to what you describe, so had the following results handy.
Does this jsfiddle do what you are attempting?
There are two key components. The first is in defining the text elements (here I append them to an SVG element):
var text = svg.selectAll('text')
.data(force.nodes())
.enter().append('text')
.text(function (d) { return d.name });
Here I'm assuming that the nodes[] array contains objects with a .name property that is to be displayed.
The second component is to translate the text elements to their appropriate positions, inside the tick handler:
function tick () {
text.attr('transform', function (d) {
return 'translate(' + d.x + ',' + d.y + ')';
});
// Other code for nodes and links
}
See the jsfiddle for a complete working example, including commented code that should allow you to add images at the nodes if you want to try to reproduce your sample image more closely.
I am trying to modify this D3 example to use with my dataset
http://mbostock.github.io/d3/talk/20111116/airports.html
I think my problem is creating the array of coordinates for calculating the Voronoi polygons, but at this point I'm not sure.
I'm getting a Uncaught TypeError: Cannot read property '0' of undefined error that points to the line where I am calling the array. The code is live, please see here
http://cds.library.brown.edu/projects/mapping-genres/symbol-maps/brown-voronoi-map.html
The map displays just fine, but the data points, the radio button, and the voronoi lines do not appear (I'm not trying to show lines between data points, so that code has been removed).
Any thoughts or suggestions would be most appreciated. Many thanks!
There are a number of small things going on here which can be easily sorted out but some will require a bit of work. The first is that you're using a new version of d3 (v3) and the example you're trying to replicate is an older version. There have been significant changes between the versions on the mapping side of things. So you can either use an old version of d3 (v2 will work I think) or investigate the changes.
The Uncaught TypeError: Cannot read property '0' of undefined error is being generated because d3.geom.voronoi(positions) line is producing NaN. I'm not 100% sure why but you could just filter these out to get a temporary fix. A simple filter would be something like:
g.append("svg:path")
.attr("class", "cell")
.attr("d", function(d, i) {
if (polygons[i][0][0]) {
return "M" + polygons[i].join("L") + "Z";
}
})
with the if true when there is not a false value (NULL, NaN, etc) in the first element of the polygon. Please note that this is temporary fix while you investigate why you're getting NaN and this will not produce the correct voronoi polygons.
The fix for the data points is very simple, you just have to set the radius to something greater than 0 such as 10 (although I imagine that you might want to scale the dots to a property of your data like TotalPublished). For instance see the last line:
circles.selectAll("circle")
.data(locations.rows
.sort(function(a, b) { return b.TotalPublished - a.TotalPublished; }))
.enter().append("svg:circle")
.attr("transform", function(d) { return "translate(" + projection(d.coordinates) + ")"; })
.attr("r", 10);
The radio button (or checkbox) does not show up as it's not generated by the javascript (although it can be). In the example you refer to its generated in the html in this snippet:
<div style="position:absolute;bottom:0;font-size:18px;">
<input type="checkbox" id="voronoi"> <label for="voronoi">show Voronoi</label>
</div>
Once you've worked through this you'll need to get the voronoi lines working. I think with your code as is the voronoi lines won't show up as the checkbox is not checked. So you could comment these lines out and see what happens. A quick tip with paths is that if you want to just show the lines you need to set the fill style to none and the stroke to whatever colour you want. If you don't set the fill to none you'll just get a black screen.