Trying to get two circles to transition simultaneously - javascript
Trying to get two circles (one red, one blue) to move to center of screen from opposite directions. Can only get the second circle to do it - unsure as to why.
I tried everything from switching up the order of functions being called to switching variable names
var svg = d3.select("body")
.append("svg")
.attr("width", 600)
.attr("height", 200);
var circles = svg.append('circle')
.attr('cx',50).attr('cy',50).attr('r',10).style('fill','red');
var circlesTwo = svg.append('circle')
.attr('cx',50).attr('cy',50).attr('r',10).style('fill','blue');
animation();
animationTwo();
function animation() {
svg.transition()
.duration(750)
.tween("precision", function() {
var area = d3.interpolateRound(0, 300);
return function(t) {
minArea = area(t);
render();
};
})
}
function animationTwo() {
svg.transition()
.duration(750)
.tween("precision", function() {
var area = d3.interpolateRound(600, 300);
return function(t) {
minArea = area(t);
renderTwo();
};
})
}
function render() {
circles.attr('cx',minArea);
}
function renderTwo() {
circlesTwo.attr('cx',minArea);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
Expected results are two circles coming to their respective positions (from off screen originally).
Actual results are I am only getting my blue circle to work.
Applying the transitions to the SVG selection is not a very idiomatic D3: you should apply them to the elements that are moving (i.e., the circles). That, by the way, is the cause of the problem you're facing: one transition is cancelling the other.
This happens because since your transitions have no name, null is used (link):
selection.transition([name]) <>
Returns a new transition on the given selection with the specified name. If a name is not specified, null is used.
Then, because all of them have the same name (null), the last one cancels the first one:
The starting transition also cancels any pending transitions of the same name on the same element that were created before the starting transition.
Therefore, to apply multiple transitions to the same element, you have to name them:
function animation() {
svg.transition("foo")
//etc...
}
function animationTwo() {
svg.transition("bar")
//etc...
}
Here is your code with that change:
var svg = d3.select("body")
.append("svg")
.attr("width", 600)
.attr("height", 300);
var circles = svg.append('circle')
.attr('cx', 50).attr('cy', 50).attr('r', 10).style('fill', 'red');
var circlesTwo = svg.append('circle')
.attr('cx', 50).attr('cy', 50).attr('r', 10).style('fill', 'blue');
animation();
animationTwo();
function animation() {
svg.transition("foo")
.duration(750)
.tween("precision", function() {
var area = d3.interpolateRound(0, 300);
return function(t) {
minArea = area(t);
render();
};
})
}
function animationTwo() {
svg.transition("bar")
.duration(750)
.tween("precision", function() {
var area = d3.interpolateRound(600, 300);
return function(t) {
minArea = area(t);
renderTwo();
};
})
}
function render() {
circles.attr('cx', minArea);
}
function renderTwo() {
circlesTwo.attr('cx', minArea);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
Again, have in mind that I'm simply answering your question here: my advice is that you should refactor that transition code completely.
Related
d3-attrTween with custom function.(What did I misunderstand about tween function?)
I have a question about attrTween (sometimes tween()). I understood custom tween function as after " attrTween('d' " argument, I define the custom function. So, I wrote the custom function as below. d3.selectAll('circle#circles1') .transition() .attrTween('d',function(){ let interpolator=d3.interpolateArray(sdata.vader,sdata1.vader); return function(t){ return d3.select(this).attr('cy',interpolator(t)) } }) What I intended is For All the circles I drew, makes a transition. The transition is attrTween. The changes is based on data array tied into the circles. Original data array is sdata and the cy value in the sdata is sdata.vader. And the transition is heading toward sdata1.and cy value for sdata1 is sdata1.vader. To access all the cy value for every single circle, I used d3.select(this).attr('cy') However, no error message is shown but no animation was made either. What did I misunderstand for the custom tween function? Can anyone help me to fix this code? Thank you inadvance. Full code is in the following link. https://codepen.io/jotnajoa/pen/WNQeEBE
There are multiple problems in the example code, which is not minimal. Providing a minimal, reproducible example would really help solve the problems. usage of HTML Id to multiple elements. In HTML, and id attribute must be unique. Here, ids are assigned to groups of circles. A class attribute should be used for this purpose, not an id. .attr('id','circles1') should be: .attr('class','circles1') Accordingly, the attrTween should lookup the circles with class circle1, rather than the unique circle with id #circle1 d3.selectAll('circle#circles1') should be d3.selectAll('.circles1') Id (or class) assigned in the wrong place. The circles1 class is assigned before the creation of the circle, hence the instructions applies to an empty selection. The class attribute should be set right after circles have been created. .attr('id','circles1') .enter() .append('circle') should be .enter() .append('circle') .attr('class','circles1') Wrong attribute tweened The attribute to transition is the circle's cy attribute, not a path's d attribute. Hence .attrTween('d',function(){ should be .attrTween('cy',function(){ Wrong data interpolated sdata.vader and sdata1.vader do not exist, sdata and sdata1 seem to be arrays of objects, which in turn do have a vader property. You probably want d.vader, and the corresponding .vader in sdata1, which would be sdata1[i].vader, in case items are the same order in both arrays. Interpolating original measures instead of coordinates. cy is originally defined as: height-yscale(d.vader) In the interpolator function, the scale function should also be used. The attrTween function calls becomes: .attrTween('cy',function(d, i){ //console.log( i, height-yscale(d.vader), height-yscale(sdata1[i].vader)) let interpolator=d3.interpolateArray(height-yscale(d.vader), height-yscale(sdata1[i].vader)); return function(t) { return interpolator(t)} }) Using attrTween where not needed. Simply transitioning the circles with attr is sufficient for this use case, there is no need to define an interpolator. d3 will move the position of circles from the original position to the destination, interpolating implicitly. d3.selectAll('.circles1') .transition() .duration(2000) .attr('cy',function(d, i){ return height-yscale(sdata1[i].vader) }) I added a long duration for demo purpose, to make obvious that the circles move to the correct location. Once in their final position, they disappear, because they are under the pink circles. P.S. Same set of corrections is applicable to circles2 set whenever relevant. Demo of the solution in the snippet below, as codepen does not allow to save modifications without creating an account. var svg; var xscale; var yscale; var sdata; var xAxis; var yAxis; var width=1500; var height=500; var margin=50; var duration =250; var vader ='vader' var textblob='textblob' var delay =5000; var tbtrue=false; var areas var circles1,circles2; var sdata1,sdata2 d3.csv('https://raw.githubusercontent.com/jotnajoa/Javascript/master/tweetdata.csv').then(function(data){ svg=d3.select('body').append('svg').attr('width',width).attr('height',height) var parser = d3.timeParse("%m/%d/%y") // data를 처리했고, date parser 하는 법 다시한번 명심하자. sdata = data; sdata.forEach(function(d){ d.vader = +d.vader; d.textblob= + d.textblob; d.date=parser(d.date) }) // scale을 정해야 함. 나중에 brushable한 범위로 고쳐야함. nice()안하면 정렬도안되고, 첫번째 엔트리 미싱이고 // 난리도 아님. xscale=d3.scaleTime() .domain(d3.extent(sdata, function(d) {return d.date })) .range([0,width*9/10]) .nice() yscale =d3.scaleLinear() .domain(d3.extent([-1,1])) .range([height*4/5,height*1/5]) .nice() //yaxis는 필요 없을 것 같은데. //캔버스에 축을 그려야 함 단, translate해서 중간에 걸치게 해야함. svg.append('g').attr('class','xaxis') .call(d3.axisBottom(xscale)) .attr('transform','translate('+margin+','+height*1/2+')') //sdata plotting var circles = svg.append('g').attr('class','circles') var area = svg.append('g').attr('class','pathline') firststage(); //generator로 데이터를 하나씩 떨어뜨리도록 한다. function firststage(){ function* vaderdropping(data){ for( let i=0;i<data.length;i++){ if( i%50==0) yield svg.node(); let cx = margin+xscale(data[i].date) let cy = height-yscale(data[i].vader) circles.append('circle') .attr('cx',cx) .attr('cy',0) .transition() .duration(duration) .ease(d3.easeBounce) .attr('cy',cy) .attr('r',3) .style('fill','rgba(230, 99, 99, 0.528)') } yield svg.node() } //generator 돌리는 부분 let vadergen = vaderdropping(sdata); let result = vadergen.next() let interval = setInterval(function(){ if(!result.done) { vadergen.next(); } else { clearInterval(interval) } }, 100); setTimeout(secondstage,5000) } function secondstage(){ function* textblobdropping(data){ for( let i=0;i<data.length;i++){ if( i%50==0) yield svg.node(); let cx = margin+xscale(data[i].date) let cy = height-yscale(data[i].textblob) circles.append('circle') .attr('cx',cx) .attr('cy',0) .transition() .duration(duration) .ease(d3.easeBounce) .attr('cy',cy) .attr('r',3) .style('fill','rgba(112, 99, 230, 0.528)') } yield svg.node() } //generator 돌리는 부분 let textblobgen = textblobdropping(sdata); let tresult = textblobgen.next() let tinterval = setInterval(function(){ if(!tresult.done) { textblobgen.next(); } else { clearInterval(tinterval) } }, 100); setTimeout(thirdstage,2500) } function thirdstage(){ //진동을 만들기 위해서, //베이다와 텍스트 블랍 값을 플립한거다 (제발 워크 아웃하길...) //그 다음 트윈으로 sdata 와 sdata1을 왔다갔다 하게하면 되지않을까? sdata1 = sdata.map(function(x){ var y={}; y['date']=x.date; y['vader']=x.textblob; y['textblob']=x.vader; return y}); sdata2 = sdata.map(function(x){ var y={}; y['date']=x.date; y['vader']=0; y['textblob']=0; return y}); d3.selectAll('circle').transition() .duration(3500) .style('fill','rgba(1, 1, 1, 0.228)') //areas는 일종의 함수다, 에리아에다가 데이터를 먹이면, //에리아를 그리는 역할을 하는것임. areas = d3.area() .x(function(d){return margin+xscale(d.date)}) .y0(function(d){return height-yscale(d.vader)}) .y1(function(d){return height-yscale(d.textblob)}) .curve(d3.curveCardinal) //이렇게 하지말고, sdata2도 만들었으니까 2->1->0 반복하는 // 무한반복 on('end','repeat') loop를 만들어보자. var uarea=area.append('path') setTimeout(repeat,500) function repeat(){ uarea .style('fill','rgba(112, 99, 230, 0.4)') .attr('d', areas(sdata)) .transition() .duration(500) .attrTween('d',function(){ var interpolator=d3.interpolateArray(sdata,sdata1); return function(t){ return areas(interpolator(t)) } }) .transition() .duration(500) .attrTween('d',function(){ var interpolator=d3.interpolateArray(sdata1,sdata2); return function(t){ return areas(interpolator(t)) } }) .transition() .duration(500) .attrTween('d',function(){ var interpolator=d3.interpolateArray(sdata2,sdata); return function(t){ return areas(interpolator(t)) } }) .on('end',repeat) } setTimeout(fourthstage,500) } function fourthstage(){ // console.log(d3.selectAll('circle#circles1').node()) circles1=svg.append('g').selectAll('circle').data(sdata) .enter().append('circle').attr('class','circles1') .attr('cx',function(d){return margin+xscale(d.date)}) .attr('cy',function(d){return height-yscale(d.vader)}) .style('fill','green') .attr('r',3) circles2=svg.append('g').selectAll('circle').data(sdata) .enter().append('circle').attr('class','circles2') .attr('cx',function(d){return margin+xscale(d.date)}) .attr('cy',function(d){return height-yscale(d.textblob)}) .style('fill','pink') .attr('r',3) d3.selectAll('.circles1') .transition() .duration(5000) .attr('cy',function(d, i){ return height-yscale(sdata1[i].vader) }) // d3.selectAll('circle#circles2') // .transition() // .attr('cy',function(d){return 0}) //tween 팩토리를 정의해야한다. //주의사항, 리턴을 갖는 함수여야한다는 것. //왜 꼭 return function(){}을 해야하나? /* function movey(d2){ let y1 = this.attr('cy') let y2 = d2.vader let interpolate=d3.interpolate(y1,y2); interpolate; } 하면 안되나?? */ } }) <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
D3.js Problem rendering barchart with object data
I have the following script for rendering a simple barchart in D3.js. I have been able to render charts ok up until a point. I have this data below which I am struggling to insert into my chart, there is no specific key I can call upon and I'm really confused how I would insert all of these into my chart. Object { "food-environmental-science": 0, "art-media-research": 0, .....} I have a seperate file for the HTML (only a snippet): var barchart1 = barchart("#otherchart"); function clickScatter(d){ var unitOfAssessment = d.UoAString; click = d.environment.topicWeights renderTopicWeights(click) } function renderTopicWeights(clickedPoint){ barchart1.loadAndRenderDataset(clickedPoint) } When I call upon the loadAndRenderDataset function, console gives me a data.map is not a function error. function barchart(targetDOMelement) { //=================== PUBLIC FUNCTIONS ========================= // barchartObject.appendedMouseOverFunction = function (callbackFunction) { console.log("appendedMouseOverFunction called", callbackFunction) appendedMouseOverFunction = callbackFunction; render(); return barchartObject; } barchartObject.appendedMouseOutFunction = function (callbackFunction) { appendedMouseOutFunction = callbackFunction; render(); return barchartObject; } barchartObject.loadAndRenderDataset = function (data) { dataset=data.map(d=>d); //create local copy of references so that we can sort etc. render(); return barchartObject; } barchartObject.overrideDataFieldFunction = function (dataFieldFunction) { dataField = dataFieldFunction; return barchartObject; } barchartObject.overrideKeyFunction = function (keyFunction) { //The key function is used to obtain keys for GUP rendering and //to provide the categories for the y-axis //These valuse should be unique GUPkeyField = yAxisCategoryFunction = keyFunction; return barchartObject; } barchartObject.overrideMouseOverFunction = function (callbackFunction) { mouseOverFunction = callbackFunction; render(); return barchartObject; } barchartObject.overrideMouseOutFunction = function (callbackFunction) { mouseOutFunction = callbackFunction; render(); //Needed to update DOM return barchartObject; } barchartObject.overrideTooltipFunction = function (toolTipFunction) { tooltip = toolTipFunction; return barchartObject; } barchartObject.overrideMouseClickFunction = function (fn) { mouseClick2Function = fn; render(); //Needed to update DOM if they exist return barchartObject; } barchartObject.render = function (callbackFunction) { render(); //Needed to update DOM return barchartObject; } barchartObject.setTransform = function (t) { //Set the transform on the svg svg.attr("transform", t) return barchartObject; } barchartObject.yAxisIndent = function (indent) { yAxisIndent=indent; return barchartObject; } //=================== PRIVATE VARIABLES ==================================== //Width and height of svg canvas var svgWidth = 900; var svgHeight = 450; var dataset = []; var xScale = d3.scaleLinear(); var yScale = d3.scaleBand(); //This is an ordinal (categorical) scale var yAxisIndent = 400; //Space for labels var maxValueOfDataset; //For manual setting of bar length scaling (only used if .maxValueOfDataset() public method called) //=================== INITIALISATION CODE ==================================== //Declare and append SVG element var svg = d3 .select(targetDOMelement) .append("svg") .attr("width", svgWidth) .attr("height", svgHeight) .classed("barchart",true); //Declare and add group for y axis var yAxis = svg .append("g") .classed("yAxis", true); //Declare and add group for x axis var xAxis = svg .append("g") .classed("xAxis", true); //===================== ACCESSOR FUNCTIONS ========================================= var dataField = function(d){return d.datafield} //The length of the bars var tooltip = function(d){return d.key + ": "+ d.datafield} //tooltip text for bars var yAxisCategoryFunction = function(d){return d.key} //Categories for y-axis var GUPkeyField = yAxisCategoryFunction; //For 'keyed' GUP rendering (set to y-axis category) //=================== OTHER PRIVATE FUNCTIONS ==================================== var maxValueOfDataField = function(){ //Find the maximum value of the data field for the x scaling function using a handy d3 max() method //This will be used to set (normally used ) return d3.max(dataset, dataField) }; var appendedMouseOutFunction = function(){}; var appendedMouseOverFunction = function(){}; var mouseOverFunction = function (d,i){ d3.select(this).classed("highlight", true).classed("noHighlight", false); appendedMouseOverFunction(d,i); } var mouseOutFunction = function (d,i){ d3.select(this).classed("highlight", false).classed("noHighlight", true); appendedMouseOutFunction(d,i); } var mouseClick2Function = function (d,i){ console.log("barchart click function = nothing at the moment, d=",d) }; function render () { updateScalesAndRenderAxes(); GUP_bars(); } function updateScalesAndRenderAxes(){ //Set scales to reflect any change in svgWidth, svgHeight or the dataset size or max value xScale .domain([0, maxValueOfDataField()]) .range([0, svgWidth-(yAxisIndent+10)]); yScale .domain(dataset.map(yAxisCategoryFunction)) //Load y-axis categories into yScale .rangeRound([25, svgHeight-40]) .padding([.1]); //Now render the y-axis using the new yScale var yAxisGenerator = d3.axisLeft(yScale); svg.select(".yAxis") .transition().duration(1000).delay(1000) .attr("transform", "translate(" + yAxisIndent + ",0)") .call(yAxisGenerator); //Now render the x-axis using the new xScale var xAxisGenerator = d3.axisTop(xScale); svg.select(".xAxis") .transition().duration(1000).delay(1000) .attr("transform", "translate(" + yAxisIndent + ",20)") .call(xAxisGenerator); }; function GUP_bars(){ //GUP = General Update Pattern to render bars //GUP: BIND DATA to DOM placeholders var selection = svg .selectAll(".bars") .data(dataset, GUPkeyField); //GUP: ENTER SELECTION var enterSel = selection //Create DOM rectangles, positioned # x=yAxisIndent .enter() .append("rect") .attr("x", yAxisIndent) enterSel //Add CSS classes .attr("class", d=>("key--"+GUPkeyField(d))) .classed("bars enterSelection", true) .classed("highlight", d=>d.highlight) enterSel //Size the bars .transition() .duration(1000) .delay(2000) .attr("width", function(d) {return xScale(dataField(d));}) .attr("y", function(d, i) {return yScale(yAxisCategoryFunction(d));}) .attr("height", function(){return yScale.bandwidth()}); enterSel //Add tooltip .append("title") .text(tooltip) //GUP UPDATE (anything that is already on the page) var updateSel = selection //update CSS classes .classed("noHighlight updateSelection", true) .classed("highlight enterSelection exitSelection", false) .classed("highlight", d=>d.highlight) updateSel //update bars .transition() .duration(1000) .delay(1000) .attr("width", function(d) {return xScale(dataField(d));}) .attr("y", function(d, i) {return yScale(yAxisCategoryFunction(d));}) .attr("height", function(){return yScale.bandwidth()}); updateSel //update tool tip .select("title") //Note that we already created a <title></title> in the Enter selection .text(tooltip) //GUP: Merged Enter & Update selections (so we don't write these twice) var mergedSel = enterSel.merge(selection) .on("mouseover", mouseOverFunction) .on("mouseout", mouseOutFunction) .on("click", mouseClick2Function) //GUP EXIT selection var exitSel = selection.exit() .classed("highlight updateSelection enterSelection", false) .classed("exitSelection", true) .transition() .duration(1000) .attr("width",0) .remove() }; return barchartObject;' } Any help would be much appreciated, and I appolguise if what I'm asking is not clear. Thanks
format your input data into object like {key:"",value:""} and pass this into d3 so that they can understand and render chart. var input = {'a':1,"b":2}; function parseData(input){ return Object.keys(input).reduce(function(output, key){ output.push({"key":key,"value":input[key]}); return output; },[]) } console.log(parseData(input)); // [{"key":"a","value":1},{"key":"b","value":2}] jsFiddle demo - https://jsfiddle.net/1kdsoyg2/1/
d3js v5 + Topojson v3 Show/Hide element on click legend
I would like this reverse action http://bl.ocks.org/d3noob/5d621a60e2d1d02086bf. When I click on an item of my legend, I want focus all elements of the map within these class. However, how isolate just one class compared to others? For example in this image, just show elements where the value is included between 23500 and 29000 and hide other elements. I suggest to 1/ filter data and 2/ fill with the same color these elements but surely it's easier. Here is my code : https://plnkr.co/edit/ga82Syjc8zxTdAxTVNXu?p=preview <!DOCTYPE html> <head> <meta charset="utf-8"> <script src="https://unpkg.com/d3#5.0.0/dist/d3.min.js"></script> <script src="https://unpkg.com/topojson#3.0.0/dist/topojson.min.js"></script> </head> <svg class="carte_evolution"> <style> </style> <body> <script> let w = 600; let h = 600; let view = [0,0,600,600]; //Dimension let svgCarteEvolution = d3.select(".carte_evolution") .attr("width", "100%") .attr("height", h) .attr("preserveAspectRatio","xMidYMid meet") .attr("viewBox", `${view[0]},${view[1]},${view[2]},${view[3]}`); //Map projection choro = d3.map(); projection = d3.geoConicConformal() .center([1.282743, 46.328377]) .scale(2600) .translate([w / 2, h / 2]); path = d3.geoPath() .projection(projection); //Load csv and topojson promises1 = d3.json("ze_WGS84_UTF8.topojson"); promises2 = d3.csv("data.csv"); Promise.all([promises1, promises2]).then(function(fr){ //Join csv + topojson let featureCollection = topojson.feature(fr[0],fr[0].objects.ze_WGS84_UTF8) for (var i=0; i< fr[1].length;i++){ var csvId = fr[1][i].codgeo; var csvValue0 = parseFloat(fr[1][i].value0); var csvYear0 = fr[1][i].year0; for (var j=0; j<featureCollection.features.length;j++){ var jsonId = featureCollection.features[j].properties.codgeo; if (csvId === jsonId) { featureCollection.features[j].properties.value0 = csvValue0; featureCollection.features[j].properties.year0 = csvYear0; break; } } } let color = d3.scaleQuantile() .range(["#d1e0c9","#b3cea8","#8fb983","#539f53","#008d36"]); color.domain([ 12000,17500,19500,21500,23500,29000 ]); //Map svgCarteEvolution.append("g") .selectAll("path") .data(featureCollection.features) .enter() .append("path") .attr("class", "dep") .attr("d", path) .style("fill", function(d){ var value = d.properties["value0"]; return value? color(value):"#ccc"; }); //legend let legend = svgCarteEvolution.selectAll("g.legend_entree") .data(color.range().reverse()) .enter() .append("g") .attr("class","legend_entree"); legend .append('rect') .attr("x", 30) .attr("y", function(d, i) { return 65 + i * 20; }) .attr("width", 20) .attr("height", 10) .style("stroke", "black") .style("stroke-width", 0.1) .style("fill", function(d){return d;}); //the data objects are the fill colors legend .append('text') .attr("x", 55) //leave 5 pixel space after the <rect> .attr("y", function(d, i) { return 65 + i * 20; }) .attr("dy", "0.8em") //place text one line *below* the x,y point .style("font-family","latomedium") .style("font-size","0.9em") .text(function(d,i) { var extent = color.invertExtent(d); //extent will be a two-element array, format it however you want: var format = d3.format(""); return `${format(+extent[0])} - ${format(+extent[1])}` }); }); </script> </body> </html> data.csv "codgeo","year0","value0","year1","value1" "0050",2012,19162,2014,19698.8 "0051",2012,18501.125,2014,19125.5 "0052",2012,18684.6666666667,2014,19454 "0053",2012,19826,2014,20573.9 "0054",2012,18881.1111111111,2014,19513 "0055",2012,17942.5,2014,18657.3 "0056",2012,19299.0476190476,2014,19703.9 "0057",2012,18873.8095238095,2014,19539.3 "0059",2012,18199,2014,18719 "0060",2012,18921,2014,19563.2 "0061",2012,21259.5238095238,2014,21748.3 "0101",2012,,2014, "0102",2012,,2014, "0103",2012,,2014, "0106",2012,,2014, "0201",2012,,2014,17250 "0202",2012,,2014,17246.7 "0203",2012,,2014,14972 "0204",2012,,2014,14612.6 "0205",2012,,2014,15617.3 "0206",2012,,2014,13369 "0301",2012,,2014, "0302",2012,,2014, "0303",2012,,2014, "0401",2012,,2014,12522.3 "0402",2012,,2014,15483.8 "0403",2012,,2014,14344.6 "0404",2012,,2014,13134 "0601",2012,,2014, "1101",2012,23144.7619047619,2014,23464 "1102",2012,22223.5,2014,22729.1 "1103",2012,20637.2222222222,2014,21088 "1104",2012,20402,2014,20901 "1105",2012,21782.7777777778,2014,22274.4 "1106",2012,18304.375,2014,18918 "1107",2012,20038.6666666667,2014,20406.5 "1108",2012,19493,2014,19936.5 "1109",2012,28156.8901098901,2014,28660 "1110",2012,20294.8,2014,20639.5 "1111",2012,22490.6666666667,2014,22812.4 "1112",2012,25996.6666666667,2014,26503.5 "1113",2012,25301.7391304348,2014,25639.6 "1114",2012,21184,2014,21611.9 "1115",2012,20586,2014,20954 "1116",2012,25449.5,2014,25742.4 "1117",2012,22279.3333333333,2014,22665 "1118",2012,19040,2014,19307.2 "1119",2012,22059.0909090909,2014,22519 "2101",2012,17416,2014,18133.9 "2102",2012,18721,2014,19275.4 "2103",2012,19554.7826086957,2014,20204 "2104",2012,20352.8571428571,2014,20892.8 "2105",2012,19630.5555555556,2014,20157.6 "2106",2012,18689,2014,19445.2 "2107",2012,17676.6666666667,2014,18309.1 "2201",2012,19209.5238095238,2014,19791 "2202",2012,17090,2014,17820 "2203",2012,16258.6666666667,2014,17014 "2204",2012,17987.619047619,2014,18675.2 "2205",2012,17320.8333333333,2014,18056 "2206",2012,18163.2,2014,18851 "2207",2012,19732.1428571429,2014,20284.7 "2208",2012,19973.3333333333,2014,20690 "2209",2012,17625,2014,18402 "2210",2012,18778.0952380952,2014,19447.6 "2211",2012,17420,2014,18158.8 "2301",2012,18978.8461538462,2014,19609.3 "2302",2012,19920,2014,20419 "2303",2012,18947,2014,19704.7 "2304",2012,20011.7391304348,2014,20639 "2305",2012,18633.4166666667,2014,19348.6 "2306",2012,19222.6666666667,2014,19937.6 "2307",2012,19716,2014,20349.3 "2401",2012,19496,2014,20116.7 "2402",2012,17587.3913043478,2014,18264.3 "2403",2012,18039.3333333333,2014,18668.9 "2404",2012,20862,2014,21386.8 "2405",2012,19088,2014,19617.5 "2406",2012,19682,2014,20246 "2407",2012,17765,2014,18244 "2408",2012,18813.3333333333,2014,19590.5 "2409",2012,18460.8695652174,2014,19134 "2410",2012,19341.3333333333,2014,20286.2 "2411",2012,18503.5,2014,19170 "2412",2012,20074.7619047619,2014,20669 "2413",2012,19886,2014,20426.5 "2414",2012,18549,2014,19111.7 "2415",2012,19298.0952380952,2014,19929 "2416",2012,19159.5833333333,2014,19763.3 "2417",2012,19013.3333333333,2014,19521.9 "2418",2012,20852,2014,21314 "2419",2012,19696.8,2014,20135.4 "2501",2012,18832.9166666667,2014,19616.7 "2502",2012,19839.6428571429,2014,20447.3 "2503",2012,19357,2014,19949.3 "2504",2012,17890,2014,18668.5 "2505",2012,17836.1904761905,2014,18619.5 "2506",2012,18373.0434782609,2014,19247.3 "2507",2012,18855,2014,19788.7 "2508",2012,18192.0833333333,2014,19076 "2509",2012,19105,2014,19896.6 "2510",2012,18837.2222222222,2014,19621.4 "2511",2012,18071.5,2014,18835 "2512",2012,18059.5,2014,18796.7 "2513",2012,17989.5652173913,2014,18533.5 "2601",2012,20356,2014,21106.7 "2602",2012,18218.4,2014,18926.7 "2603",2012,20719.6666666667,2014,21264.4 "2604",2012,18828.3333333333,2014,19445.7 "2605",2012,17496,2014,18232 "2606",2012,18618.3333333333,2014,19419.2 "2607",2012,18150,2014,18717 "2608",2012,19290,2014,20024 "2609",2012,18376,2014,19020 "2610",2012,18123.5,2014,18997.2 "2611",2012,18232,2014,19093.1 "2612",2012,19023.3962264151,2014,19581.7 "2613",2012,18402.2222222222,2014,19055 "2614",2012,19352.6666666667,2014,19970 "3110",2012,17632,2014,18171.3 "3111",2012,19872.3214285714,2014,20498.6 "3112",2012,17909.5,2014,18786 "3113",2012,19370.8695652174,2014,20029 "3114",2012,17411.6666666667,2014,18153.1 "3115",2012,16527.5,2014,17161.7 "3116",2012,17494,2014,18124.5 "3117",2012,16522.5,2014,17252.5 "3121",2012,18958.5714285714,2014,19749.5 "3122",2012,15848,2014,16471.3 "3123",2012,17325,2014,18070 "3124",2012,17319,2014,18080.7 "3125",2012,16943,2014,17776.5 "3126",2012,17054,2014,17783.8 "3127",2012,17860.6666666667,2014,18527.8 "4101",2012,19057,2014,20396.5 "4102",2012,18167.3913043478,2014,18923.8 "4103",2012,20100,2014,20734.3 "4104",2012,19253.4615384615,2014,19914.7 "4105",2012,18178,2014,18899.6 "4106",2012,18415.5555555556,2014,19180 "4107",2012,19765.3333333333,2014,20611.4 "4108",2012,17936.3043478261,2014,18670.5 "4109",2012,19220,2014,19884 "4110",2012,19699,2014,20456 "4111",2012,19975.5555555556,2014,21490 "4112",2012,18561.9047619048,2014,19239.6 "4113",2012,18533,2014,19207 "4114",2012,17932,2014,18625 "4115",2012,18513.6,2014,19304 "4201",2012,21662,2014,22259.4 "4202",2012,22121.4285714286,2014,22678 "4203",2012,20694.9743589744,2014,21249.2 "4204",2012,20996.6666666667,2014,21554 "4205",2012,20637.2,2014,20939.5 "4206",2012,22284.4,2014,22998 "4207",2012,21402,2014,21892 "4208",2012,20530.4761904762,2014,20987.7 "4209",2012,27116,2014,27578 "4301",2012,19866.7857142857,2014,20474.7 "4302",2012,19037.6923076923,2014,19688.7 "4303",2012,26342.380952381,2014,26900.4 "4304",2012,23223.3333333333,2014,24219 "4305",2012,19166.4516129032,2014,19840 "4306",2012,19092.2222222222,2014,19879.5 "4307",2012,20652,2014,21500.8 "4308",2012,18128,2014,18743 "4309",2012,18185,2014,18844.7 "5201",2012,18881.4814814815,2014,19667.6 "5202",2012,17665.6,2014,18286 "5203",2012,20616,2014,21241 "5204",2012,19844.8,2014,20508 "5205",2012,19443,2014,20094.4 "5206",2012,18905.7142857143,2014,19605 "5207",2012,17964.6666666667,2014,18666 "5208",2012,18107.3333333333,2014,18734.4 "5209",2012,18951.7857142857,2014,19703.8 "5210",2012,18635.7142857143,2014,19332 "5211",2012,18973.2258064516,2014,19556.5 "5212",2012,18178.6666666667,2014,18824 "5213",2012,19304.6666666667,2014,19966.7 "5214",2012,18363.3333333333,2014,19098.9 "5215",2012,19146.6666666667,2014,19786.7 "5216",2012,18103.3333333333,2014,18876.7 "5217",2012,18984,2014,19686 "5218",2012,19186.1904761905,2014,19813.8 "5219",2012,20042.380952381,2014,20712.9 "5301",2012,19939.2,2014,20655.3 "5302",2012,18221.4285714286,2014,18960 "5303",2012,20016,2014,20691.3 "5304",2012,18641.5,2014,19282.2 "5305",2012,19581.3333333333,2014,20232.9 "5306",2012,19916.0869565217,2014,20649.4 "5307",2012,17941.3333333333,2014,18671 "5308",2012,19373.3333333333,2014,19991.9 "5309",2012,19634.347826087,2014,20377.6 "5310",2012,18645.3846153846,2014,19283.8 "5311",2012,18720,2014,19397.2 "5312",2012,20578,2014,21143.8 "5313",2012,19380.6666666667,2014,20159.3 "5314",2012,19439,2014,20248.1 "5315",2012,19573,2014,20304.3 "5316",2012,18751.25,2014,19417.2 "5317",2012,18940,2014,19701.3 "5318",2012,20075.4166666667,2014,20740 "5401",2012,17907.5,2014,18522.9 "5402",2012,18704.6666666667,2014,19349 "5403",2012,18580.3846153846,2014,19280 "5404",2012,17407,2014,18222 "5405",2012,19327.5,2014,20055.3 "5406",2012,18512.4,2014,19128 "5407",2012,19217.6,2014,19903.5 "5408",2012,18673,2014,19379.3 "5409",2012,20270,2014,20855.6 "5410",2012,19489.1666666667,2014,20238.1 "5411",2012,17890,2014,18620.5 "5412",2012,18078.0952380952,2014,18834 "5413",2012,19446.5,2014,20043 "7201",2012,18008.4637681159,2014,18697.3 "7202",2012,18425,2014,19159 "7203",2012,18106.6666666667,2014,18658.7 "7204",2012,20667,2014,21249.5 "7205",2012,18035.3333333333,2014,18711.3 "7206",2012,18450.9523809524,2014,19101.3 "7207",2012,20320.8695652174,2014,20948 "7208",2012,19536,2014,20100 "7209",2012,18869.3333333333,2014,19480.7 "7210",2012,17737.5,2014,18362 "7211",2012,17410,2014,17987.6 "7212",2012,20164.6666666667,2014,20683.9 "7213",2012,19245,2014,19834 "7214",2012,20302,2014,20867.6 "7301",2012,18256,2014,18876.3 "7302",2012,17957,2014,18514 "7303",2012,18034.6666666667,2014,18724.8 "7304",2012,19017.2222222222,2014,19799.3 "7305",2012,17916.1904761905,2014,18698 "7306",2012,18578,2014,19336.7 "7307",2012,18857.619047619,2014,19230 "7308",2012,18793.3333333333,2014,19457.6 "7309",2012,19058.6956521739,2014,19787.6 "7310",2012,18895.3333333333,2014,19429.5 "7311",2012,18878.1780538302,2014,19430.7 "7312",2012,18183.8461538462,2014,18879.2 "7313",2012,18197.3333333333,2014,18632 "7401",2012,19040,2014,19716 "7402",2012,18143.3333333333,2014,18897.2 "7403",2012,17301.3043478261,2014,17984 "7404",2012,19118,2014,19698 "8201",2012,20643.8095238095,2014,21227.1 "8202",2012,20174.6666666667,2014,20682.4 "8203",2012,18474.4444444444,2014,19119.6 "8204",2012,18964.6153846154,2014,19694 "8205",2012,17833.8461538462,2014,18486 "8206",2012,18796,2014,19433 "8207",2012,18750.4347826087,2014,19414.5 "8208",2012,19158.8,2014,19833.1 "8209",2012,20058.9655172414,2014,20741.1 "8210",2012,21119.1153846154,2014,21684.5 "8211",2012,19926,2014,20499.3 "8212",2012,18585.7142857143,2014,19272 "8213",2012,21155,2014,21768 "8214",2012,20965,2014,21470 "8215",2012,20356.9230769231,2014,20849.2 "8216",2012,21009.6,2014,21576.5 "8217",2012,20380.7692307692,2014,20996 "8218",2012,23005.2173913043,2014,23712.7 "8219",2012,27201.9230769231,2014,27749.3 "8220",2012,21017,2014,21626.5 "8221",2012,21245.0909090909,2014,21706.7 "8222",2012,23036.0869565217,2014,23862.9 "8301",2012,18453.9393939394,2014,19023.3 "8302",2012,18534.3333333333,2014,19190 "8303",2012,18492,2014,19091.3 "8304",2012,18855.7142857143,2014,19606.7 "8305",2012,17307.5595238095,2014,18159 "8306",2012,17459.1666666667,2014,18457 "8307",2012,17890.4761904762,2014,18484.8 "8308",2012,18963,2014,19466.1 "8309",2012,18347.1428571429,2014,19084 "8310",2012,20412,2014,21071.7 "8311",2012,19008.5,2014,19795.7 "8312",2012,18111.6666666667,2014,18856.5 "9101",2012,17408.5714285714,2014,17945.3 "9102",2012,16617.3333333333,2014,17173 "9103",2012,17123.3333333333,2014,17661 "9104",2012,16840,2014,17349.6 "9105",2012,18586,2014,19386.7 "9106",2012,18106.6666666667,2014,18625 "9107",2012,17128.4615384615,2014,17763 "9108",2012,16940.8695652174,2014,17503.5 "9109",2012,17352,2014,17867.5 "9110",2012,16858,2014,17391.3 "9111",2012,19566.6666666667,2014,20024.4 "9112",2012,17690.4,2014,18320.7 "9113",2012,18336.5217391304,2014,19110 "9114",2012,18100.4545454545,2014,18640.7 "9115",2012,17447.2222222222,2014,17903.9 "9116",2012,17108,2014,17654.6 "9301",2012,18502,2014,19034.2 "9302",2012,18934.8,2014,19401.5 "9303",2012,18952.3076923077,2014,19365.3 "9304",2012,18794.7619047619,2014,19335 "9305",2012,20466.1111111111,2014,20830.8 "9306",2012,20292.8,2014,20668.7 "9307",2012,19959,2014,20333.3 "9308",2012,21603.3333333333,2014,22211.1 "9309",2012,18084,2014,18679.5 "9310",2012,19009,2014,19507 "9311",2012,19514.6666666667,2014,20090 "9312",2012,19813.6,2014,20490.6 "9313",2012,18840,2014,19254.8 "9314",2012,19946.0357142857,2014,20238 "9315",2012,19492,2014,20070.5 "9316",2012,17739.1304347826,2014,18245.7 "9317",2012,17904,2014,18510.6 "9401",2012,19633.8461538462,2014,20418.3 "9402",2012,17570,2014,17890 "9403",2012,17200.8333333333,2014,17640.5 "9404",2012,17777.2666666667,2014,18376.4 "9405",2012,17254.5,2014,17473.5 "9406",2012,17553.3333333333,2014,18426 "9407",2012,16885,2014,17577.7
Your instincts will work - filter the path data according to the color: features.filter(function(feature) { return color(feature.properties["value0"]) == d; }) I saved your feature paths in the variable features in the plunkrs below As the datum of each legend entry is a color, we can just filter the paths based on which datum would produce the same color when scaled. We could alternatively give class names or apply other indicators to filter. Color the filtered paths on the event, revert all paths on some sort anti-event. Since you haven't said what will reset the map, I'll assume if you click on the same legend entry twice in a row the map should reset (this lets you toggle between each legend entry). To do so we'll need to keep track of which legend entry is currently in focus: var highlighted = ""; Then we just listen for a click event on the legend entry and: .on("click",function(d,i) { highlighted = ""; //reset // revert map back to default state } else { highlighted = d; // filter features to highlight certain ones. }) Altogether that might look like: .on("click",function(d,i) { // clicking an active entry: reset: if(highlighted == d) { // reset opacity on each path: features.style("opacity",1); // reset legend entries' fills: legend.selectAll("rect") .style("fill",function(d) { return d; }); // reset highlight variable since nothing is highlighted highlighted = ""; } // clicking a different entry: highlight that entry: else { //update highlighted variable highlighted = d; // set opacity low for all features, filter for chosen features and style accordingly: features.style("opacity",0.2) .filter(function(f) { return color(f.properties["value0"]) == d; }) .style("opacity",1); // hollow legend entries legend.selectAll("rect") .style("fill","white") // fill selected option: d3.select(this).select("rect") .style("fill",function(d) { return d;}); } }) Which looks something like: (I'm formatting the legend entries to show what is highlighted - I changed your stroke to accomodate this) Here's an updated plunkr. If you want to be able to toggle multiple legend items at once, then the logic gets a bit more detailed and the highlighted variable becomes an array As an alternative with mouseover/off events rather than clicks (so no need to track what is actively shown), here's a slightly different approach using the same principles: plunkr
Restart d3 simulation on user input from range slider
I am building a "spring" using the d3-force layout. I want to manipulate it's properties like "strength" and "distance" via user input. For that I am currently using an "input range slider". For better understanding I set up a working draft on codepen where this question is related to: http://codepen.io/bitHugger/pen/XNqGNE?editors=1010 The HTML: <input id="strengthElem" step="0.1" type="range" min="0" max="2"/> I wanted to do the event handling something along like this: let strengthElem = window.document.getElementById('strengthElem'); let strength; strengthElem.addEventListener('click', function(evt) { strength = strengthElem.value; console.log('strength', strength); }, false); Now I would like to restart or recalculate the d3.simulation object when some interaction happens with the range slider. This is my current simulation: let simulation = d3.forceSimulation().nodes(nodes) .force("link", d3.forceLink() .id(function(d) { return d.index; }) .strength(function(d) { return 2; }) .distance(function(d) { return 2; })) .force("charge", d3.forceManyBody()); For the strength and the distance the values are currently hard coded.I would like to change it to e.g.: .strength(function(d) { return strength; }) .distance(function(d) { return distance; }) I tried to setup a d3.call().on() function but could not get it working. I wonder how I can manipulate the simulation based on unser input, that happens outside of the force() function / outside of the svg container. Sadly I can't get something working and I don't know how to setup a proper d3 event listener that reacts on the input button and then recalculates the force layout based on the changed values. Any ideas?
Instead of creating a link force in-place without keeping a reference to the force, you should first create the force and just pass the reference to the simulation. That way, you are later on able to manipulate the force according to your sliders' values: // Create as before, but keep a reference for later manipulations. let linkForce = d3.forceLink() .id(function(d) { return d.index; }) .strength(2) .distance(2); let simulation = d3.forceSimulation().nodes(nodes) .force("link", linkForce) .force("charge", d3.forceManyBody()); When registering the event handlers on the sliders you may also want to use d3.select() for ease of use, and assign the functions using selection.on(). d3.select('#strengthElem') .on('click', function() { // Set the slider's value. This will re-initialize the force's strenghts. linkForce.strength(this.value); simulation.alpha(0.5).restart(); // Re-heat the simulation }, false); d3.select('#distanceElem') .on('click', function(evt) { // Set the slider's value. This will re-initialize the force's strenghts linkForce.distance(this.value); simulation.alpha(0.5).restart(); // Re-heat the simulation }, false); Within the handler functions this points to the actual DOM element, whereby allowing to easily access the slider's value. The link force's parameters may now be updated using the previously kept reference. All that is left to do, is to re-heat the simulation to continue its calculations. Have a look at this snippet for a working demo: 'use strict'; var route = [[30, 30],[192, 172],[194, 170],[197, 167],[199, 164],[199, 161],[199, 157],[199, 154],[199, 150],[199, 147],[199, 143],[199, 140],[200, 137],[202, 134],[204, 132],[207, 129],[207, 126],[200, 200]]; let distance = 1; let createNode = function(id, coords) { return { radius: 4, x: coords[0], y: coords[1], }; }; let getNodes = (route) => { let d = []; let i = 0; route.forEach(function(coord) { if(i === 0 || i === route.length-1) { d.push(createNode(i, coord)); d[i].fx = coord[0]; d[i].fy = coord[1]; } else { d.push(createNode(i, coord)); } ++i; }); return d; }; let getLinks = (nodes) => { let next = 1; let prev = 0; let obj = []; while(next < nodes.length) { obj.push({source: prev, target: next, value: 1}); prev = next; ++next; } return obj; }; let force = function(route) { let width = 900; let height = 700; let nodes = getNodes(route); let links = getLinks(nodes); d3.select('#strengthElem') .on('click', function() { linkForce.strength(this.value); // Set the slider's value. This will re-initialize the force's strenghts simulation.alpha(0.5).restart(); // Re-heat the simulation }, false); d3.select('#distanceElem') .on('click', function(evt) { linkForce.distance(this.value); // Set the slider's value. This will re-initialize the force's strenghts simulation.alpha(0.5).restart(); // Re-heat the simulation }, false); let linkForce = d3.forceLink() .id(function(d) { return d.index; }) .strength(2) .distance(2); let simulation = d3.forceSimulation().nodes(nodes) .force("link", linkForce) .force("charge", d3.forceManyBody()); let svg = d3.select('svg').append('svg') .attr('width', width) .attr('height', height); let link = svg.append("g") .attr('class', 'link') .selectAll('.link') .data(links) .enter().append('line') .attr("stroke-width", 1); let node = svg.append("g") .attr("class", "nodes") .selectAll("circle") .data(nodes) .enter().append("circle") .attr("r", function(d) { return d.radius; }) .attr("fill", function(d) { return '#fabfab'; }); simulation.nodes(nodes).on("tick", ticked); simulation.force("link").links(links); function ticked() { link .attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); node .attr("cx", function(d) { return d.x; }) .attr("cy", function(d) { return d.y; }); } }; force(route); .link { stroke: #777; stroke-width: 2px; } .links line { stroke: #999; stroke-opacity: 0.6; } .nodes circle { stroke: #fff; stroke-width: 1.5px; } <script src="https://d3js.org/d3.v4.js"></script> <div>Strength <input id="strengthElem" step="0.1" type="range" min="0" max="2"/></div> <div>Distance <input id="distanceElem" step="1" type="range" min="0" max="50"/></div> <svg style="width: 900; height: 700;"></svg> I have also updated the codepen accordingly.
One way to do is is removing the contents of the svg and re-drawing it with your desired constants. I didn't understand where you got stuck because I only changed few lines like you said in your question. Inside your click handlers I cleared the contents of the svg and called the "draw" function: strengthElem.addEventListener('click', function(evt) { strength = strengthElem.value; console.log('strength', strength); d3.select('svg').selectAll("*").remove(); force(route); }, false); Moved your config variables to a global scope: var distance = 1; let distElem = window.document.getElementById('distanceElem'); let strengthElem = window.document.getElementById('strengthElem'); var strength = strengthElem.value; distance = distElem.value; And just like you've said I've changed to return parameters: .strength(function(d) { return strength; }) .distance(function(d) { return distance; })) Full example: http://codepen.io/anon/pen/ObZYLo?editors=1010
Dashed line doesn't work
I have a list of radio buttons indicating a country. I want that when user clicks on a radio button, the right line chart are loaded. To do so I have 4 csv files with data for each country. The csv files are like this: year,death,value 2012,Tuberculosis,NA 2011,Tuberculosis,NA 2010,Tuberculosis,301 2009,Tuberculosis,344 2008,Tuberculosis,333 2007,Tuberculosis,329 2006,Tuberculosis,350 2005,Tuberculosis,NA 2004,Tuberculosis,NA 2003,Tuberculosis,396 2002,Tuberculosis,413 2001,Tuberculosis,415 2000,Tuberculosis,460 1999,Tuberculosis,517 1998,Tuberculosis,558 1997,Tuberculosis,597 1996,Tuberculosis,609 1995,Tuberculosis,647 2012,Tetanus,NA 2011,Tetanus,NA 2010,Tetanus,17 2009,Tetanus,27 2008,Tetanus,18 ... HERE is my code. I don't know why Plunker create the error "Bad request". Anyway, this is my result: if the user clicks on Italy, then the browser loads Italy.csv file and creates the chart, if the user clicks on Hungary, then browser loads Hungary.csv file and creates the chart and so on. The problem is that in my csv files, there are some "holes" on data. For example, I haven't any data about number of deaths in Italy between 2003 and 2006 so I want to show a dashed line instead of a solid line. In my chart I'm not able to do that. In particular, I wrote code to do that but it doesn't work. Note that the missing data will change according to the countries. For some countries I have all the data and not for other countries. Also, if the user clicks on the circle (the legend) of the corresponding series disappears, the circle should turn white and the axes change to fit the current data. This doesn't work and I don't understand why. As you can see, the console doesn't show any errors. In this similar example, it works: PLUNKER I badly explained myself. With "missing data" I mean NA values. NEW PLUNKER I understand that the chart of Belgium is right. I tried to understand why the Denmark chart is that but sorry, I don't understand when you say "Code generate multiples paths for every abailable data and just ONE path for all NA data. Instead it must generate a segments for every gap on data.". The code is organized in this way: // some code... /************************************************************************** *** GET SELECTED COUNTRY AND DRAW FIRST CHART **************************** **************************************************************************/ var l_selectedCountry = document.querySelector('input[name="l_country"]:checked').value; l_createLineChart(l_selectedCountry); var l_updateradio = function() { l_selectedCountry = $('input[name=l_country]:checked', '#l_countries').val(); l_createLineChart(l_selectedCountry); } $("#l_countries").on("change", l_updateradio); /************************************************************************** *** DRAW THE RIGHT CHART BASED ON SELECTED COUNTRY ********************** **************************************************************************/ function l_createLineChart(l_selectedCountry) { // remove last chart d3.select("#l_chartcontainer") .html(""); // adds the svg canvas var svg = d3.select("#l_chartcontainer") .append("svg") .attr("width", l_width + margin.left + margin.right) .attr("height", l_height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var l_file = "./data/line_data/" + l_selectedCountry + ".csv"; /************************************************************************** *** GET RIGHT DATA ******************************************************** **************************************************************************/ d3.csv(l_file, function(l_error, l_data) { l_data.forEach(function(d) { d.year = parseDate(d.year); d.value = +d.value; }); // code about axis... /************************************************************************** *** GET ALL LINE DATA (solid & dashed) ************************************ **************************************************************************/ var l_dataNest = d3.nest() .key(function(d) { return d.death; }) .entries(l_data); console.log("l_dataNest"); console.log(l_dataNest); // code about labels... /************************************************************************** *** GET DASHED LINE DATA ************************************************** **************************************************************************/ var l_dashedData = l_getDashed(l_dataNest); // dashed line (extreme point) for each disease console.log("l_dashedData"); console.log(l_dashedData); // other code... /************************************************************************** *** DRAW SOLID LINE ******************************************************* **************************************************************************/ svg.append('g') .attr('class', 'l_line-container') .selectAll('.normal-line-paths') .data(l_dataNest) // set our data .enter() // enter the data and start appending elements .append('path') .call(l_path); // calling our path function for later use when appending lines /************************************************************************** *** DRAW DASHED LINE ****************************************************** **************************************************************************/ svg.append('g') .attr('class', 'dashed-line-container') .selectAll('.dashed-line-paths') .data(l_dashedData) .enter() .append('path') .call(l_dashedPath); // other code... }); } // code about event... function l_path(myPath) { myPath.style("fill", "none") .style("stroke", function(d) { return l_color[d.key]; }) .attr('class', function(d) { return 'normal-line-paths path-' + d.key.replace(/\s+/g, ''); }) .attr("d", function(d) { return valueline(d.values); }) .style("opacity", 0.5) .on("mouseover", l_onMouseOverLine) .on("mouseout", l_onMouseOutLine); } function l_dashedPath(myPath) { myPath.style("fill", "none") .style("stroke", function(d) { return l_color[d.key]; }) .style("stroke-width", 5) .attr("stroke-dasharray", '4') .attr('class', function(d) { return 'dashed-line-paths path-' + d.key.replace(/\s+/g, ''); }) .attr("d", function(d) { return valueline(d.values); }) .style("opacity", 0.5) .on("mouseover", l_onMouseOverLine) .on("mouseout", l_onMouseOutLine); } /** * Function to return the data points that will create the dashed lines. */ function l_getDashed(data) { return data.map(function(collection, index) { var l_startNaN = false; var l_dashed = { key: collection.key, // disease values: [] //array of death, death and year }; collection.values.forEach(function(dataPoint, index) { var l_value = dataPoint.value; var values = collection.values; if($.isNumeric(l_value) && l_startNaN) { l_startNaN = false; l_dashed.values.push(values[index]); } else { if(($.isNumeric(l_value) && l_startNaN) || (!$.isNumeric(l_value) && !l_startNaN)) { l_startNaN = true; l_dashed.values.push(values[index-1]); } } }) if((l_dashed.values.length % 2)) { l_dashed.values.pop(); } return l_dashed; }); } If I comment this piece of code: // apend a group element which will contain our dashed lines /*svg.append('g') .attr('class', 'dashed-line-container') .selectAll('.dashed-line-paths') .data(l_dashedData) .enter() .append('path') .call(l_dashedPath);*/ I get: and if I comment: // apend a group element which will contain our lines /*svg.append('g') .attr('class', 'l_line-container') .selectAll('.normal-line-paths') .data(l_dataNest) // set our data .enter() // enter the data and start appending elements .append('path') .call(l_path); */ I get: So there must be a problem in l_dashedPath(myPath) method. But if I print d, I get the four ends of the two portions dashed (Tuberculosis): 23, 32, 15, 16 that are correct. .attr("d", function(d) { console.log(d); return valueline(d.values); }) Also the l_getDashed(data) method seems correct to me.
Inside Plunker you can't have spaces on filename. Rename it 'United Kingdom.csv' to something with underscore (_) or so. "United_Kingdom.csv" Your function l_getdashed, don't take the next point for every dashed segment. At the end you just get a point, not a segment. I'ver re-refactory your function: function l_getDashed(data) { return data.map(function(collection, index) { var l_startNaN = false; var l_dashed = { key: collection.key, values: [] }; collection.values.forEach(function(dataPoint, index) { var l_value = dataPoint.value; var values = collection.values; if ($.isNumeric(l_value) && l_startNaN) { l_startNaN = false; l_dashed.values.push(values[index]); } else if ( !$.isNumeric(l_value) !== l_startNaN) { l_startNaN = true l_dashed.values.push(values[index-1]); } }) if (l_dashed.values.length % 2) { l_dashed.values.pop() } return l_dashed; }); } Here the update working Plunker DENMARK Your code has concept errors. Thats why you see 'Denmark' like that. Graphically: Code generate multiples paths for every abailable data and just ONE path for all NA data. Instead it must generate a segments for every gap on data.- BELGIUM Belguim is a different history. The graph is ok, your data has isolated points (red dots) and can not be dismiss. You can not draw a dashed-line from 1997-2009 (black arrow) because you'll discarding data.