Use d3.js and d3-tip.js to draw some circles. When circles are at same location there may be other attributes which are interesting about the overlapping circles. This is a method to separate those circles on the user interface to give the user separate handling. In addtion, the circles are labeled by location, and user can click on a lone circle to see metadata information.
Last active
November 7, 2017 22:51
-
-
Save louking/fd0fdbfc44e2fc0bd5bfb5e861c7222d to your computer and use it in GitHub Desktop.
overlapping labeled circles explode - select displays tip using d3-tip
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: apache-2.0 | |
height: 500 | |
scrolling: no | |
border: yes |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
circle { | |
fill: steelblue; | |
stroke: black; | |
stroke-width: 1.5px; | |
} | |
circle.handle { | |
fill: red; | |
} | |
text { | |
font-family: helvetica, sans-serif; | |
font-weight: bold; | |
} | |
.d3-tip { | |
line-height: 1.2; | |
font-size: small; | |
font-weight: bold; | |
font-family: sans-serif; | |
padding: 6px; | |
background: white; | |
border: 1px; | |
border-style: solid; | |
border-radius: 6px; | |
/* pointer-events: none; */ | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var rcircle = 10, // r for circle | |
dexp = rcircle * 4, // distance for explosion | |
durt = 500, // transition duration (msec) | |
textdy = 5, // a bit of a hack, trial and error | |
pi = Math.PI; | |
var t = d3.transition(durt); | |
// need #vis div for d3.tip to work | |
var vis = d3.select("#vis") | |
.append("svg") | |
.attr("width", 720) | |
.attr("height", 250); | |
var svg = d3.select("svg"); | |
// Initialize tooltip - https://github.com/Caged/d3-tip | |
tip = d3.tip() | |
.direction('e') | |
.offset([0,rcircle+1]) | |
.attr("class", "d3-tip") | |
// .attr("class", function(d) { "tip-" + d.loc }) | |
.html(function(d) { return "ID = " + d.id | |
+ "<br/>multiple lines" | |
+ "<br/><a href='https://google.com' target=_blank>link test</a>"; | |
}); | |
// Invoke the tip in the context of visualization | |
vis.call(tip) | |
// remove tip when click anywhere other than circle group | |
svg.on("click", tip.hide); | |
d3.json( | |
"locations.json", | |
function(error, data) { | |
if (error) throw error; | |
// showing general pattern from https://bl.ocks.org/mbostock/3808218 but in this case only enter() will be executed | |
// group idea adapted from https://www.dashingd3js.com/svg-group-element-and-d3js | |
// data join | |
var group = svg.selectAll("g") | |
.data(data) | |
.enter().append("g") | |
// cx and cy don't normally apply to group, but this is convenient for later processing | |
.attr("cx", function(d) { return d.x; }) | |
.attr("cy", function(d) { return d.y; }) | |
.attr("class", function(d) { return "g-loc-" + d.loc; }) | |
.attr("transform", "translate(0,0)") | |
.on("click", explodeData); | |
group.append("circle") | |
.attr("r", rcircle) | |
.attr("cx", function(d) { return d.x; }) | |
.attr("cy", function(d) { return d.y; }) | |
.attr("cid", function(d) { return d.id; }) | |
.attr("class", function(d) { return "c-loc-" + d.loc; }); | |
group.append("text") | |
.attr("x", function(d) { return d.x; }) | |
.attr("y", function(d) { return d.y; }) | |
.attr("text-anchor", "middle") | |
.attr("dy", function(d) { return textdy }) | |
.attr("class", function(d) { return "t-loc-" + d.loc; }) | |
.text(function(d) { return d.loc; }); | |
} | |
); | |
// called with group containing circle, text | |
// if there are other groups in same location, explode | |
// else special handling for lone group | |
function explodeData(d, i) { | |
// Use D3 to select element and also all at same location | |
var thisg = d3.select(this); | |
var theselocs = d3.selectAll(".g-loc-" + d.loc) | |
var numlocs = theselocs.size(); | |
// if only one at location, maybe there is some special processing | |
if (numlocs == 1) { | |
// handle single selection click | |
// don't let this through to svg click event | |
// http://bl.ocks.org/jasondavies/3186840 | |
d3.event.stopPropagation(); | |
tip.show(d); | |
// multiple at location, explode | |
} else { | |
// if not selected yet, explode all in same loc | |
if (!thisg.attr("exploded")) { | |
d3.select(this).raise(); | |
theselocs.attr("exploded", true); | |
var cx = Number(thisg.attr("cx")); | |
var cy = Number(thisg.attr("cy")); | |
// create lines now so they're underneath | |
// p1 = p3 because we'll be transitioning | |
theselocs.each(function (d,i) { | |
svg.append('line') | |
.attr("class", "l-loc-" + d.loc) | |
.attr("x1", cx) | |
.attr("y1", cy) | |
.attr("x2", cx) | |
.attr("y2", cy) | |
.attr("stroke-width", 1.5) | |
.attr("stroke", "black") | |
.transition(durt) | |
.attr("x2", cx + dexp * Math.cos((2*pi/numlocs)*i)) | |
.attr("y2", cy + dexp * Math.sin((2*pi/numlocs)*i)) | |
}); | |
// create handle for original location | |
svg.append("circle") | |
.attr("id", "exploded-" + d.loc) | |
.attr("class", "handle") | |
.attr("loc", d.loc) | |
.attr("r", rcircle) | |
.attr("cx", cx) | |
.attr("cy", cy) | |
.on("click", unexplodeData); | |
// explode | |
theselocs | |
.each(function(d, i){ | |
var thisg = d3.select(this); | |
// transition to new location | |
thisg.raise().transition(t) | |
.attr("transform", "translate(" | |
+ dexp * Math.cos((2*pi/numlocs)*i) + "," | |
+ dexp * Math.sin((2*pi/numlocs)*i) + ")" | |
); | |
}); | |
// if exploded and individual selected, maybe there is some special processing | |
} else { | |
// handle single selection click | |
// don't let this through to svg click event | |
// http://bl.ocks.org/jasondavies/3186840 | |
d3.event.stopPropagation(); | |
tip.show(d); | |
} | |
} // multiple at location | |
}; | |
// called with handle for an exploded group | |
function unexplodeData(d, i) { | |
// Use D3 to select element | |
var handle = d3.select(this); | |
var loc = handle.attr("loc"); | |
var x = handle.attr("cx"); | |
var y = handle.attr("cy"); | |
var theselocs = d3.selectAll(".g-loc-" + loc); | |
// set exploded circles to original state | |
theselocs.transition(t) | |
.attr("selected", null) | |
.attr("transform", "translate(0,0)") | |
.attr("exploded", null); | |
// shrink lines | |
d3.selectAll(".l-loc-" + loc) | |
.transition(t) | |
.attr("x2", x) | |
.attr("y2", y) | |
.remove() | |
// remove handle | |
d3.select("#exploded-" + loc).remove(); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<link href="circles.css" rel="stylesheet"> | |
<div id="vis"></div> | |
<script src="//d3js.org/d3.v4.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.8.0-alpha.1/d3-tip.js"></script> | |
<script src="circles.js"></script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ | |
{ "id":1, "loc":1, "x":140, "y":180 }, | |
{ "id":2, "loc":1, "x":140, "y":180 }, | |
{ "id":3, "loc":1, "x":140, "y":180 }, | |
{ "id":4, "loc":1, "x":140, "y":180 }, | |
{ "id":5, "loc":2, "x":520, "y":70 }, | |
{ "id":6, "loc":2, "x":520, "y":70 }, | |
{ "id":7, "loc":2, "x":520, "y":70 }, | |
{ "id":8, "loc":2, "x":520, "y":70 }, | |
{ "id":9, "loc":2, "x":520, "y":70 }, | |
{ "id":10, "loc":2, "x":520, "y":70 }, | |
{ "id":11, "loc":3, "x":320, "y":150 } | |
] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment