Problems adding interactivity in d3.js Maps [dropdown menu] - javascript

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";
});
*/

Related

d3 pie chart problems with exiting entering

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).

Coloring a map using D3.js

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

problems with latest cluster force layout example

Based on this work: http://bl.ocks.org/mbostock/7882658
If I substitute the automatic nodes creation by a JSON.stringify() output of the automatically generated data like this...
var nodes = [
{"cluster":2,"radius":1.6180680659922448},
{"cluster":0,"radius":3.3575295077569},
{"cluster":1,"radius":0.9569281165554346},
{"cluster":3,"radius":10.7245554165012}
];
...I get an exception "cannot read property x of undefined" on the line:
var x = d.x - cluster.x,
This is inside the cluster(alpha) function. So, apparently the d3.map function that automatically generates the data is putting something in the structure that the JSON stringification has not caught? Maybe I am just overlooking something simple...help is appreciated. Thanks! Here is a fiddle to help out: http://jsfiddle.net/Nivaldo/FJ3qq/1/
I commented out the code that is not working. Also, another detail, it does not seem like the original code as i left it (except that i reduced the count of clusters and nodes) is actually handling the right number of distinct clusters. It should paint 4 different ones but is only painting with 3 colors.
The problem is that nodes is not the only data structure that needs to be initialised -- clusters needs to be as well. In particular, specific nodes are assigned to specific cluster indices. If you don't do that, things will break.
To fix, do something like
nodes.forEach(function(d) { clusters[d.cluster] = d; });
Complete jsfiddle here.

Do AngularJS Directives for D3JS exist?

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.

d3.js Update circle on line chart

I'm posting there because I couldn't find something on other post or on google.
http://jsfiddle.net/CUQaN/9/
As you can see on the JS fiddle, I have a line chart with circle on each point.
I want to update this chart with new data. The problem is that I can receive less or more point than I already have on the graph. For example, I can have 8 point on my line chart and then when I'm updating the chart, I can have just 4, or even 15 point. And my circle are not updating properly because I'm just changing the value of the circle which already exist.
But I really don't know how to update them properly.
I can have that data sometimes :
var data = [
{"date":"4-May-12","close":Math.random()*568.13,"open":Math.random()*35.12},
{"date":"3-May-12","close":Math.random()*568.13,"open":Math.random()*35.12},
{"date":"2-May-12","close":Math.random()*568.13,"open":Math.random()*35.12},
{"date":"1-May-12","close":Math.random()*568.13,"open":Math.random()*35.12},
{"date":"30-Apr-12","close":Math.random()*354.98,"open":Math.random()*424.56},
{"date":"27-Apr-12","close":Math.random()*24.00,"open":Math.random()*253.89},
{"date":"26-Apr-12","close":Math.random()*490.70,"open":Math.random()*215.54},
{"date":"25-Apr-12","close":Math.random()*42.00,"open":Math.random()*351.23},
{"date":"24-Apr-12","close":Math.random()*210.28,"open":Math.random()*20.23},
{"date":"23-Apr-12","close":Math.random()*20.70,"open":Math.random()*368.34},
{"date":"20-Apr-12","close":Math.random()*412.98,"open":Math.random()*42},
{"date":"19-Apr-12","close":Math.random()*26.44,"open":Math.random()*20.56},
{"date":"18-Apr-12","close":Math.random()*48.34,"open":Math.random()*356.45},
{"date":"17-Apr-12","close":Math.random()*26.44,"open":Math.random()*20.56},
{"date":"15-Apr-12","close":Math.random()*48.34,"open":Math.random()*356.45},
];
And just that data other times : (more or less)
var data = [
{"date":"4-May-12","close":Math.random()*568.13,"open":Math.random()*35.12},
{"date":"3-May-12","close":Math.random()*568.13,"open":Math.random()*35.12},
{"date":"2-May-12","close":Math.random()*568.13,"open":Math.random()*35.12},
{"date":"1-May-12","close":Math.random()*568.13,"open":Math.random()*35.12},
{"date":"30-Apr-12","close":Math.random()*354.98,"open":Math.random()*424.56},
];
Is someone can help me please ?
Thanks a lot !
It might be better to remove the old points and add the new ones rather than trying to move them. To do this you can use an id function to make the points unique - see here
A small example:
svg.selectAll("circle")
.data(myData, function(d) { return d.x; })
.enter()
.append("circle");
The important part here is that points are identified by their x-value, rather than their index in the data point array. Infact this might help in your current situation as the points will only move up/down and not side to side.

Categories