I have a heatmap designed on d3js. I have added two filters for sorting the data. When I'm changing the filters I see the updated data in the console and the chart refreshes. But after each refresh, the earlier visualized chart still remains.
I have tried .remove() and .exit from d3. These didn't seem to work.
Here's the link for my code in codesandbox: https://codesandbox.io/s/5x6qkjw6kp
Quick Answer
The way you have your code setup, you are calling the whole heatmap creation function every time a filter changes, so you're reading data, axes, everything again. Based on that, you could easily clear all elements on the chart and have them recreated from scratch, rather than using the true enter, exit, update pattern, by putting this line before you define your chart.
d3.select(".chart").selectAll("*").remove();
So your code will now look like
//Setting chart width and adjusting for margins
d3.select(".chart").selectAll("*").remove();
const chart = d3
.select(".chart")
.attr("width", width + margins.right + margins.left)
.attr("height", height + margins.top + margins.bottom)
.append("g")
.attr(
"transform",
"translate(" + margins.left + "," + margins.top + ")"
);
Enter Exit Update
This section has been heavily edited, but looking through the code, quite a lot of changes would have to be made to adapt it to make it use Enter/Exit/Update type processes. Think about putting the main creation steps at the start of the chart creation outside of the function, as these only need to run once at load (titles, axes, things like that). Clicking new elements would then not recreate the whole chart, but just alter the dataset used to create the rect objects.
This works by removing all elements which are children of the main chart group before the chart is recreated.
Hope that makes sense, let me know if anything is unclear and I'll tidy it up!
Related
I'm using D3's forced layout graph to plot the data.
When I call the update function using setInterval with new data, the force layout graph nodes start from a random position. How can I fix this?
I read other answers such as using d.x d.y or alpha(0) , in case of alpha(0) it did not work but d.x d.y using was successful until the data is changed and then the new node is not in same position as it is supposed to be.
My Current working fiddle: https://jsfiddle.net/mootqvs1/5/
In D3.js version 3 you have a property fixed for the node to stay at the current position. In version 4 this property is replaced with d.fx and d.fy, if they are set the node will stay at that position.
I was using nvd3.js to create a simple stacked bar chart as described here
I added the code mentioned in the link in an angular directive as follows:
app.directive('stackBar', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
nv.addGraph(function() {
var chart = nv.models.multiBarChart()
/*.transitionDuration(350)*/
.reduceXTicks(true) //If 'false', every single x-axis tick label will be rendered.
/*.rotateLabels(0) */ //Angle to rotate x-axis labels.
.showControls(true) //Allow user to switch between 'Grouped' and 'Stacked' mode.
.groupSpacing(0.1) //Distance between each group of bars.
chart.xAxis
.tickFormat(d3.format(',f'));
chart.yAxis
.tickFormat(d3.format(',.1f'));
d3.select(element[0])
.datum(exampleData())
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
//Generate some nice data.
function exampleData() {
return stream_layers(3,10+Math.random()*100,.1).map(function(data, i) {
return {
key: 'Stream #' + i,
values: data
};
});
}
}
}
});
Here's my HTML:
<td class="centered" colspan="5">
<div stack-bar>
</div>
</td>
But, I am getting the following error:
Uncaught ReferenceError: stream_layers is not defined
Any idea where I am going wrong?
Also, 'transitonDuration' was also not working so I commented it out. I initially thought, this maybe some problem related to the version of d3, but I am using the latest version and still the problem persists.
EDIT:
Huang Feng's answer helped me get rid of the error. But instead of getting any chart I am getting a lot of text. Here's a screenshot:
Any idea why?
Also, the directive is in an ng-repeat, and thats why there are multiple rows as in the screenshot.
This is because you don't define the stream_layers function, and it's also not a function in nvd3 lib.
It's defined here:
http://nvd3.org/assets/js/data/stream_layers.js
If you want to use it, you should include this lib in the html like:
<script src="../stream_layers.js"></script>
If you want a detail example, here is one for your reference:
http://bl.ocks.org/mbostock/3943967
I encountered similar output (and errors), and there were 3 things needed to fix this:
1. Add stream_layers.js to path
As mentioned by huan feng, you need to include the stream_layers.js function to generate fake data for the example. I downloaded the source code from nvd3.org, and the stream_layers.js file was located in novus-nvd3-8ceb270/test/stream_layers.js (you'll most likely need to update at least that first part of your path based on the latest shortened git hash.
2. Replace transitionDuration with duration
Another issue is that the API has changed for NVD3. After inspecting the source code (line 456 in novus-nvd3-8ceb270/src/models/multiBarChart.js), the transitionDuration method has been replaced with just duration. Here is a snippet from your code above with the duration option corrected:
var chart = nv.models.multiBarChart()
.duration(350)
.reduceXTicks(true) //If 'false', every single x-axis tick label will be rendered.
/*.rotateLabels(0) */ //Angle to rotate x-axis labels.
.showControls(true) //Allow user to switch between 'Grouped' and 'Stacked' mode.
.groupSpacing(0.1) //Distance between each group of bars.
3. Create an SVG
Lastly, your screen shot is only showing text. I think the reason for this is because you were trying to append your chart to a div when it needs to be appended to an SVG element. One way of correcting this is by appending an svg to element[0], and then selecting that svg to use with your vizualization:
var svg = d3.select(element[0])
.append('svg')
.attr('id', 'my-bar-chart-svg')
d3.select('#my-bar-chart-svg')
.datum(exampleData())
.call(chart);
If you're still not seeing anything, you might need to fiddle with the width and height of the SVG element you created to append your chart to. Here's one quick hack for testing:
var svg = d3.select(element[0])
.append('svg')
.attr('id', 'my-bar-chart-svg')
.attr('width', 800)
.attr('height', 400)
d3.select('#my-bar-chart-svg')
.datum(exampleData())
.call(chart);
I have a JavaFX Webview which renders a graph (using D3). I want to implement the 'zoom on mouse scroll' on this graph in the JavaFX application (when the mouse is hovered on top of the webview). I was able to do it successfully on a web-browser using the following d3 js code
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.behavior.zoom().on("zoom", zoomHandler))
.append('g');
function zoomHandler() {
svg.attr("transform",
"translate(" + d3.event.translate + ")"
+ " scale(" + d3.event.scale + ")");
}
but if I do something similar inside the JavaFX application's WebView, the graph is rendered initially in the WebView but on scroll, just disappears and only reappears if I bring back the mouse scroll to the initial position (when the application was started)
The following code is how I use WebView to render the graph
final URL urlLoadMainGraph = getClass().getResource("html/MainGraph.html");
weMainGraph.load(urlLoadMainGraph.toExternalForm());
Is there something wrong on what is being done ?
Thanks
I cannot provide a solution, but i have the same problem.
The problem is, that you have not specified any scale.
JavaFX is just jumping from the minimum to the maximum scale of the zoom with one "tip" on the mousewheel :(. if you specify it in a certain range, it could work out for you. Use this for example to zoom a little bit outside.
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height).call(d3.behavior.zoom().scaleExtent([0.75, 1]);
best so far... hoping for anwsers!
(PS: i couldnt comment... thats why this kind of answer.
There seems to be a bug in your zoomhandler code example. You might want to start with a handler like the following and go on from there:
function zoomHandler() {
alert('zoom');
}
Here is a more detailed d3 zoom example:
http://bl.ocks.org/mbostock/3892919
And here is a JavaFx wrapper for D3 that also contains a working zoom example:
https://github.com/stefaneidelloth/javafx-d3
https://github.com/stefaneidelloth/javafx-d3/blob/master/javafx-d3-demo/src/main/java/com/github/javafxd3/demo/client/democases/behaviors/ZoomDemo.java
I’m new to to d3 and have combined mbostock’s stacked bar graph example ( http://bl.ocks.org/mbostock/3886208 ) with a map to show data (canada.json)
I would like the graph to display an updated chart on the d3.mouseover event of the province:
http://gao8a.github.io/ (something like this)
Unfortunately, I was only able to get the axises to display. It's showing either multiple or the same axis overlapping:
(These will take ~ 3 seconds to load)
Multiple:
http://bl.ocks.org/GAO8A/566e238a72e5ebd1e2c1
Same Axis overlap
http://bl.ocks.org/GAO8A/64f94bb494c4a73f2bf6
I understand I probably need a ‘mouseout’ event to delete the previous but I’m not quite sure how to design that either.
Can anyone point out what I’m doing wrong and how I should be loading the data?
PS:
I was going to make a jsfiddle but can’t seem to get it to get it to work with my hosted canada.json data.
https://raw.githubusercontent.com/GAO8A/GAO8A.github.io/master/canada.json
Thanks
Your issue is that you keep adding the axes in the tooltip element. Unfortunately this creates the overlaps. What you could do is add the axes once, and the readjust their domain with the new values that correspond to the element being hovered.
So in essence if you could add the following lines:
var X_AXIS = tooltip.append("g").attr("class", "x axis x-axis").attr("transform", "translate(0," + height + ")");
X_AXIS.call(xAxis)
X_AXIS.append("text").attr("dy", "3em").attr("dx", "50em").style("text-anchor", "end").text("Month");
var Y_AXIS = tooltip.append("g").attr("class", "y axis y-axis")
Y_AXIS.call(yAxis)
Y_AXIS.append("text").attr("transform", "rotate(-90)").attr("dy", "-3em").attr("dx", "-8em").style("text-anchor", "end").text("Temperature (Celcius)");
just before adding your map, this would in a sense 'initialize' the axes.
So further, in your mouseover handler, you could add the following lines, just after you determine your x and y domains.
X_AXIS.call(xAxis)
Y_AXIS.call(yAxis)
or better still, to add some transition:
X_AXIS.transition().duration(400).call(xAxis)
Y_AXIS.transition().duration(400).call(yAxis)
This way, you don't keep adding axes, you just readjust the ones currently intialized.
Hope this helps.
I'm trying to make a custom chart starting with the Cumulative Line Chart http://nvd3.org/ghpages/cumulativeLine.html
I'd like to display data with origin being top left so I swapped the y range, and now the x axis is on top and I need to translate it to the bottom.
I'm trying to use the common d3js transform attribute
.attr('transform', 'translate(0,' + height+ ')');
as suggested here but it does not seem to be working!
I located this section in the cumulativeLineChart model (around line 350)
g.select('.nv-x.nv-axis')
// .attr('transform', 'translate(0,' + y.range()[0] + ')');
.attr('transform', 'translate(0,' + 300 + ')'); // my testing alteration
d3.transition(g.select('.nv-x.nv-axis'))
.call(xAxis);
Y axis can't be translated either. I think it has something to do with the indexLine and the way the scope can be adjusted, but I may be completely off. In fact, I want to get rid of the interactive indexLine feature altogether...
Am I even modifying the right part of the NVD3 code?!
Any help would be GREATLY appreciated. I've spent hours and hours trying to get the whole thing work (occasionally running back and forth between nvd3 and d3), and I am stuck. Thanks!