Extjs D3 Horizontal tree - d3.js

How to make the links start from the edge of the rectangle.
I am using Extjs modern framework 6.2.1. I am using sencha architect premium controls. I have overridden the control to bring this custom view. Now i am facing problem on links. I dont know how to make start it from the edge of the rectangle. Any help will be greatly appreciated

var rectSize=246;
this.setDiagonal(d3.svg.diagonal()
.source(function(d) { return {"x":d.source.x, "y":(d.source.y+rectSize)}; })
.target(function(d) { return {"x":(d.target.x), "y":d.target.y}; }) .projection(function(d) { return [d.y, d.x]; }));
Set the new diagonal with the new node y value. add the width of the node to source node y attr (Set it at the source). It will reflect for all the nodes

Related

D3 Selection Highlight (efficiency?)

I have a simple visual of many rects, over 100 I'd say. For aesthetic purposes I want to create a high light effect on mouse click. I also wanted to make this effect somewhat intuitive by removing that effect once the user clicks on a new rect. However I couldn't get this to work without resorting to a d3.selectAll() call, so I'm thinking this approach might not be ideal if this project gets any bigger. Here is the code:
.on('click.highlight', function() {
//set any previously highlighted rects back to normal color/brightness
d3.selectAll('.highlight').transition().duration(250)
.style('fill', function(d) { return d3.rgb(d.color)})
d3.select(this).classed('highlight',true);
//now it's safe to assign the current highlighted rect a brighter hue... i think
d3.select(this).transition().duration(250)
.style('fill', function(d) { return d3.rgb(d.color).brighter(.5)})
})
Though this code does what I wanted it to do, but presumably there could only ever be 1 other highlight rect to worry about at any give time. So again, I'm not sure that using d3.selectAll() is warranted here.
So anyway, is there a more efficient way? I'd like to keep it all within one .on('click') function if possible.
If you are looking to avoid use of .selectAll, you could create a selection of one rect that contains the last clicked rectangle. Each time you click on a rectangle:
unhighlight the previously selected highlighted rect
update that selection to reflect the most recently clicked rectangle
highlight the newly selected rect
I use the variable highlightedRect to hold the selection that will allow the above workflow:
var svg = d3.select("body").append("svg")
.attr("width",600)
.attr("height",400);
var highlightedRect = d3.select(null);
var rects = svg.selectAll("rect")
.data(d3.range(1600))
.enter()
.append("rect")
.attr("y",function(d) { return Math.floor(d/50)*12; })
.attr("x",function(d) { return d%50 * 12 })
.attr("width",11)
.attr("height",11)
.attr("stroke","white")
.on("click",function(d) {
// Recolor the last clicked rect.
highlightedRect.attr("fill","black");
// Color the new one:
highlightedRect = d3.select(this);
highlightedRect.attr("fill","steelblue");
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

How to dragover in this algorithm using d3.js drag.behaviour()?

I am trying to design an association miminap using d3.js. My goal is to position different items according to data and associate them using a drag function.
I try to calculate distance to different elements and if distance is lower than total radius, I consider this condition as a dragover. However drag function keeps selecting the node I drag instead of the node I drag it to.
var drag = d3.behavior.drag()
.on('dragstart', function() {})
.on('drag', function(d) {
nodes=d3.select(this.parentNode).selectAll("circle")[0];
nodes.pop(this);
var minDist=1000;
for (i=0;i<nodes.length;i++) {
var currentNode=d3.select(nodes[i]);
var r1=parseInt(this.style.r);
var r2=parseInt(currentNode.style("r"));
var dx=d3.event.x-parseInt(currentNode.style("cx"));
var dy=d3.event.y-parseInt(currentNode.style("cy"));
var dist=Math.sqrt(dx*dx+dy*dy);
if(dist<(r1+r2)) {
d.con=currentNode.attr("id");
currentNode.style("fill","red");
console.log(currentNode[0][0]);
}
}
d3.select(this)
.style("cx",d3.event.x)
.style("cy",d3.event.y);
})
Why does my futile attempt to remove the node I drag by entering nodes.pop(this) does not work ?
I have added an editable codepen version :
https://codepen.io/TeaCult/pen/ezJVyz?editors=1111
Thank you for reading.

Multiple nvd3 graphs and select issue

I have multiple nvd3 pie charts in the same page. Now when I try to position them,individually using this code below
d3.select(".nv-pieWrap")
.attr("transform", "translate(0,-35)");
Only the first graph in the page gets repositioned. It is said in the nvd3 support document that the select keyword if used in multiple functions,only selects the first element in the page.
Now when I replace "select" with "selectAll",every graph is re-positioned.
I want to position them separately ie the "translate" coordinates would be different in different cases. How would I accomplish that?. Can anyone help?
d3.selectAll(".nv-pieWrap")
.attr("transform", "translate(0,-35)");
I've got two solutions of the problem.
One solution is you can specify 'transform' attribute not as a constant "translate(0,-35)" but a function instead. And within the function decide which coordinates you need based on the chart's index:
d3.selectAll(".nv-pieWrap")
.attr("transform", function(d, i) {
// i is a chart index
if (i === 0) {
return 'translate(0,-35)';
} else {
return 'translate(100,-35)';
}
});
Another solution is to wrap your code
d3.select(".nv-pieWrap")
.attr("transform", "translate(0,-35)");
into a transforming function which takes a node and transform as a parameter, e.g.
var transform = function(chart, transform) {
return
d3.select(chart)
.attr("transform", transform);
}
draw("#pieChart1", "translate(0,-35)");
draw("#pieChart2", "translate(100,-35)");

Semantic zoom on map with circle showing capital

I wanted to implement semantic zoom on map of d3.js. I have developed a example of map and Major cities of particular country, which is working fine but sometime circle got overlap due to near places in maps so if i implement semantic zoom which will solve my circle overlapping problem.
I don't understand how to transform only graph not circle in my map.
My zooming function code is :
var zoom = d3.behavior.zoom()
.on("zoom",function() {
g.attr("transform","translate("+
d3.event.translate.join(",")+")scale("+d3.event.scale+")");
g.selectAll("circle")
.attr("d", path.projection(projection));
g.selectAll("path")
.attr("d", path.projection(projection));
});
My jsfiddle link
Anybody help me please!
Are you asking how to not scale the circles according to the zoom? The way you have it you are scaling the g element and the circles are in it. The way I'd do it is to "shrink" the circles when zoomed.
// zoom and pan
var zoom = d3.behavior.zoom()
.on("zoom",function() {
g.attr("transform","translate("+
d3.event.translate.join(",")+")scale("+d3.event.scale+")");
g.selectAll("circle")
.attr("r", function(){
var self = d3.select(this);
var r = 8 / d3.event.scale; // set radius according to scale
self.style("stroke-width", r < 4 ? (r < 2 ? 0.5 : 1) : 2); // scale stroke-width
return r;
});
});
Update fiddle.
I know this is an old post and it already has an accepted answer but as the original post suggests, d3's Semantic Zoom is a cleaner way of doing this. I implemented the same thing you did (circles on cities on a map) by following this demo https://bl.ocks.org/mbostock/368095. The only thing I had to change was I had to subtract the initial position of the circles in the transform function in order to correct their initial position.
function transform(t) {
return function(d) {
const point = [d.x, d.y] // this is the initial point
const tPoint = t.apply(point) // after translation
const finalPoint = [tPoint[0] - d.x, tPoint[1] - d.y]// subtract initial x & y to correct
return `translate(${finalPoint})`;
};
}

how to transition a multiseries line chart to a new dataset

I could really use some guidance setting up a transition on my multiseries line chart. As an example of what I need, I've started with this great multiseries line chart: http://bl.ocks.org/mbostock/3884955. To that code, I've added an update() function that's called once using setInterval(). I've also created a new data set called data2.csv which is similar to data.tsv but has different values.
The update function should change the data that the line chart is displaying. Forget about making a nice smooth transition, I can't even get the data to update in the chart view. When I try using the update function, it looks like the new data is loaded properly into the javascript variables, but the lines on the chart don't change at all.
I've seen variations on this question asked a few times but haven't found an answer yet. Can anyone help me figure out how to transition this multi-series line chart to a new dataset (also multiseries)?
function update() {
d3.csv("data2.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date"; }));
// format the date
data.forEach(function(d) {
d.date = parseDate(d.date);
});
// rearrange the data, same as in the original example code
var cities2 = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, temperature: +d[name]};
})
};
});
// update the .city g's to the new dataset
var city2 = svg.selectAll(".city")
.data(cities2);
// redraw the lines with the new data???
city2.selectAll("path")
.attr("d", function(d) { return line(d.values); });
clearInterval(transitionInterval);
});
}
UPDATE: NikhilS's answer contains the key to the solution in the comment trail.
You should make sure you are following the enter + update process as outlined by Mike Bostock in his stuff on the General Update Pattern. It looks like you haven't invoked any kind of d3 transition. You also haven't specified an enter or exit for the update function, which will cause problems if you have new data coming in and/or old data going out. Try changing this:
var city2 = svg.selectAll(".city")
.data(cities2);
city2.selectAll("path")
.attr("d", function(d) { return line(d.values); });
to the following:
var city2 = svg.selectAll('.city')
.data(cities2);
var cityGroups = city2.enter().append('g')
.attr('class', 'city');
cityGroups.append('path')
.attr('class', 'line');
d3.transition().selectAll('.line')
.attr('d', function(d) { return line(d.values); });
city2.exit().remove();
I made a basic data re-join and update demo a while back, which you can view here.
use d3 Transition, you can make some sort of animation.
If you want to select a sub-interval of the data to plot the graph, no need manipulation on the data, just use a d3 brush and clip the graph
For a multi-series line graph with most of the line graph elements, you could refer to this example: http://mpf.vis.ywng.cloudbees.net/

Resources