Drag nvd3 line graph using drag behaviour - d3.js

I have been working in nvd3 for sometime.
I faced a situation like, I need to drag and align line graph using mouse. refer here for the pic
I used d3 functionality
"d3.behaviour.drag().on("drag", move)"
and the move function would be
function move(d) {
d3.select(this)
.transition()
//.duration(500)
.ease("linear")
.attr("transform", function(d) {
return "translate("+d3.event.x+")";
});
}
the problem is this is not working in nvd3 graphs because d3.event.x is not available or may be available in different way. I could not find a way to drag.
But if you put hard code number like
return "translate(100)";
its working which is in the picture.
Can anyone help me in this situation please to find out d3.event.x functionality in nvd3.
Any help would be grateful.

I figured out the problem. Just get the d3.event.x in a variable and pass that variable to the translate function. It worked.
function move(d) {
var xevent = d3.event.x;
d3.select(this)
.transition()
.duration(500)
.ease("linear")
.attr("transform", function(d) {
return "translate("+xevent+")";
});
}

Related

Right way to add labels to d3-geomap

I try to add labels to map done with d3-geomap, but can't make it work.
The choropleth map itself gets painted correctly, but adding the labels doesn't work out right. The labels show up on the wrong position.
After painting the map I loaded again the topojson file again and then add text blocks like that:
d3.json("https://d3-geomap.github.io/d3-geomap/topojson/countries/ESP.json").then(function(es) {
let path = d3.geoPath().projection(d3.geoMercator());
svg.select("svg").append("g")
.selectAll("labels")
.data(topojson.feature(es, es.objects.units).features)
.enter().append("text")
.attr("class", "place-label")
.attr("x", function(d) { return path.centroid(d)[0]; })
.attr("y", function(d) { return path.centroid(d)[1]; })
.attr("text-anchor","middle")
.text(function(d) { return d.properties.name; });
});
The problem here is that I can't figure out the correct position of the labels. I also tried to apply the same transform as to the polygons, but then have all the same y position.
Here is the example on bl.ocks.
I made some changes to your code and published it in this gist. When testing it locally, the map displayed like the image below. At this size, labels don't work well, but if you resize the map and/or show fewer labels it should be okay.
Some info on the changes. Whenever you want to draw something on top of a map with d3-geomap, it should go in the postUpdate function. This way the map is already rendered and its SVG elements, the geo data and the path object are accessible via the map object you created. No need to load the Topojson file a second time. The function passed to postUpdate looks like follows:
function drawLabels() {
map.svg.append("g").attr('class', 'zoom')
.selectAll("text")
.data(topojson.feature(map.geo, map.geo.objects.units).features)
.enter().append("text")
.attr("class", "place-label")
.attr("x", function(d) { return map.path.centroid(d)[0]; })
.attr("y", function(d) { return map.path.centroid(d)[1]; })
.attr("text-anchor","middle")
.text(function(d) { return d.properties.name; })
.on('click', map.clicked.bind(map));
}
This page of the documentation shows the available map attributes and accessor functions. Hope this helps.

How to add label or custom value on Mapchart's path using geoChoroplethChart and dc.js?

