use d3 to create neo4j node tooltip - d3.js

When using neo4j, if you click the node, the circle tooltip will be shown with three new features.
I am just wondering how to have this kind of tooltip by using d3. Does anyone has any ideas how to implement it?
Thanks.

Here is a very basic example to get you started.
It displays a circle and its concentric "tooltip" arcs when it's clicked:
var width = 400;
var height = 200;
var svg =
d3.select("svg").attr("width", width).attr("height", height)
.append("g").attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")");
var clicked = false;
svg.append("circle")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", 45)
.attr("fill", "green")
.attr("stroke", "light-grey")
.on("click", addOrRemoveTooltip)
.attr("cursor", "pointer");
function addOrRemoveTooltip() {
if (clicked) {
d3.selectAll("path").remove();
clicked = false;
} else {
var arc = d3.arc().innerRadius(47).outerRadius(80);
svg.selectAll("arcs")
.data([
{ start: 0, end: 1/3 },
{ start: 1/3, end: 2/3 },
{ start: 2/3, end: 1 }
])
.enter().append("path")
.attr("d", d => arc({
"startAngle": d.start * 2 * Math.PI + 0.01,
"endAngle": d.end * 2 * Math.PI - 0.01
}))
.attr("fill", "lightgrey");
clicked = true;
}
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg></svg>
The circle is given a listener on clicks:
svg.append("circle").on("click", doSomething)
When the circle is clicked, this listener is activated and draws 3 arcs this way:
var arc = d3.arc().innerRadius(47).outerRadius(80);
svg.selectAll("arcs")
.data([
{ start: 0, end: 1/3 },
{ start: 1/3, end: 2/3 },
{ start: 2/3, end: 1 }
])
.enter().append("path")
.attr("d", d => arc({
"startAngle": d.start * 2 * Math.PI + 0.01,
"endAngle": d.end * 2 * Math.PI - 0.01
}))
We then need a global variable which will store the status of the button: if it's clicked or not.
This way when the circle's click listener is activated again, we know that its previous state was clicked, which means the tooltip arcs should be removed:
d3.selectAll("path").remove();

Related

Creating semi circular a Gauge using NVd3

I have a project in which I am using nvd3.js and want to create a semi-circular gauge. How do I create it?
I did try to use guage.js for the same but the problem I am facing there is that I cannot add customized/tags labels for the gauge that are of string type and I could not find a way around the problem. Any help is appreciated.
This is what I came up with. It's not perfect, the tooltips are rather meaningless. Also I've been trying to come up with a gradient fill to give it a 3D look, but haven't managed to yet.
<div style="height: 200px; width: 350px;">
<div style="height: 350px; width: 350px;">
<svg id="gaugeContainer"></svg>
</div>
</div>
<script>
//d3.json("/api/gauge", updateChart);
updateChart({ "max": 100, "current": 90 });
function updateChart(data) {
var containerSvg = "gaugeContainer";
var height = 350;
var width = 350;
// The first graph section always starts at 0 degrees, but we
// want it to be horizontal so we rotate the whole thing -90
// degrees, then correct the labels.
// The formula for needle angle is ((current/max) * 180) - 90
// current / max will always be 1.0 or less,
// the fraction of max that the needle indicates,
// * 180 converts to degrees and -90 compensates for rotation.
var indicatorAngle = ((data.current / data.max) * 180) - 90;
var colors = ["green", "orange", "red", "white"];
// Apparently after 4 sections are defined, the fifth one is
// dead, as in not graphed. We play a little trick, defining a
// very small white section so we can display
// the max value at the end of its visible range.
// The key values could just as easily be strings.
var graphData = [
{ key: Math.round((data.max / 8) * 1), y: 1 },
{ key: Math.round((data.max / 8) * 3), y: 1 },
{ key: Math.round((data.max / 8) * 6), y: 2 },
{ key: Math.round((data.max / 8) * 8), y: 0.2 },
{ key: "", y: 3.8 }
];
var arcRadius = [
{ inner: 0.6, outer: 1 },
{ inner: 0.6, outer: 1 },
{ inner: 0.6, outer: 1 },
{ inner: 0.6, outer: 1 }
];
nv.addGraph(function () {
var chart = nv.models.pieChart()
.x(function (d) { return d.key })
.y(function (d) { return d.y })
.donut(true)
.showTooltipPercent(false)
.width(width)
.height(height)
.color(colors)
.arcsRadius(arcRadius)
.donutLabelsOutside(true)
.showLegend(false)
.growOnHover(false)
.labelSunbeamLayout(false);
d3.select("#" + containerSvg)
.datum(graphData)
.transition().duration(1)
.attr('width', width)
.attr('height', height)
.attr("transform", "rotate(-90)")
.call(chart);
// draw needle
d3.select("#" + containerSvg)
.append("path")
.attr("class", "line")
.attr("fill", "none")
.style("stroke", "gray")
.style("stroke-width", "1")
.attr("d", "M0, -3 L153, 0 0,3 Z")
.attr("transform", "translate(175,179) rotate(" + indicatorAngle + ")");
d3.select("#" + containerSvg)
.append("circle")
.attr("r", "6")
.attr("cx", "0")
.attr("cy", "0")
.style("stroke", "gray")
.style("stroke-width", "1")
.attr("transform", "translate(175,179) rotate(" + indicatorAngle + ")");
// correct text rotation
d3.select("#" + containerSvg).selectAll("text")
.attr("transform", "rotate(90)");
return chart;
});
}
</script>

D3.js relocate element when zooming

I have multiple group elements with text element inside them. When I'm zooming with the mouse wheel, then everything is fine, my text is still inside my paths (polygons).
But when I'm zooming in automatically, then my text doesn't relocate.
Here is my function with auto zoom, I'm trying to find a specific path by ID, fill it with yellow, center and zoom to it.
function findByID(ID) {
svgContainer.selectAll("path")
.data(feat.features)
.filter(function (d) {
if (d.properties.myID == ID) {
centered = centered !== d && d;
var paths = svgContainer.selectAll("path")
.classed("active", function (d) {
d === centered;
});
var t0 = projection.translate(),
s0 = projection.scale();
projection.fitSize([width, height], centered);
var interpolateTranslate = d3.interpolate(t0, projection.translate()),
interpolateScale = d3.interpolate(s0, projection.scale());
var interpolator = function (t) {
projection.scale(interpolateScale(t))
.translate(interpolateTranslate(t));
paths.attr("d", path);
};
d3.transition()
.duration(5000)
.tween("projection", function () {
return interpolator;
});
return true;
}
})
.attr("fill", "#e9f356");
}
Here is a screenshot where I used my mouse wheel:
And here is a screenshot after my auto zoom is done. My lines are fade away also, why is it so?
Edit: This is how I add my text:
svgContainer.selectAll(null)
.data(feat.features.filter(function (d) { return d.properties.myId > 0; }))
.enter()
.append("g").attr("id", "txt")
.attr("transform", function (a) {
var centro = path.centroid(a);
return "translate(" + centro[0] + "," + centro[1] + ")";
})
.append("text")
.attr("text-anchor", "middle")
.attr("font-size", function (d) {
var bb = path.bounds(d)
return ((bb[1][0] - bb[0][0]) / 10) + "px";
})
.text("A/10/10/3");
Ok, I did it but when I try to zoom out with the mouse wheel it zooms out completely instantly. How can I make it smooth?
function findByID(ID) {
svgContainer.selectAll("path")
.data(feat.features)
.filter(function (d) {
if (d.properties.myID == ID) {
var bounds = path.bounds(d),
dx = bounds[1][0] - bounds[0][0],
dy = bounds[1][1] - bounds[0][1],
x = (bounds[0][0] + bounds[1][0]) / 2,
y = (bounds[0][1] + bounds[1][1]) / 2,
scale = .9 / Math.max(dx / width, dy / height),
translate = [width / 2 - scale * x, height / 2 - scale * y];
d3.select("#mainGroup").transition()
.duration(5000)
.style("stroke-width", 1.5 / scale + "px")
.attr("transform", "translate(" + translate + ")scale(" + scale + ")");
return true;
}
})
.attr("fill", "#e9f356");
}

How to add padAngle to one edge of arc using d3.js

Can anyone tell me How to add padAngle to one edge of arc using d3.js ?
I have created donut chart using d3.js. And found that there is an option to add space between arcs using .padAngle(). I achieved it. As per my requirement I need to add padding only one side of arc.
Can anyone suggest me how can I achieve it ?
As per my requirement, I should add extra padding for every 3 arcs. So I am passing padding using paddingFunction(it's customized) on top of arc. But it's adding both sides.
Below is my code:
var data = [46.00, 67, 50.00, 10.00,30.00,40.00],
color = ['red', 'green', 'black', 'pink','blue','orange'],
grpStrk = [0.1, 0.1, 0.5, 0.1, 0.1, 0.5],
width = '272',
height = '272',
radius = height/2 - 11;
/* function to render donut chart as per values and colors */
function renderSavingsDonutChart(data, color, grpStrk) {
try {
d3
} catch (err) {
return false;
}
/* function to return padding for chart arc based on flag to differenciate groups */
var paddingFunction = function(d,i) {
return grpStrk[i];
};
var arc = d3.svg.arc().innerRadius(radius-20).outerRadius(radius);
var pie = d3.layout.pie().sort(null);
if(isMobile()){
width = 257;
height = 257;
var arc = d3.svg.arc().innerRadius(radius-25).outerRadius(radius);
var forVbWidth = width-4;
var forVbHeight = height-4;
var svg = d3.select("#results-donut-chart").append("svg").attr("width", width).attr("height", height).attr("viewBox","2 2 " + forVbWidth + " " + forVbHeight).attr("preserveAspectRatio","xMidYMid").append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
}else{
var svg = d3.select("#results-donut-chart").append("svg").attr("width", width).attr("height", height).attr("viewBox","9 9 " + (width-19) + " " + (height-19)).attr("preserveAspectRatio","xMidYMid").append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
}
svg.selectAll("path")
.data(pie(data))
.enter()
.append("path")
.style(
{
"fill": function(d, i) {
return color[i];
}
})
//.attr("d", arc);
.attr("d", arc.padAngle(paddingFunction));
//On Resize
var aspect = width / height,
chart = $("#results-donut-chart svg");
$(window).on("resize load", function() {
var targetWidth = chart.parent().width();
chart.attr("width", targetWidth);
chart.attr("height", targetWidth / aspect);
});
}
/*Calling savings donut on page load*/
renderSavingsDonutChart(data, color, grpStrk);

Keeping text horizontal while it's position is rotating with d3.js

I'm trying to add labels to the planets around the sun into this example : http://bl.ocks.org/djvanderlaan/4953593.
So far, I've managed to add the labels, but the orientation of the labels is rotating with their position, while I want to keep them horizontal for the comfort of the readers.
I've been finding a beginning of a solution to my problem here : how to keep text orientation unchanged during rotation in SVG
but it's seems very complicated to me (I am a newbie and really not good at trigonometry) and plus, it's not using d3.js.
Here is the code that I'am using :
<div id="planetarium">
</div>
<script type="text/javascript">
var w = 800, h = 600;
var t0 = Date.now();
var planets = [
{ R: 300, r: 5, speed: 5, phi0: 90, name : 'Mercure'},
{ R: 150, r: 10, speed: 2, phi0: 190, name : 'Saturne'}
];
var svg = d3.select("#planetarium").insert("svg")
.attr("width", w).attr("height", h);
svg.append("circle").attr("r", 20).attr("cx", w/2)
.attr("cy", h/2).attr("class", "sun")
var container = svg.append("g")
.attr("transform", "translate(" + w/2 + "," + h/2 + ")")
container.selectAll("g.planet").data(planets).enter().append("g")
.attr("class", "planet").each(function(d, i) {
var orbit = d3.select(this).append("circle").attr("class", "orbit")
.attr("r", d.R);
var planet = d3.select(this).append("circle").attr("r", d.r).attr("cx",d.R)
.attr("cy", 0).attr("class", "planet");
var text = d3.select(this).append("text")
.attr("x", d.R)
.attr("y", ".31em")
.text(function(d) { return d.name; });
d3.timer(function() {
var delta = (Date.now() - t0);
planet.attr("transform", function(d) {
return "rotate(" + d.phi0 + delta * d.speed/200 + ")";
});
text.attr("transform", function(d) {
return "rotate(" + d.phi0 + delta * d.speed/200 + ")";
});
});
});
</script>
Here is my plunkr : http://plnkr.co/edit/dJEVXIeR7ly536tcMPWt?p=preview Thank you very much for your help !
I've finally found a solution inspired by this example : D3.js: rotate group, keep text the same orientation?
Instead of making two different variables for planets and text, I've gathered them in a same rotating group, and then added an inverse rotation on the text, but centered on the planet's center rather than the center of the container. Then I set both phi0 (the positions of the planets at the beginning of the animation) to 0, so that the text would be frozen horizontally. Here is my code :
var planets = [
{ R: 300, r: 5, speed: 5, phi0: 0, name : 'Mercure'},
{ R: 150, r: 10, speed: 2, phi0: 0, name : 'Saturne'}
];
//...
var container = svg.append("g")
.attr("transform", "translate(" + w/2 + "," + h/2 + ")")
var orbit = container.selectAll(".orbit")
.data(planets)
.enter()
.append("circle")
.attr("class", "orbit")
.attr("r", function(d) {return d.R;});
var planets = container.selectAll(".planet")
.data(planets)
.enter()
.append("g")
planets.append("circle")
.attr("class", "planet")
.attr("r", function(d) {return d.r;})
.attr("cx", function(d) {return d.R; })
.attr("cy", 0);
planets.append("text")
.attr("dx", function(d) {return d.R;})
.attr("dy", ".35em")
.text(function(d) {return d.name});
d3.timer(function() {
var delta = (Date.now() - t0);
planets.attr("transform", function(d) {
return "rotate(" + d.phi0 + delta * d.speed/200 + ")";
});
planets.selectAll("text").attr("transform", function(d) {
return "rotate(" + -1*(d.phi0 + delta * d.speed/200) + " " + d.R + " " + 0 + ")";
});
});
Not sure this is a very good solution, but for the moment it works. I will learn trigonometry though, I promise ;)

d3 circles shifting position on click-to-zoom

I'm trying to implement zooming on a d3 graphic with a bunch of data as circles. The data is made up of 1 large circle and many smaller circles plotted inside it. I want to click on the smaller circles and zoom to them. I'm using a variation of the zoom from the zoomable circle packing demo. I don't want to use the demo code directly but I've got it mostly working.
Initially all the circles are in their correct positions. However when I click on the smaller circles, they shift position right before the zoom. When I click on the white circle to zoom back, you can see they are now permanently shifted. And when it does zoom, the circles don't zoom into the center of the viewport, like they do in the demo.
I've noticed that when I comment out the transform line
node.attr("transform", function(d) { return "translate(" + (xscale(d.cx) - v[0]) * k + "," + (yscale(d.cy) - v[1]) * k + ")"; });
the circles now remain in their correct positions. Their sizes scale up as they should, but now they just merge into one another as they get bigger, because I'm no longer translating them. So the problem must be something in my transform attribute, but I can't figure out what it is. Or maybe it's something with my initial view? When I uncomment the zoomTo(view) the circles immediately move to the incorrect positions.
How do I get their positions to remain in the right positions? And how do I get the circles to zoom to the center of the viewpoint? I thought I followed the demo code pretty closely, but it's not quite working right. Any ideas?
I'd also like the axes to zoom as well but I haven't gotten that far into my problem yet.
Here's my jsfiddle.
And my full javascript code
function loadPlateDesign(){
var width = 400;
var height = 400;
var padding = 55;
var plateid = 7443;
var plateCen = {'ra': 230.99167, 'dec': 42.68736 };
var data = [{'name':7443,'color': 'white', 'cx': 0.0, 'cy': 0.0, 'r': 200},
{'color': 'red', 'cx': 8.23066, 'cy': -134.645, 'ra':231.1,'dec':42.1,'name': '1901', 'r': 10.0,
'children':[{'color': 'red', 'cx': 8.23066, 'cy': -134.645, 'ra':231.1,'dec':42.1,'name': 'a', 'r': 2.0}]},
{'color': 'blue', 'cx': -167.524, 'cy': -90.009, 'name': '711', 'r': 5.0}];
var xscale = d3.scale.linear().domain([330.,-330.]).range([0,400]);
var yscale = d3.scale.linear().domain([330.,-330.]).range([0,400]);
// initial focus and view
var focus = {'name':7443,'color': 'white', 'cx': 0.0, 'cy': 0.0, 'r': 200};
var view = [xscale(0.0),yscale(0.0),200*2];
// make the main svg element
var svg = d3.select('#platedesign').append('svg')
.attr('width',width+padding)
.attr('height',height+padding);
// add the plate and ifu data
var ifus=svg.selectAll('circle').data(data).enter().append('circle')
.attr('id',function(d){return d.name;})
.attr('cx',function(d,i){return xscale(d.cx);})
.attr('cy',function(d,i){return yscale(d.cy);})
.attr('r',function(d,i){return d.r;})
.style('fill',function(d,i){return d.color;})
.style('stroke','black')
.on('click',function(d){
if (focus != d) zoom(d), d3.event.stopPropagation();
});
// add the axes
var rascale = d3.scale.linear().domain([plateCen.ra+1.5,plateCen.ra-1.5]).range([0,400]);
var decscale = d3.scale.linear().domain([plateCen.dec+1.5,plateCen.dec-1.5]).range([0,400]);
xaxis = d3.svg.axis().scale(rascale).orient('bottom');
yaxis = d3.svg.axis().scale(decscale).orient('right').ticks(5);
svg.append('g').attr('class','x axis')
.attr('transform','translate(0,'+(height+5)+')')
.call(xaxis)
.append('text')
.attr('x',width/2)
.attr('y',35)
.style('text-anchor','middle')
.text('RA');
svg.append('g').attr('class','y axis')
.attr('transform','translate('+(width+5)+',0)')
.call(yaxis)
.append('text')
.attr('transform','rotate(90)')
.attr('x',height/2)
.attr('y',-35)
.style('text-anchor','middle')
.text('Dec');
var node = svg.selectAll("circle");
//zoomTo(view);
function zoom(d){
console.log('zooming to', d.name);
var focus0 = focus; focus=d;
var newview = [xscale(d.cx), yscale(d.cy), d.r*2+20];
var transition = d3.transition()
.duration(d3.event.altKey ? 7500 : 750)
.tween('zoom', function(d){
var i = d3.interpolateZoom(view, newview);
return function(t) {zoomTo(i(t)); };
});
}
function zoomTo(v) {
var k = height / v[2]; view = v;
console.log(height, v);
node.attr("transform", function(d) { return "translate(" + (xscale(d.cx) - v[0]) * k + "," + (yscale(d.cy) - v[1]) * k + ")"; });
ifus.attr("r", function(d) { return d.r * k; });
}
}
Looks like you are mixing positioning methods. You set an initial cx and cy but then you zoom on a transform. Re-factoring a bit to get all the positioning done with transform fixes it up. I also found that you should initiate the view and do the zoom calculations on your d.cx and d.cy instead of on the xscale(d.cx).
function zoom(d){
console.log('zooming to', d.name);
var focus0 = focus; focus=d;
var newview = [d.cx, d.cy, d.r*2+20];
var transition = d3.transition()
.duration(d3.event.altKey ? 7500 : 750)
.tween('zoom', function(d){
var i = d3.interpolateZoom(view, newview);
return function(t) {zoomTo(i(t)); };
});
}
function zoomTo(v) {
var k = height / v[2]; view = v;
console.log(height, v);
node.attr("transform", function(d) { return "translate(" + xscale((d.cx - v[0]) * k) + "," + yscale((d.cy - v[1]) * k) + ")"; });
ifus.attr("r", function(d) { return d.r * k; });
}
Updated fiddle.

Resources