Using D3js to make a line that beyond some point turns dashed - d3.js

I construct the "var cities" in such a way that beyond "some_date" the "at" turns from "0" to "1". Then I try to use this information to draw a line that beyond "some_date" turns dashed. Could please someone tell me what is wrong with the code?
var cities = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
if(d.date < some_date){
return {date: d.date, temperature: +d[name], vis:"0", at:"0"};
}else{
return {date: d.date, temperature: +d[name], vis:"0", at:"1"};
};
})
};
});
city.append("path")
.attr("class", "line")
.attr("d", function(d) {if(d.name.match(/pib_1t/)){
d.vis = "1";
return line(d.values);
}})
.style("stroke", function(d) { return color(d.name); })
.style("stroke-dasharray", function(d){
console.log(d.name);
d.values.forEach(function(e, i) {
if(e.at == "1"){
return ("2,2");
}else{
return ("2,0");
};
});
});
Well, I tried to use a "custom line generator" but I got an ugly result. So I solved the problem constructing two lines. The first one goes until "some_date" is reached and is a normal line. The second is a dashed line and it starts at "some_date". See the code bellow.
// first line
city.append("path")
.attr("class", "line")
.attr("d", function(d) {if(d.name.match(/pib_1t/)){
d.vis = "1";
return line(d.values.slice(0,[bisect(data,some_date)]));
}})
.style("stroke", function(d) { return color(d.name); });
// second line
city.append("path")
.attr("class", "line")
.style("stroke-dasharray", ("2, 2"))
.attr("d", function(d) {if(d.name.match(/pib_1t/)){
d.vis = "1";
return line(d.values.slice([bisect(data, some_date)-1],data.length));
}})
.style("stroke", function(d) { return color(d.name); });

Related

Line Highlights but dots dont