var IndChart = dc.geoChoroplethChart("#india-chart");
var states = data.dimension(function (d) {
return d["state_name"];
});
var stateRaisedSum = states.group().reduceSum(function (d) {
return d["popolation"];
});
IndChart
.width(700)
.height(500)
.dimension(states)
.group(stateRaisedSum)
.colors(d3.scale.ordinal().domain().range(["#27AE60", "#F1C40F", "#F39C12","#CB4335"]))
.overlayGeoJson(statesJson.features, "state", function (d) { //console.log(d.properties.name);
return d.id;
})
.projection(d3.geo.mercator().center([95, 22]).scale(940))
.renderLabel(true)
.title(function (d) { console.log(d); return d.key + " : " + d.value ;
})
.label(function (d) { console.log(d);}) ;
wanted to add Label or custom value(25%, added in Map chart screen-shots) in map chart for each path using dc.js.
In the comments above, you found or created a working example that answers your original question. Then you asked how to make it work for two charts on the same page.
This is just a matter of getting the selectors right, and also understanding how dc.js renders and redraws work.
First off, that example does
var labelG = d3.select("svg")
which will always select the first svg element on the page. You could fix this by making the selector more specific, i.e. #us-chart svg and #us-chart2 svg, but I prefer to use the chart.select() function, which selects within the DOM tree of the specific chart.
Next, it's important to remember that when you render a chart, it will remove everything and start from scratch. This example calls dc.renderAll() twice, so any modifications made to the first chart will be lost on the second render.
In contrast, a redraw happens when any filter is changed, and it incrementally changes the chart, keeping the previous content.
I prefer to listen to dc.js chart events and make my modifications then. That way, every time the chart is rendered or redrawn, modifications can be made.
In particular, I try to use the pretransition event whenever possible for modifying charts. This happens right after drawing, so you have a chance to change things without any glitches or pauses.
Always add event listeners before rendering the chart.
Adding (the same) handler for both charts and then rendering, looks like this:
usChart.on('pretransition', function(chart) {
var project = d3.geo.albersUsa();
var labelG = chart.select("svg")
.selectAll('g.Title')
.data([0])
.enter()
.append("svg:g")
.attr("id", "labelG")
.attr("class", "Title");
labelG.selectAll("text")
.data(labels.features)
.enter().append("svg:text")
.text(function(d){return d.properties.name;})
.attr("x", function(d){return project(d.geometry.coordinates)[0];})
.attr("y", function(d){return project(d.geometry.coordinates)[1];})
.attr("dx", "-1em");
});
usChart2.on('pretransition', function(chart) {
var project = d3.geo.albersUsa();
var labelG = chart.select("svg")
.selectAll('g.Title')
.data([0])
.enter()
.append("svg:g")
.attr("id", "labelG")
.attr("class", "Title");
labelG.selectAll("text")
.data(labels.features)
.enter().append("svg:text")
.text(function(d){return d.properties.name;})
.attr("x", function(d){return project(d.geometry.coordinates)[0];})
.attr("y", function(d){return project(d.geometry.coordinates)[1];})
.attr("dx", "-1em");
});
dc.renderAll();
I used one more trick there: since pretransition happens for both renders and redraws, but we only want to add these labels once, I use this pattern:
.selectAll('g.Title')
.data([0])
.enter()
.append("svg:g")
.attr("class", "Title");
This is the simplest data binding there is: it says we want one g.Title and its data is just the value 0. Since we give the g element the Title class, this ensures that we'll add this element just once.
Finally, the result of this expression is an enter selection, so we will only add text elements when the Title layer is new.
Fork of your fiddle.

Mouseover events in D3 v4 Chord Diagram

