I have a series of horizon graphs that have been created like this:
d3.select("body")
.selectAll(".horizon")
.data(metrics)
.enter()
.append("div")
.attr("class", "horizon")
.attr("id", function(d){return d.toString();})
.call(context.horizon().height(['75']));
where metrics is an array of metric objects.
I want to redefine the extent for one of those horizon objects.
I've tried calling the extent function on the one graph, but I'm not sure if I'm calling it, or even selecting it, correctly:
d3.select("#id-attribute")
.call(context.horizon().extent(function(d,i){return([0,1000]);}));
This sort-of seems to work, but the graph display gets screwed up, with additional whitespace being added below the graph and the motion of the graph not accounting for that and leaving the top of the graph unanimated. I suspect that it's in some way due to it being a new instance of the extent object, so I tried this:
d3.select("#id-attribute")
.call(extent(function(d,i){return([0,1000]);}));
but that generates: "ReferenceError: extent is not defined".
I've tried redefining the metric's extent function, effectively:
metrics[3].extent = function(d,i) {return([0,100]);};
but that causes the graph to be blank (although mousing over it reveals numbers in the readout), and causes its readout and the readouts of the graphs that appear below it to be blank when the mouse is not hovering over any of the graphs.
I honestly have no comprehension of how this stuff fits together, nor am I particularly experienced with JavaScript, so I'm not sure where my error lies. I'm totally out of my depth. Any tips would be greatly appreciated.
Do you need to redefine the extent after the initial call where the horizons are generated?
If not, try something like this:
d3.select("body")
.selectAll(".horizon")
.data(metrics)
.enter()
.append("div")
.attr("class", "horizon")
.attr("id", function(d){return d.toString();})
.call(context.horizon()
.height(['75'])
.extent(function(d,i){ //use this function to change extents
if (d.toString() == 'Whatever the name is'){
return [0,1000];
} else {
return [ <default min>, <default max>]
}
})
)
You can either set your own default extent, or use a function like d3.min()/d3.max() to get the value for the horizons. You just need to return an array with two elements.
Related
I'm building a "burnup" d3 line chart attempting to forecast trends based on scope and historic data.
A dropdown box selects the data to be displayed, ideally transitioning between lines, but I'm having troubles clearing the previous data displayed, and instead the new lines are written over the existing lines.
Link to the jsfiddle: https://jsfiddle.net/dgf1vts8/
Currently doing it this way (line 329):
svg.append("path")
.datum(selectedData)
.attr("class", "line")
.attr("d", line);
other approach I've tried and did not work (line 318)
var lines = svg.selectAll(".line").data(selectedData).attr("class","line");
lines.transition().duration(500).attr("d",line);
lines.enter()
.append("path")
.attr("class","line")
.attr("d",line);
lines.exit()
.remove();
Any guidance with this would be much appreciated
Since you only append new object in your update function, every time that function is invoked a new line is added to the chart.
The easiest workaround would be to just remove all path objects before you add the new ones.
svg.selectAll("path").remove();
However, the enter-update-exit logic for this would be
var lines = svg.selectAll(".line").data(selectedGroup);
lines.enter() // enter any new data
.append("path")
.attr("class", "line")
.merge(lines)
.datum(selectedData)
.attr("d", line);
lines.exit() // exit
.remove();
The logic for lines is different as for e. g. points or bars, where you need to create an object for every data point of the time series. For lines you only have to create one path object. This is why the data binding to the selection
svg.selectAll(".line")
should be the label of the line. If you work with multiple lines, it is often best to nest the array.
If you start a new project you should probably use the current version d3v7 and not d3v3, since there have been major changes which break compatibility. I reduced your fiddle and changed all the parts to make it work with d3v7: https://jsfiddle.net/esd4kofr/1/
I have a question about creating paths using d3.
I was trying to study a code as below.
However, to create a path, the code made an argument svg.selectAll then tie the data set. I was curious about that and manipulate the object to an arbitrary thing, and it still works.
I think I roughly understand the concept of enter(), update() and exit().
However, this case, it challenges me a lot.
Why do I have to select dummy object first and make something?
and according to the concept of enter() in D3, once enter is executed, the newly assigned object should be assigned for all the path I made but it wasn't
The dummy argument selectAll('random') did nothing literally.
The svg part I'm questioning is below.
svg.selectAll("random")
.data(allDensity)
.enter()
.append("path")
.attr("transform", function(d){console.log(d3.values(d));return("translate(0," + (yName(d.key)-height) +")" )})
.datum(function(d){return(d.density)})
.attr("fill", "#69b3a2")
.attr("stroke", "#000")
.attr("stroke-width", 1)
.attr("d", d3.line()
.curve(d3.curveBasis)
.x(function(d,i) { return x(d[0]); })
.y(function(d,i) { return y(d[1]); })
)
entire code:https://codepen.io/jotnajoa/pen/dyogmOz
The strong part of d3 is in modifying objects it previously created. If you don't provide a dummy object, d3 would need an explicit check to see whether it is already created or not. By providing a dummy object, such test isn't needed any more. Also, on a complicated page, the dummy object gives the exact position into the html tree to place the d3 object.
Note that d3 is very open minded, and if you really want you can create the element yourself, especially for examples where the whole page is just one d3 object.
I have a project that is about data visualization, however I am encountering problems. I need to render a map of a country (Brazil) using d3.js. When I move the mouse through the state it should appear the acronym of the state with the income per capita. In addition each state must be in a color tint (in the case I chose green) based on per capita income. I am sending my code because I am not getting my map to render the correct colors and are not showing the acronym and the income. If anyone can help, I appreciate it.
Here´s a link of the code
I see a couple problems with the code that builds each country's path -- try updating this section:
svg.append("g")
.selectAll("path")
.data(topojson.feature(br, br.objects.states).features)
.enter().append("path")
.attr("class", "states")
.attr("fill", function(d) { return color(valueByÚF.get(d.UF)); })
.attr("d", path)
.append("title")
.text(function(d) { return "UF: "+ d.UF; });
Honestly, I didn't check to see if the quantize scale is the right one to use for your data, so you may want to try other scales...
I am following the following D3 bullet chart example, trying to modify it a bit so that the different colors of the ranges are also included in the JSON: http://www.d3noob.org/2013/07/introduction-to-bullet-charts-in-d3js.html. The reason for the change is that I need the colors to be dynamic and depend on various things.
This exists one other place in the forum, but old and unsolved. I should add that I am a total newbie to d3, and don't have a lot of JavaScript experience in general.
Here is the JSON I use. "rangecolor" will in the future be an array of different colors, as there are several ranges, but for simplicity I attempt only with one color to begin with.
{
"title":"Memory Used",
"subtitle":"MBytes",
"ranges":[256,512,1024],
"rangecolor": "red",
"measures":[768],
"markers":[900]
}
Now, getting an idea of how to use it, I looked at the working example for title:
var title = svg.append("g")
.style("text-anchor", "end")
.attr("transform", "translate(-6," + height / 2 + ")");
title.append("text")
.attr("class", "title")
.text(function(d) { return d.title; });
The problem is that I cannot get the following to work:
d3.selectAll(".bullet .range.s0")
.style("fill", function(d) { return d.rangecolor; });
The following does work:
d3.selectAll(".bullet .range.s0")
.style("fill", function(d) { return "red"; });
And I can also extract the rangecolor value to the title:
title.append("text")
.attr("class", "title")
.text(function(d) { return d.rangecolor; }); //works - title is now "red"
My approach might be misguided, so any help on how to best include color ranges to the JSON and using it would be much appreciated.
The problem is that when you select all bullets, there is no data bound to them so d is undefined here:
d3.selectAll(".bullet .range.s0")
.style("fill", function(d) { return d.rangecolor; });
Why? You did not perform a data join like this:
d3.selectAll('.something').data(somethingData)
.style('fill', function (d) { // d is defined });
You should wonder why it works for the title on the contrary. This is because when you do this:
var title = svg.append("g");
title inherits data from the svg selection. See Mike Bostock explanation. In fact I was myself not aware of this behaviour, I prefer performing data joins explicitly.
I don't know the overall structure of your code, but you might apply you rangecolor properties with data inheritance (as for title) or refactor to use explicit data joins.
I have talked with some experienced developers that state that the tutorial I am following is not a good one. It is a bit messy, and I am trying to find something cleaner. Troubleshooting has been difficult in this regard, and
The exact issue presented here was solved by using d3.selectAll(".bullet .range.s0").data(data).
Hello guys I am trying to draw a grouped bar chart with d3js
I want through several tutorials and finally tried with this code:
<script>
var data = [[10,20,30,40,50],[20,20,40,50,60]];
var canvas = d3.select("body")
.append("svg")
.attr("width",500)
.attr("height",500);
canvas.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("height",function(d){return d*10;})
.attr("width",50)
.attr("x",function(d,i){return i*60})
.attr("y",function(d,i){return 500-(d*10);})
.attr("fill","red");
</script>
There is some error. How to set data for grouped chart..
To answer your first question, the error you're using is coming from the data structure that you've chosen. The error is happening due to the way that the .data(data).enter() clause works.
The whole clause will implicitly iterate over the elements of the first level in your outter array. That is each element d that is passed to
.attr("height",function(d){...) .attr("x",function(d)...) and .attr("y",function(d)...) is an array. So, the internals of each function are operating with constants on the array, which makes no sense. To clarify in javascript what does d * [1,2,3,4] mean?
To answer your second question. If you want to create a grouped bar chart check out this link: http://bl.ocks.org/mbostock/3887051.