Im trying to add a 'highlight' effect that is shown in this link(https://www.d3-graph-gallery.com/graph/parallel_custom.html) My lines are made up of two things, first is the line itself, and the second are dots that appear on each line. When my mouse hovers on the red line, the line stays the same but the red dots disapear for some reason. I want the red dots along with the red line to stay the same when i hover over it. What am i doing wrong here? the picture below shows what happens when i hover over the red line, and as you can see the red dots disappear
// Highlight individual line and dots
var highlight = function(d) {
var selected_line = d.key
// first every group turns grey
svgObj.selectAll(".line")
.transition().duration(200)
.style("stroke", "lightgrey")
.style("opacity", "0.2")
svgObj.selectAll(".dot")
.transition().duration(200)
.style("stroke", "lightgrey")
.style("opacity", "0.2")
// Second the hovered line takes its color
svgObj.selectAll("." + selected_line)
.transition().duration(200)
.style("stroke", color(selected_line))
.style("opacity", "1")
svgObj.selectAll("." + selected_line)
.transition().duration(200)
.style("stroke", color(selected_line))
.style("opacity", "1")
}
// UnHighlight
var doNotHighlight = function(d) {
svgObj.selectAll(".line")
.transition().duration(200).delay(50)
.style("stroke", function(d){ return( color(d.key))})
.style("opacity", "1")
}
// Draw the line
svgObj.selectAll(".line")
.data(sumstat)
.enter()
.append("path")
.attr("class", function (d) { return "line " + d.key} ) // 2 class for each line: 'line' and the group name
.attr("fill", "none")
.attr("stroke", function(d){ return color(d.key) })
.attr("stroke-width", 4.5)
.attr("d", function(d){
return d3.line()
.curve(d3.curveMonotoneX)
.x(function(d) { return x(d.year); })
.y(function(d) { return y(+d.n); })
(d.values)
})
//.on("mouseover", function(){return tooltip.style("visibility", "visible");})
//.on("mousemove", function(){return tooltip.style("top", (d3.event.pageY-10)+"px").style("left",(d3.event.pageX+10)+"px");})
//.on("mouseout", function(){return tooltip.style("visibility", "hidden");})
.on("mouseover", highlight)
.on("mouseleave", doNotHighlight )
// Draw dots on points
svgObj.selectAll(".dot")
.data(data)
.enter()
.append("circle")
.attr("class", function (d) { return "dot " + d.key } ) // 2 class for each line: 'line' and the group name
.style("fill","white")
.style("stroke-width", "3px")
.style("stroke", function (d) { return color(d.name) })
.attr("cx", function(d) {return x(d.year); })
.attr("cy", function(d) {return y(d.n); })
.attr("r", 5.5)
It is because the data of .line and .dot is different, one is sumstat with key property, the other one is data without key. so the code
.append("circle")
.attr("class", function (d) { return "dot " + d.key } )
will return "dot undefined" for each circle's class.
And then in highlight() the selected dots will not be included.
I adjusted your code into a demo, but you might change the bind data of .line and .dot consistently, to avoid such mistake after.

Rotate the text placed in map d3.js

Using this I was able to add names to the map: Add names of the states to a map in d3.js
Now, how do I rotate those text as it is overlapping with each other.
For those who are looking for the solution.
Adding this solved it. It produces composition matrix.
.attr('transform', function(d) {
return 'translate('+path.centroid(d)[0]+','+path.centroid(d)[1]+') rotate(-45);
})
Here is the updated code:
function draw(){
d3.json("readme.json", function(json) {
g.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr("d", path)
.on("click", click);
g.selectAll("text")
.data(json.features)
.enter()
.append("svg:text")
.text(function(d){
return d.properties.name;
})
// .attr("x", function(d){
// return path.centroid(d)[0];
// })
// .attr("y", function(d){
// return path.centroid(d)[1];
// })
.attr("text-anchor","middle")
.attr('font-size','6pt')
// added
.attr('transform', function(d) {
return 'translate('+path.centroid(d)[0]+','+path.centroid(d)[1]+') rotate(-45);
})
});
}

Adding data label in D3 Line chart (for Power BI)

Whole source code can be found here.
Requirement: I need to add Data Labels to the Line chart (aka above to the circles)
Attempt: Added
selectionCircle.append("text")
.attr(function (d) { return d.x; })
.attr(function (d) { return d.y; })
Below the line
var selectionCircle = this.sMainGroupElement2.selectAll("circle").data(dataPoints, function (d) { return d.dataId; });
Tried adding my code at various places in the original code but not working.
Try adding the following code below the existing "selectionCircle.exit().remove();" line:
this.sMainGroupElement2.selectAll("text").remove();
var selectionLabel = this.sMainGroupElement2.selectAll("circleLabel").data(dataPoints, function (d) { return d.dataId; });
selectionLabel.enter()
.append("text")
.classed(".circleLabel", true)
.text(function(d) { return d.ActualOrg; })
.attr("x", function (d) { return d.x; })
.attr("y", function (d) { return d.y-sH*0.05; })
.attr("fill", "#bebebe")
.attr("style", "font-family:calibri;font-size:" + sL * 0.04 + "px")
.attr("text-anchor", "middle")
selectionLabel.exit().remove();
It not best-pratice to remove (they should be updated instead) all elements like the first line does, but it will get you started.
I'm not really knowledgeable with Power BI, but in d3.js you would just append the text nodes in the enter statement, just like you appended the circle.
So it would look something like this:
selectionCircle.enter()
.append("circle")
.classed(".circle112", true)
.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; })
.attr("r", sH * 0.02)
.attr("fill", statusColor)
.attr("stroke", "white")
.attr("stroke-width", sH * 0.015)
.append("text")
.text('some label text can also be a function') //Change this
.attr(function (d) { return d.x; })
.attr(function (d) { return d.y; });
The .enter() method of d3 iterates through the data and is used to add new elements depending on the data.
Hope this helps.

Dynamically adjust bubble radius of counties?