I'm trying to make a D3 chord diagram following Mike Bostock's v4 example and a v3 example with mouseover events.
In the v3 example above, there is a fade function that highlights specific ribbbons for a mouseover'd group:
function fade(opacity) {
return function(d, i) {
svg.selectAll("ribbons")
.filter(function(d) {
return d.source.index != i && d.target.index != i;
})
.transition()
.style("opacity", opacity);
};
}
Though for the life of me I can't get it working in my v4 example, despite trying to put it in a similar spot:
//Draw the ribbons that go from group to group
g.append("g")
.attr("class", "ribbons")
.selectAll("path")
.data(function(chords) { return chords; })
.enter().append("path")
.attr("d", ribbon)
.style("fill", function(d) { return color(d.target.index); })
.style("stroke", function(d) { return d3.rgb(color(d.target.index)).darker(); })
.on("mouseover", fade(.1)) /* Where attempt at mouseover is made */
.on("mouseout", fade(1));
.append("title").
text(function(d){return chordTip(d);})
Here is a jsfiddle with my attempt (with working tooltips, but non-working fade mouseovers): https://jsfiddle.net/wcat76y1/3/
I believe my error has something to do with how I'm segregating variables, but I'm not sure how exactly where I went wrong.
I found part 2 of the previous answer didn't work for me but this did.
function fade(opacity) {
return function(d, i) {
d3.selectAll("g.ribbons path")
.filter(function(d) {
return d.source.index != i && d.target.index!= i;
})
.transition()
.style("opacity", opacity);
};
}
The line you had with just ribbons didn't seem to select the right element. I agree with the first corrections you did, but didn't use the text elements.
Here's my forked fiddle https://jsfiddle.net/kLe38tff/
I managed to figure it out. Looks like there were several issues that combined to obfuscate my attempted solutions. While I can mark this as solved, there are still some elements to my solution that I don't understand .
I was adding the mouseover events to the wrong section (DOM element?). It should have been added to the operations on the group elements not ribbon elements. Hence, it should have gone here:
//Draw the radial arcs for each group
group.append("path")
.style("fill", function(d) { return color(d.index); })
.style("stroke", function(d) { return d3.rgb(color(d.index)).darker(); })
.attr("d", arc)
.on("mouseover", fade(.1))
.on("mouseout", fade(1))
Using svg.selectAll("ribbons") in the fade function was not selecting the elements I wanted (I'm still not sure why not...). Replacing that command with just the variable ribbons allowed the selection to complete successfully:
function fade(opacity) {
return function(d, i) {
ribbons
.filter(function(d) {
return d.source.index != i && d.target.index != i;
})
.transition()
.style("opacity", opacity);
};
}
Adding <text> elements to the variable ribbons during its creation led to the opacity being modified on the text element instead of the ribbon element itself. Separating this to a separate line of code fixed the final issue.
The updated JSFiddle shows the fully working example: https://jsfiddle.net/wcat76y1/5/

How do I add a transition delay between multiple individual transitioning polygons in D3?

The original Code can be found at: http://bl.ocks.org/Guerino1/3a51eeb95d3a8345bc27370e8c9d5b77
I have numerous polygons that are transitioning onto an svg canvas (from left to right, at the bottom of the HTML page).
The code I use to create an transition the chevrons leverages D3 Polygon:
// Create Polygons for SDLC
svgCanvas.selectAll("a")
.data(dataSet)
.enter().append("a")
.attr("xlink:href", function(d) { return d.link; })
.append("svg:polygon")
//svgCanvas.selectAll("polygon")
//.data(dataSet)
//.enter().append("polygon")
.attr("id", function(d,i){ return (selectString.replace(/ /g,'_').replace(/#/g,'') + "_index_" + i); })
.attr("originalcolor","violet")
.style("stroke","blue")
.style("fill","violet")
.style("stroke-width",2)
.attr("points", origin)
.on('mouseover', chevronMouseOver)
.on("mouseout", chevronMouseOut)
.on("click", chevronMouseOut)
.transition() // <------- TRANSITION STARTS HERE --------
.duration(3000)
.attr("points", calculateChevron);
Currently, all polygons transition into the svg canvas, together. I'd like to put a delay between each of them, so that it looks more like dealing from a deck of cards, one at a time.
How would I properly add a D3 delay to make this happen?
Thanks for any help you can offer.
try this..
.transition() // <------- TRANSITION STARTS HERE --------
.delay(function(d,i){ return 100*i; })
.duration(3000)
.attr("points", calculateChevron);

d3.js: Update multiple pie charts

This question has been asked before but I don't think the given solution is the cleanest way to do it so I'm hoping someone may have figured it out since then. I am generating multiple pie charts using d3.js and am dynamically updating them through SQL queries. This is my update function:
function updateCharts()
{
var updatedDataSet = getDataSet();
// Create a pie layout and bind the new data to it
var layout = d3.layout.pie()
.value(function(d, i) { return d[i].count; })
.sort(null);
// Select the pie chart
var pieChartSVG = d3.selectAll("#pie");
// Select each slice of the pie chart
var arcsUpdate = pieChartSVG.selectAll("g.slice")
.data(layout([updatedDataSet]))
.enter();
// Apply transitions to the pie chart to reflect the new dataset
arcsUpdate.select("path")
.attr("fill", function(d, i) { return color(i); })
.attr("d", arc)
.transition()
.duration(1000)
.attrTween("d", arcTween);
}
But it doesn't work. If I take the .enter() out of arcsUpdate then it works but applies the same changes(data and tweens) to each chart. I could get around this by doing a foreach() on the elements returned from pieChartSVG but I can't think of a way of doing that other than the one described in the other question.
I have had to use the solution from the other question as I have to move forward but it's not a "clean" solution so I'd love to know if anybody is aware of a better way to handle it.
I thought you need take the .enter() out of arcsUpdate just like
var arcsUpdate = pieChartSVG.selectAll("path")
.data(layout([updatedDataSet]));
// Apply transitions to the pie chart to reflect the new dataset
arcsUpdate.enter()
.append("path")
.attr("fill", function(d, i) { return color(i); })
.attr("d", arc)
.transition()
.duration(1000)
.attrTween("d", arcTween);
This is the correct way.
And if it applies the same changes(data and tweens) to each chart. Please check out they are binding same updateDataSet or not.

Resources