Last active
June 28, 2019 18:50
-
-
Save Alex-Devoid/0f2546070ee4c8769d763eba6fd2a19b to your computer and use it in GitHub Desktop.
d3ClusterAndBeeswarm
This file contains hidden or 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
| <!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