I'm going to need some basic charts for one of my apps, but I would like to use D3JS if I'm able to wrap my head around it in time to fulfill the project requirements. I'm still developing my understanding of SVG and D3JS so I can use it effectively, so far I've been able to make a very basic bar chart that takes 2 dimensional arrays and shows bar charts based on the length of each array in the top level array. This works pretty darn well (though it could use some decoration/axis labels etc.). The next kind of chart I was going to work on is a pie chart since these are very common as well.
Basically what I'm wondering is, does anyone know if someone has already done this and posted to github (or shared the source elsewhere) so I don't have to start from scratch here. I realize D3JS is used for doing very custom data display but I really just want it for the basics plus the ability to customize. So is anyone aware of an effort to create directives for D3JS and/or anyone aware of somewhere that outlines all the basic chart types in D3JS (I keep finding complex examples, which look amazing but I fear I won't understand/learn from them).
Basically I would just like to have an easy way to drop in (and then style) the following charts: bar, line, pie (other standard chart types I'm not thinking of are welcome). Also I have seen the Google Charts and High Charts options, which are both nice and give you a bit of this out of the box, but I prefer a build up approach rather than a strip down most of the time.
Also I'm aware of and used this article to make the original bar graph I needed (mixing it with another straight D3JS tutorial) but are there more efforts anyone is aware of?
Here's my basic bar chart so far:
.directive('barChart', function ( /* dependencies */ ) {
// define constants and helpers used for the directive
var width = 500,
height = 80;
return {
restrict: 'E', // the directive can be invoked only by using <bar-chart></bar-chart> tag in the template
scope: { // attributes bound to the scope of the directive
val: '='
},
link: function (scope, element, attrs) {
// initialization, done once per my-directive tag in template. If my-directive is within an
// ng-repeat-ed template then it will be called every time ngRepeat creates a new copy of the template.
// set up initial svg object
var vis = d3.select(element[0])
.append("svg")
.attr("class", "chart")
.attr("width", width)
.attr("height", height);
// whenever the bound 'exp' expression changes, execute this
scope.$watch('val', function (newVal, oldVal) {
// clear the elements inside of the directive
vis.selectAll('*').remove();
// if 'val' is undefined, exit
if (!newVal) {
return;
}
var totalDataSetSize = 0;
for (var i = 0; i < newVal.length; i++) {
totalDataSetSize += newVal[i].length
};
function calcBarWidth(d) {
return (totalDataSetSize==0)?0:d.length/totalDataSetSize*420;
}
vis.selectAll("rect")
.data(newVal)
.enter().append("rect")
.on("click", function(d,i) {alert("Total gardens: "+ d.length)})
.attr("y", function(d, i) { return i*20; })
.attr("width", calcBarWidth)
.attr("height", function(d) {return 20});
vis.selectAll("text")
.data(newVal)
.enter().append("text")
.attr("x", function(d) { return calcBarWidth(d)+50})
.attr("y", function(d, i) { return (i+1)*20; })
.attr("dx", -3) // padding-right
.attr("dy", "-.3em") // vertical-align: middle
.style("font-size", ".7em")
.attr("fill", "black")
.attr("text-anchor", "end") // text-align: right
.text(function(d,i){ var yesOrNo = i?" No":" Yes"; return d.length.toString() + yesOrNo})
},true);
}
};
});
Any tips/pointers on this code are welcome as well, as I said still a complete novice with D3JS and still fairly new to Angular.
There are 2 github projects about angular directive for d3.js:
https://github.com/robinboehm/angular-d3-directives
https://github.com/cmaurer/angularjs-nvd3-directives
Hmm okay apparently my initial research wasn't that good, I just came upon this which looks like it covers what I want:
http://phloxblog.in/angulard3/start.html#.Ui9XPBKJB-M
Still if there are alternatives I'm open to hearing/seeing them as well. If I don't see any better responses in the next day I'll accept this answer.
-------------------------- Edit 1
Also if anyone knows why prototype is being used here I'd like to know I'm going to try to remove the dependency on it since I'd rather not introduce more code if I don't need it.
-------------------------- Edit 2
Reading up on the topic more there are some other samples which also have a Class setup for the purpose of abstracting/decoupling the D3 code from the AngularJS code (to allow for extension is one argument I've seen).
http://bl.ocks.org/biovisualize/5372077
I've built a set of extensible directives for using D3 to build charts. They are quite flexible and I already use them extensively. You can get the package from Bower as well as "angularD3".
The basic idea is to construct charts declaratively while still allowing access to the more powerful aspects of D3 and providing extensibility for further development of custom directives.
The root directive <d3-chart> acts as a container and controller for the various chart components such as axis, lines, areas, arcs, gradients, etc. The d3ChartController is allows you to write your own custom chart directives easily. We use this ourselves for some specialized custom labels and such.
Here is a sample of how things are declared:
<d3-data src="data/data.csv" data="line" columns="year, savings, total, optimal"></d3-data>
<d3-data src="data/donutData.csv" data="pie" columns="age,population"></d3-data>
<div d3-chart>
<d3-axis data="line" name="year" label="Year" extent="true" orientation="bottom" ticks="5"></d3-axis>
<d3-axis data="line" name="savings" label="Deposits" orientation="left" ticks="5"></d3-axis>
<d3-axis data="line" name="total" label="Savings" orientation="right" ticks="5"></d3-axis>
<d3-line data="line" x="year" y="optimal" yscale="total"></d3-line>
</div>
There's also dangle.js. It's supposed to be for elasticsearch results, but frankly it's robust enough that you can use it for many other use cases.
I would be nice if AngularJS could integrate d3 like they did with bootstrap. There is really nothing new on this topic (angular & d3). Most of the d3 directives (except nvd3) are two years old. Here is another article from 2013 but the idea is more sustainable. You use the newest d3 for the calculations only. And you use native angular for the dom manipulation. The drawback is, that I was not able to get transitions running by this approach.
Related
I want to produce maps as an output for quantitative analysis.
I Have multiple variables to add to my map, and, I dont want to have n-output, in this case, n-maps. You can have a look of the simplified dataframe (colomns are years, but it could also be a set of different variables) :
Simple data frame
It seems to me d3.js was a good idea, because it can display 1 map with a dropdown menu, and the user can select the variable.
I have produced a map with d3.js before, and I use it as template. I have made it quiet basic and simple (really small dataset, etc), you can found it here in this repo GitHub :
https://github.com/Mathiaslauber/Without_interactivity
Here is the Github repo with my first try of implementing interactivity
https://github.com/Mathiaslauber/Interactivity_first_try
Or if you prefer, I have just made a FiddleJS to make things easier : Fiddle JS Interacivity first try
It was part of a bigger project, you can have a look if you want. GithubPages
So far, I have made deseperates modifications that remain unsucessful, because examples are usually very diffrent in many aspects, so hardly reproductible for me. I'm quiet new AND bad with function implemantation in JavaScript.
The two following links are examples that did not seem that complicated and inspired me :
[A] http://tietyk.github.io/D3/Prototype/part9-9.html
[B] http://bl.ocks.org/rgdonohue/6a30b731230f6e242a54
I have interesting bunch of codes at lines 196-226 below, also in the js file.
Problem is I'm missing clues to make it work.
First one used come from source [A] and it's based on an hover effect.
Second, from source [B] and used button
Both refered to json properties, and I use Csv. The overall format and function implementation is different.
I could spend like a month trying to figure this out myself. So if you have any hints, It would help me a lot : Things i should check, that are missing in my code, etc.
// 2 bunch of codes that were used in order to add interactivity
/*
color();
d3.selectAll("input").on("change", function() {
attribute = this.id;
color();
});
function color(){
d3.selectAll('circle')
.transition()
.attr( "fill", function(d){
return (mycolor.domain())}
)
}
*/
/*
var timeline = d3.selectAll(".time-series")
.on("mouseover", function(d, i) {
year = i;
svg.selectAll("path")
.attr("class", quantify);
d3.selectAll(".time-series")
.attr("class", "time-series");
this.className = "time-series active";
});
*/
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).
After making several bar charts using enter, update, exit method in D3js, I wanted to try the same with a pie chart. I thought I applied selections correctly, but the pie chart won't update with the new data in JSON. I looked for similar examples online, but couldn't find one which involved an .on("click" method. I want users to compare the lifespans of humans and animals using a donut chart. I'm trying to implement the search tool through the database of animals right now.
here's what a data object looks like for the query Goat:
[{"Animal":"Male","Life_Span":73},{"Animal":"Goat","Life_Span":10}]
I'm having trouble with this code in particular:
var pie = d3.pie()
.sort(null)
.value(function(d) { return d.Life_Span; });
//code for accessing data, etc
//enter remove selections
var path = svg.selectAll("path")
.data(pie(newdata))
var enterdata =
path.enter().append("path")
.attr("d",arc)
path.exit().remove()
enterdata.exit().remove()
I posted the full code on Plunkr here: http://plnkr.co/edit/3QSAPxQpju63tIXRd9p7?p=preview
A few weeks into learning d3js, I'm still struggling with enter,update exit selections even after reading many tutorials on the subject. I would really appreciate any help. Thanks
In D3 v4.x, you need to merge the update and enter selections:
var enterdata = path.enter()
.append("path")
.merge(path)
.attr("d",arc)
You don't need an exit selection because your data array has always 2 objects: except for the first time, your enter selection is always 0, and your exit selection is always 0.
I took the liberty of colouring the paths in different colours, according to their indices:
.attr("fill", (d,i) => i ? "teal" : "brown");
Here is your updated plunker: http://plnkr.co/edit/l6OzmxVfid9Cj7iv7IMg?p=preview
PS: You have some problems in your code, which I'll not change (here I'm only answering your main question), but I reckon you should think about them:
Don't mix jQuery and D3. You can do everything here without jQuery
Don't load all your CSV every time the user chooses an animal. It doesn't make sense. Instead of that, put the click function inside the d3.csv function (that way, you load the CSV only once).
I'm new to StackOverflow and I just started using D3.
I need to show the values on a map. I saw this question that is very similar to what I should do.
What I'd like is to color the countries based on the values in the column Date to a CSV and based on selected year by user (radio button).
How can I do that?
I created a gray color scale and have included them in an array, then I created a method chooseColor(value) that returns the correct color based on the value of the country in that year.
I think it is not the most efficient method to do this thing...
Also in my CSV there are not all the countries present in the European Union. For example, I have no data on Russia so I "turned off" some countries putting an if inside the event on mouseover.
But I would also cut part of Russia in the map in order to enlarge the known countries. How can I do also that thing?
I looked at these examples: Choropleth and Threshold Choropleth by Mike Bostock on bl.ocks.org but I have not understand how to color the countries...
(I wanted to put links but I can't post more than 2 links because of my low reputation)
This is my code.
I apologize for my bad English. Thank you all,
Pier
EDIT
I admit I did not understand some things in your code.
Why I need events on mouseover and mouseout? And what are hover and rhover? I thought they were events related to this question. But in my case I don't need it, no?
Use array_values or d is the same, right? Does not change if I use d or array_values, right? It is a stupid question but it confused me.
I modified the makemap method in this way. I understand correctly how to use your code?
function makemap(error, europe, dessease) {
dess = dessease.slice();
counties = topojson.feature(europe, europe.objects.collection);
vector = svg.selectAll("path")
.data(counties.features)
.enter()
.append("path")
.attr("class", "county")
.attr("id", function(d) {
return "coun" + d.properties.indx;
})
.attr("d", path)
.style("fill", function(array_values) {
return color(array_values[d.country]);
});
In this case there is an error concerning d, of course. Sorry, I do not know where I'm wrong...
The country's color will depend on a value. So the color IS a function of "value". To do that you must to define a range of color based on your values:
var color = d3.scale.linear()
.domain([mn,mx]) // <--- min and MAX of your value
.range(["#ffffff","000000"]);
then define the color of your country:
svg.selectAll(".county")
.style("fill", function(array_values) {
return color(array_values[d.country]);
});
Must-Read: Jerome Cukier - d3: scales, and color
I've been attempting to learn better visualization with node.js and the mapbox library.
Using this example here: Running Map Example
I'd like to add a graph of speed, and allow a user to click on a node, and see data about that position in a little popup - For today, I just want to get speed working.
It seems to be a recursive algorithm, so I need to implement variables to store the previous position and time, but I've ran into three problems:
I don't know how to use this date format: "2015-01-19T21:24:20Z" or Chroniton's parsing of it to generate a subtractable number to get the difference in time.
I don't know how to get the distance between two points using the code given, I could simply do sqrt((.x(point1) - .x(point2)) + (.y(point1) + .y(point2)), but I'm not sure how coordinates are stored or parsed in this example.
I don't know where to calculate the speed. It seems like the coordinates are only defined after the graphs are displayed, since the coordinates aren't used in the graphics. I am probably wrong, but I need some direction.
Here is what I have now:
Using the elevation display as my template, I think I have made it able to display the line by adding in the following three snippets:
Setting the scale:
var speed = d3.scale.linear()
.range([height, 0])
.domain([0, d3.max(dataRet, function(d) {
return d[1][2];
})]);
Adding in the line, with data:
var SpeedLine = d3.svg.line()
.x(function (d) { return x(d[0]); })
.y(function (d) { return speed(d[2])})
Displaying the line:
svg.append('path')
.datum(dataRet)
.attr('class', 'speed-line')
.attr('d', speedLine);
I know I have to add in a speed function similar to this psudocode:
var dt = chroniton.domain(Time1, Time2)
var speed[i] = LongLat(previousPoint).distanceto(currentPoint)/dt
And on the popup box:
dt.format(something to do with time formatting)
Note 1:, I changed the name of the function datePlaceHeart to dataRet since I'll be adding new things to do it, and datePlaceHeartSpeedStuffAndThings was getting a bit long ;)
Note 2: I haven't been able to start the pop-up because I haven't figured out how to calculate speed using the given data, and well, it seems kinda silly to do the easy one first. (With my luck, its actually not easy)
Please help? Here is my edited code in full (Edited index.js):
Code