Following the County Bubbles example, it's easy to add a bubble for each county. This is how it is added in the example:
svg.append("g")
.attr("class", "bubble")
.selectAll("circle")
.data(topojson.feature(us, us.objects.counties).features
.sort(function(a, b) { return b.properties.population - a.properties.population; }))
.enter().append("circle")
.attr("transform", function(d) { return "translate(" + path.centroid(d) + ")"; })
.attr("r", function(d) { return radius(d.properties.population); })
.append("title")
.text(function(d) {
return d.properties.name
+ "\nPopulation " + formatNumber(d.properties.population);
});
However, rather than using a variable from the json file (population), I need to update the radii according to a variable which dynamically changes (so I cannot put it in the json file beforehand as was done in the example). I call updateRadii() when a county is clicked, which needs access to the FIPS.
var currFIPS,
flowByFIPS;
var g = svg.append("g");
queue()
.defer(d3.json, "us.json")
.defer(d3.csv, "census.csv", function(d) {
return {
work: +d.workplace,
home: +d.residence,
flow: +d.flow
}
})
.await(ready);
function ready(error, us, commute) {
// Counties
g.append("g")
.attr("class", "counties")
.selectAll("path")
.data(topojson.feature(us, us.objects.counties).features)
.enter().append("path")
.attr("d", path)
.on("click", function(d) {
// Get FIPS of selected county
currFIPS = d.id;
// Filter on selected county (i.e., grab
// people who work in the selected county)
var data = commute.filter(function(d) {
return d.work == currFIPS;
});
// Create d3.map for where these people live
flowByFIPS = d3.map(data, function(d) {
return d.home;
});
// Update radii at "home" counties to reflect flow
updateRadii();
});
// Bubbles
g.append("g")
.attr("class", "counties")
.selectAll("circle")
.data(topojson.feature(us, us.objects.counties).features)
.enter().append("circle")
.attr("id", function(d) { return d.id; })
.attr("transform", function(d) {
return "translate(" + path.centroid(d) + ")";
})
.attr("r", 0); // invisible before a county is clicked
}
function updateRadii() {
svg.selectAll(".counties circle")
.transition()
.duration(300)
.attr("r", function(d) {
return flowByFIPS.get(d.id).flow
});
}
According to the error code, I believe that the circles do not have an id (FIPS code) attached. How do I get them to have an id? (I tried nesting the circle with the path using .each as explained in this answer, but could not get it working.)
Note that the above code works for updating fill on paths (rather than circles). For example, sub updateRadii(); for updateFill(); with the function as:
function updateFill() {
svg.selectAll(".counties path")
.transition()
.duration(300)
.attr("fill", function(d) {
return flowByFIPS.get(d.id).color; // e.g., "#444"
});
}
The problem here is that you don't supply d3 with data in the update function. I will recommend you update the data loaded from the file on the clicks, and from there you update the svg.
var update = function() {
g.selectAll(".country")
.data(data)
.attr("r", function(d) { return d.properties.flow_pct });
};
var data = topojson.feature(us, us.objects.counties).features;
data.forEach(function(x) { x.properties.flow_pct = /* calc the value */; })
g.append("g")
.attr("class", "counties")
.selectAll(".country")
.data(data)
.enter()
.append("circle")
.attr("class", "country")
.attr("transform", function(d) {
return "translate(" + path.centroid(d) + ")";
})
.on("click", function(d) {
// more code
data.forEach(function(x) { x.properties.flow_pct = /* calc the NEW value */; })
update();
});
update();
I've tried to use as much as the same code as before, but still trying to straiten it a bit out. The flow is now more d3-like, since the update function works on the changed data.
Another plus which this approach is both first render and future updates use the same logic for finding the radius.
It turns out to be an obvious solution. I forgot to check for the cases where a flow doesn't exist. The code above works if updateRadii() is changed to:
function updateRadii() {
svg.selectAll(".counties circle")
.transition()
.duration(300)
.attr("r", function(d) {
if (currFIPS == d.id) {
return 0;
}
var county = flowByFIPS.get(d.id);
if (typeof county === "undefined") {
return 0;
} else {
return county.flow;
}
});
}

Create points on a D3 multiline graph

I'm trying to add points to a line graph using d3 in this example:
http://bl.ocks.org/mbostock/3884955
I was also trying to follow this post
How do you get the points to look like this picture from the documentation?
http://github.com/mbostock/d3/wiki/line.png
The stroke of the circle should match the line color.
var color = d3.scale.category10();
d3.csv("data.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date"; }));
data.forEach(function(d) {
d.date = parseDate(d.date);
});
var ranks = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, ranking: +d[name]};
})
};
});
var rank = svg.selectAll(".rank")
.data(ranks)
.enter().append("g")
.attr("class", "rank");
rank.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.name); });
var point = rank.append("g")
.attr("class", "line-point");
point.selectAll('circle')
.data(function(d){ return d.values})
.enter().append('circle')
.attr("cx", function(d) { return x(d.date) })
.attr("cy", function(d) { return y(d.ranking) })
.attr("r", 3.5)
.style("fill", "white")
.style("stroke", function(d) { return color(d.name); });
.style("stroke", function(d) { return color(this.parentNode.__data__.name); })
See JSBin code
Found answer here

Resources