Skip to content

Instantly share code, notes, and snippets.

@Alex-Devoid
Last active June 28, 2019 18:50
Show Gist options
  • Select an option

  • Save Alex-Devoid/0f2546070ee4c8769d763eba6fd2a19b to your computer and use it in GitHub Desktop.

Select an option

Save Alex-Devoid/0f2546070ee4c8769d763eba6fd2a19b to your computer and use it in GitHub Desktop.
d3ClusterAndBeeswarm
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
<script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script><script src="//cdnjs.cloudflare.com/ajax/libs/topojson/1.6.9/topojson.min.js"></script><script src="//speedway.tucson.com/rwisner/guatmap/datamaps.gtm.js" type="text/javascript"></script><link href="https://unpkg.com/[email protected]/dist/css/bootstrap/tabulator_bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"> <script type="text/javascript" src="https://unpkg.com/[email protected]/dist/js/tabulator.min.js"></script><script src="https://cdn.jsdelivr.net/npm/[email protected]"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-modal/0.9.1/jquery.modal.min.js"></script><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jquery-modal/0.9.1/jquery.modal.min.css">
<script src="https://omnipotent.net/jquery.sparkline/2.1.2/jquery.sparkline.min.js"></script><script src="https://d3js.org/d3.v4.min.js"></script><script src="https://rawgit.com/Kcnarf/d3-beeswarm/master/build/d3-beeswarm.js"></script><style>
.btn-toolbar {
flex-wrap: wrap;
}
.btn-group {
flex-wrap: wrap;
}
.circ {
fill: #bc53ff;
fill-opacity: 0.6;
cursor: pointer;
}
/* tooltips */
.tooltip22 {
position: absolute;
text-align: left;
font-family: "PT Sans", sans-serif;
white-space: normal;
padding: 6px;
font-size: 14px;
background: #eee;
border: 1px solid gray;
border-radius: 10px;
/* pointer-events: none; */
/* cursor: none; */
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<div id="svganchor"></div>
</div>
<div class="row">
<div class="btn-toolbar mb-3" style="justify-content: center; display: flex;" role="toolbar" aria-label="Toolbar with button groups">
<button id="value" class="buttonScatter active">Files</button>
<button id="profit" class="buttonGroup">Profit</button>
</div>
</div>
</div>
</body>
<script type="text/javascript">
function sentinceFun(d) {
var result = d.split(',');
result.forEach(function(d,i,array) {
array[i] = Number(d);
})
return result;
}
function otherSentinceFun(d) {
var result = d.split(',');
return result;
}
var data_set2;
var data_s = 'Citizenship';
var margin = {top: 0, right: 0, bottom: 0, left: 0},
radius = 4,
tooltip2,
dataset,
x,
width = 600,
height = 500;
var maxRadius = 20,
maxHeight = height/2-maxRadius;
var arrangementMax = -Infinity;
var beeswarmArrangement = [];
var globTick;
var chartState = {};
chartState.variable = "";
chartState.lable = "";
var svg2 = d3.select("#svganchor")
.append("svg")
.attr("viewBox", "0 0 960 500")
.attr("height", height)
.attr("x1", 0)
.attr("y1", (height/2)-30)
.attr("x2", width)
.attr("y2", (height/2)-30);
var url = 'https://speedway.tucson.com/misc/google_sheets/?action=getdata&id=124';
// d3.json('data1test.json', function(data){
d3.json(url,function(error, response) {
response[1].forEach(function(d,i) {
var obj = {id: d[0],
Name: d[0],
Case: parseInt(d[response[2].indexOf("Case")]),
value: parseInt(d[response[2].indexOf("Docs")]),
profit: d[response[2].indexOf("profit")],
Citizenship: d[response[2].indexOf("Citizenship")],
noDefs: parseInt(d[response[2].indexOf("nodefs")]),
plea: d[response[2].indexOf("Plea")],
migrantsNum: parseInt(d[response[2].indexOf("migrants")]),
migCit: d[response[2].indexOf("migCit")],
otherSen: otherSentinceFun(d[response[2].indexOf("otherSen")]),
probation: sentinceFun(d[response[2].indexOf("probation")]),
prison: sentinceFun(d[response[2].indexOf("prison")]),
smugSentence: d[response[2].indexOf("Sentence")]
}
response[1].splice(i, 1, obj);
});
var data = response[1];
console.log(data);
while(361 < data.length){
data.pop();
}
var xAxisCall = d3.axisBottom();
var xAxisCallGroup = d3.axisBottom();
var xAxisCallBand = d3.axisBottom();
var x = d3.scaleLinear();
var p = d3.scalePoint().range([0, width])//.padding(5);
x.domain([-10, d3.max(data, function (d) { return d.value;})+10])
.range([margin.left, width - margin.right]);
xAxisCall.scale(x).ticks(10);
function setScaleGroup(data_set2){
p.domain(data.map(function(d){ return d[data_set2]; }));
xAxisCallGroup.scale(p);
}
function setScale2(data_set2){
x.domain([-10, d3.max(data, function (d) { return d[data_set2];})+10])
.range([margin.left, width - margin.right]);
xAxisCall.scale(x);
}
function updateAxisGroup(){
svg2.select("#Xaxis")
.transition()
.duration(1500)
.delay(500)
.call(xAxisCallGroup);
}
function updateAxis2(){
svg2.select("#Xaxis")
.transition()
.duration(1500)
.call(xAxisCall)
}
var tooltip2 = d3.select("#svganchor").append("div")
.attr("class", "tooltip22")
.style("opacity", 0);
///////////
function computeArrangementMax() {
arrangementMax = -Infinity;
beeswarmArrangement.forEach(function(bee) {
if (arrangementMax < Math.abs(bee.y)) {
arrangementMax = Math.abs(bee.y);
}
})
}
function lineStretch(bee){
var freeCoord = bee.y;
return maxHeight*freeCoord/arrangementMax;
}
function logStretch(bee){
var freeCoord = bee.y;
return maxHeight*Math.sign(freeCoord)*Math.log((Math.E-1)*Math.abs(freeCoord)/arrangementMax+1);
}
////////
///////////
//split and grouped force
//////
//////
var simulation = d3.forceSimulation()
.force('y', d3.forceY(height/2).strength(1))
.force("charge", d3.forceManyBody().strength(1)) // Nodes are attracted one each other of value is > 0
.force("collide",d3.forceCollide(radius+2)//.iterations(3)
)
.alpha(2);
var init_decay;
init_decay = setTimeout(function(){
console.log('init alpha decay')
simulation.alphaDecay(0.1);
}, 500);
function drawBeeswarm(data_set2){
setScale2(data_set2);
var swarm = d3
.beeswarm()
.data(data) // set the data to arrange
.distributeOn(function(d) {
// set the value accessor to distribute on
return x(d[data_set2]); // evaluated once on each element of data
}) // when starting the arrangement
.radius(radius) // set the radius for overlapping detection
.orientation('horizontal') // set the orientation of the arrangement
// could also be 'vertical'
.side('symetric') // set the side(s) available for accumulation
// could also be 'positive' or 'negative'
// .arrange(); // launch arrangement computation;
// return an array of {datum: , x: , y: }
// where datum refers to an element of data
// each element of data remains unchanged
beeswarmArrangement = swarm.arrange();
computeArrangementMax();
var circles1 = svg2.selectAll('circle')
.data(beeswarmArrangement);
var circlesEnter = circles1.enter().append('circle')
.attr('cx', function(bee) { return bee.x;})
.attr('cy', function(bee) {return height/2 + lineStretch(bee);})
.attr('r', 4)
.style("fill", function(bee){
if (bee.datum.Case === 223){
console.log('this is scott');
return "red";
}
})
.style("opacity", function(bee){
if (bee.datum.Case !== 223){
return .70;
}
})
.on("mouseover", function(d) {
console.log('tooltip');
tooltip2.style("opacity", .98);
tooltip2.html("Name: <strong>" + d.datum.Name + "<br>"
+ ": <strong>" + d.datum[data_set2] + "")
.style('top', d3.event.pageY + 'px')
.style('left', d3.event.pageX + 'px')
.style("opacity", 0.9);
})
.on("mouseout", function(d) {
tooltip2.transition()
.duration(200)
.style("opacity", 0);
});
circles1 = circles1.merge(circlesEnter);
}
drawBeeswarm('value');
function tick1(){
svg2.selectAll("circle")
.transition()
.duration(1750)
.attr('cx', function(d){
return d.x = Math.max(radius, Math.min(height - radius, d.x))})
.attr('cy', function(d){return d.y = Math.max(radius, Math.min(height - radius, d.y))});
}
function grouped(data_set2){
console.log('simulation');
var allGroup = [];
var allGroup = d3.map(beeswarmArrangement, function(d){return(d.datum[data_set2])}).keys();
console.log(allGroup);
p.range([0, (width)]).domain(allGroup);
xAxisCallGroup.scale(p);
simulation.nodes(beeswarmArrangement).force('x', d3.forceX().strength(1).x(function(d){
return p(d.datum[data_set2])
})).on('tick', tick1);
updateAxisGroup();
simulation
.alpha(3)
.alphaDecay(0.2)
.restart();
clearTimeout(init_decay);
init_decay = setTimeout(function(){
console.log('init alpha decay');
simulation
.alpha(0)
.alphaDecay(0.2);
}, 2000);
//
}
svg2.append("g")
.attr("id", "Xaxis")
.call(xAxisCall);
var statAxis = svg2.append("line")
.attr("id", "axis");
statAxis.attr("x1", 0)
.attr("y1", height/2)
.attr("x2", width)
.attr("y2", height/2);
/// scatter Line scale
d3.selectAll('.buttonScatter').on('click', function(){
simulation.stop();
d3.selectAll('.buttonGroup, .buttonScatter').classed('active', false);
var button = d3.select(this);
button.classed('active', true);
data_set2 = button.attr('id');
drawBeeswarm(data_set2);
tooltipHTML(data_set2);
setScale2(data_set2);
updateAxis2();
d3.selectAll('circle')
.transition()
.delay(250)
.duration(1500)
.attr("cx", function(bee){return bee.x;})
.attr("cy", function(bee) {return height/2 + lineStretch(bee);});
});
/// Grouped band scale
d3.selectAll('.buttonGroup').on('click', function(){
simulation.stop();
d3.selectAll('.buttonGroup, .buttonScatter').classed('active', false);
var button = d3.select(this);
button.classed('active', true);
data_set2 = button.attr('id');
grouped(data_set2);
tooltipHTML(data_set2);
})
function tooltipHTML(data_set2){
console.log('mouse over this one: '+ data_set2);
svg2.selectAll("circle").on("mousemove", function(d) {
var newHtml = "Name: <strong>" + d.datum.Name + "<br>"
+ ": <strong>" + d.datum[data_set2] + "";
tooltip2.html(newHtml);
});
}
})
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment