Adjusting circle size text and svg size - javascript

I am a new javascript programmer. I'm trying to adjust the circles' size and svg size given the size of the window. Also, the code now creates circles of different sizes, but haven't been able to simultaneously adjust to the text size.
var width = 600;
var height = 600;
// Place your JSON here.
var data = [
{ CategoryName: 'Adaptive Security', SkillProficiencyId: 1 },
{ CategoryName: 'Programmer', SkillProficiencyId: 2 },
{ CategoryName: 'Coffee Drinker', SkillProficiencyId: 3 }
];
/*
This 'cxBase' will be multiplied by element's index, and sum with offset.
So for 3 elements, cx = 0, 200, 400 ...
All these values changeable by this vars.
*/
const cxBase = 200;
const cxOffset = 100;
console.log(data);
// Make SVG container
var svgContainer = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
// This function will iterate your data
data.map(function(props, index) {
var cx = cxBase * (index) + cxOffset; // Here CX is calculated
var elem = svgContainer.selectAll("div").data(data);
var elemEnter = elem.enter()
.append("g")
var circles = elemEnter.append("circle")
.attr("cx", cx)
.attr("cy", 100)
.attr("r", props.SkillProficiencyId * 20)
.style("fill", "blue");
elemEnter
.append("text")
.style("fill", "white")
.attr("dy", function(d){
return 105;
})
.attr("dx",function(d){
return cx - (props.CategoryName.length * 3.5);
})
.text(function (d) {
return props.CategoryName
});
});
Using .attr("viewBox", "0 0 680 490") doesn't work so far. Just makes all the circles bigger but not in proportion to the window
// Make SVG container
var svgContainer = d3.select("body")
.append("svg")
.attr("viewBox", "0 0 680 490")
.attr("presserveAspectRatio", "xMinYMin meet")
//.attr("height", height)
;

I made five improvements:
Circle's width based on SkillProficiencyId.
Increased the svg width from 600 to 900.
Text will appear always on the middle of the circle: text.style("text-anchor", "middle") did the trick.
Text size proportional to circle size;
A smartest way to calc circle dx (w/o using arbritary offsets);
Codepen: https://codepen.io/mayconmesquita/pen/RwWoZbv
JS Code:
var width = 900;
var height = 400;
// Place your JSON here.
var data = [
{ CategoryName: 'Adaptive Security', SkillProficiencyId: 1 },
{ CategoryName: 'Programmer', SkillProficiencyId: 2 },
{ CategoryName: 'Coffee Lover', SkillProficiencyId: 3 },
{ CategoryName: 'Coffee Roaster', SkillProficiencyId: 4 }
];
function getCircleSize(SkillProficiencyId) {
var minSize = 60;
return minSize + (minSize * (SkillProficiencyId * .2));
}
// Make SVG container
var svgContainer = d3
.select("body")
.classed("svg-container", true)
.append("svg")
.attr("width", width)
.attr("height", height);
// This function will iterate your data
data.map(function(props, index) {
/*
The new CX calc:
('circleSize' * 2) will be multiplied by element's index.
So for 3 elements, cx = 70, 140, 210 ...
All these values changeable by this vars.
*/
var circleSize = getCircleSize(props.SkillProficiencyId);
var cx = (circleSize * 2) * (index); // Here CX is calculated
var elem = svgContainer.selectAll("div").data(data);
var elemEnter = elem
.enter()
.append("g")
.attr("transform", "translate(100, 0)"); // prevent svg crops first circle
var circles = elemEnter
.append("circle")
.attr("cx", cx)
.attr("cy", 160)
.attr("r", circleSize)
.style("fill", "blue");
elemEnter
.append("text")
.style("fill", "white")
.attr("dy", 165)
.attr("dx", cx)
.text(props.CategoryName)
.style("font-size", ((circleSize * .2) + index) + "px")
.style("text-anchor", "middle");
});

Related

d3js beeswarm with force simulation

I try to do a beeswarm plot with different radius; inspired by this code
The issue I have, is that my point are offset regarding my x axis:
The point on the left should be at 31.7%. I don't understand why, so I would appreciate if you could guide me. This could be improved by changing the domain of x scale, but this can't match the exact value; same issue if I remove the d3.forceCollide()
Thank you,
Data are available here.
Here is my code:
$(document).ready(function () {
function tp(d) {
return d.properties.tp60;
}
function pop_mun(d) {
return d.properties.pop_mun;
}
var margin = {top: 20, right: 20, bottom: 20, left: 40},
width = 1280 - margin.right - margin.left,
height = 300 - margin.top - margin.bottom;
var svg = d3.select("body")
.append("svg")
.attr("viewBox", `0 0 ${width} ${height}`)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var z = d3.scaleThreshold()
.domain([.2, .3, .4, .5, .6, .7])
.range(["#35ff00", "#f1a340", "#fee0b6",
"#ff0000", "#998ec3", "#542788"]);
var loading = svg.append("text")
.attr("x", (width) / 2)
.attr("y", (height) / 2)
// .attr("dy", ".35em")
.style("text-anchor", "middle")
.text("Simulating. One moment please…");
var formatPercent = d3.format(".0%"),
formatNumber = d3.format(".0f");
d3.json('static/data/qp_full.json').then(function (data) {
features = data.features
//1 create scales
var x = d3.scaleLinear()
.domain([0, d3.max(features, tp)/100])
.range([0, width - margin.right])
var y = d3.scaleLinear().domain([0, 0.1]).range([margin.left, width - margin.right])
var r = d3.scaleSqrt().domain([0, d3.max(features, pop_mun)])
.range([0, 25]);
//2 create axis
var xAxis = d3.axisBottom(x).ticks(20)
.tickFormat(formatPercent);
svg.append("g")
.attr("class", "x axis")
.call(xAxis);
var nodes = features.map(function (node, index) {
return {
radius: r(node.properties.pop_mun),
color: '#ff7f0e',
x: x(node.properties.tp60 / 100),
y: height + Math.random(),
pop_mun: node.properties.pop_mun,
tp60: node.properties.tp60
};
});
function tick() {
for (i = 0; i < nodes.length; i++) {
var node = nodes[i];
node.cx = node.x;
node.cy = node.y;
}
}
setTimeout(renderGraph, 10);
function renderGraph() {
// Run the layout a fixed number of times.
// The ideal number of times scales with graph complexity.
// Of course, don't run too long—you'll hang the page!
const NUM_ITERATIONS = 1000;
var force = d3.forceSimulation(nodes)
.force('charge', d3.forceManyBody().strength(-3))
.force('center', d3.forceCenter(width / 2, height/2))
.force('x', d3.forceX(d => d.x))
.force('y', d3.forceY(d => d.y))
.force('collide', d3.forceCollide().radius(d => d.radius))
.on("tick", tick)
.stop();
force.tick(NUM_ITERATIONS);
force.stop();
svg.selectAll("circle")
.data(nodes)
.enter()
.append("circle")
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", d => d.radius)
.style("fill", d => z(d.tp60/100))
.on("mouseover", function (d, i) {
d3.select(this).style('fill', "orange")
console.log(i.tp60,i)
svg.append("text")
.attr("id", "t")
.attr("x", function () {
return d.x - 50;
})
.attr("y", function () {
return d.y - 50;
})
.text(function () {
return [x.invert(i.x), i.tp60]; // Value of the text
})
})
.on("mouseout", function (d, i) {
d3.select("#t").remove(); // Remove text location
console.log(i)
d3.select(this).style('fill', z(i.tp60/100));
});
loading.remove();
}
})
})

d3 projection geoMercator returns none

I'm trying to plot some cordinates on an image using d3 v4 following this Link.When i'm trying to pass my co-ordinates to the projection function it returns NAN for some of the data points. I got some help from here that javascript follows the following latitude and longitude convention but not sure how it exacty works.
This is the format of my data:
{coordinates: [60, 84],coordinates: [204, 92.4],coordinates: [117, 132.72]}
D3 code :
var el = d3.select('.js-map'),
// 150 DPI image
width = 300,
// 150 DPI image
height = 300;
var thisObj = this;
var projection = d3.geoMercator()
.scale(1)
.translate([0, 0])
console.log('projection', projection);
var path = d3.geoPath()
.projection(projection);
var map = el.append('svg')
.attr('width', width)
.attr('height', height);
map.append('image')
.attr('xlink:href', this.floorMaps[0])
.attr('width', width)
.attr('height', height);
this.floorSensorInfo.forEach((data, index) => {
var lonlat = projection(data.coordinates);
console.log('Longitude Latitude', lonlat);
I can see my data output like [2.0420352248333655, NaN]and not sure what happened exactly.
and moreover if someone can explain following the first link which i realy don't understand it would be really helpful
Exported bounds of raster image
rasterBounds = [[-122.7895, 45.4394], [-122.5015, 45.6039]]
Update:
#Andrew suggested to plot normal co-ordinates because latitude and longitude apply only to world maps. So i had pasted my below working code version now which is plotting the points on the image now.
var svg = d3.select("body")
.append("svg")
.attr("width",960)
.attr("height",500)
// image width and height in pixels, we don't want to skew this or scale this (then image units aren't straight pixels)
var imageWidth = 300;
var imageHeight = 168;
var color_hash = { 0 : ["apple", "green"],
1 : ["mango", "orange"],
2 : ["cherry", "red"]
}
function scale(coords) {
return [coords[0] * imageWidth / 100, coords[1] * imageHeight / 100];
}
svg.append("image")
.attr("width",imageWidth)
.attr("height",imageHeight)
.attr("x", 0) // could be non-zero, but we would have to shift each circle that many pixels.
.attr("y", 0)
.attr("xlink:href", this.floorMaps[0])
var data = this.floorSensorInfo
// var dataNest = d3.nest()
// .key(function (d) { return d['sensor_name']; })
// .entries(data)
data.forEach(function (d, i) {
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) { return (d.value)[0]; })
.attr("cy", function(d) { return (d.value)[1]; })
.attr("r", 5)
.style("fill", function(d) {
var color = color_hash[data.indexOf(d)][1]
return color;
})
svg.append('text')
.attr("x", 20+(i)*100) // space legend
.attr("y", imageHeight+20)
// style the legend
.style("stroke", function () { // Add the colours dynamically
return d['color'] = color_hash[data.indexOf(d)][1];
})
//.attr("dy", ".35em")
.text( d.sensor_name);
//.text("jjjjjjj")
})}
var svg = d3.select("body")
.append("svg")
.attr("width",960)
.attr("height",500)
// image width and height in pixels, we don't want to skew this or scale this (then image units aren't straight pixels)
var imageWidth = 300;
var imageHeight = 168;
var color_hash = { 0 : ["apple", "green"],
1 : ["mango", "orange"],
2 : ["cherry", "red"]
}
function scale(coords) {
return [coords[0] * imageWidth / 100, coords[1] * imageHeight / 100];
}
svg.append("image")
.attr("width",imageWidth)
.attr("height",imageHeight)
.attr("x", 0) // could be non-zero, but we would have to shift each circle that many pixels.
.attr("y", 0)
.attr("xlink:href", this.floorMaps[0])
var data = this.floorSensorInfo
// var dataNest = d3.nest()
// .key(function (d) { return d['sensor_name']; })
// .entries(data)
data.forEach(function (d, i) {
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) { return (d.value)[0]; })
.attr("cy", function(d) { return (d.value)[1]; })
.attr("r", 5)
.style("fill", function(d) {
var color = color_hash[data.indexOf(d)][1]
return color;
})
svg.append('text')
.attr("x", 20+(i)*100) // space legend
.attr("y", imageHeight+20)
// style the legend
.style("stroke", function () { // Add the colours dynamically
return d['color'] = color_hash[data.indexOf(d)][1];
})
//.attr("dy", ".35em")
.text( d.sensor_name);
//.text("jjjjjjj")
})}
javascript d3.js

different animation for the same event

The basic idea is that every time that I click the plane will drop a bomb. The moving plane and the dropping bomb are ok, i have two problems:
1) If I drop multiple bombs every explosion animation starts at the same moment of the previous one. Is there is a way to have a different animation for every dropped bomb?
2) I am trying to use ease("cubic"), but there is some mistake using it so if possible can you share a tip on how to use it well?
let svg = d3.select("svg"),
width = svg.attr("width"),
height = svg.attr("height"),
speed = 0.3,
movement = 600;
let x = 0;
let y = 50;
let w = 100;
let plane = svg.append("svg:image")
.attr("x", x)
.attr("y", y)
.attr("width", 100)
.attr("height", 100)
.attr("xlink:href", "b52js.png");
transition();
svg.on("click", function() {
var bombingx = plane.attr("x")
let bomb = svg.append("svg:image")
.attr("x", bombingx - 2.5 + 50)
.attr("y", y + 50)
.attr("width", 15)
.attr("height", 20)
.attr("xlink:href", "bomb.png");
bomb
.transition()
.duration(1200)
.attr("y", height - 10)
.ease("cubic")
.on("end", function() {
let exp = svg.append("svg:image")
.attr("x", bombingx)
.attr("y", height - 190)
.attr("height", 250)
.attr("width", 150)
.attr("xlink:href", "giphy.gif");
d3.timer(function(elapsed) {
exp.remove()
}, 1500);
bomb.remove();
})
});
Referring to my comment, lets change the xlink:href randomly on every click. Gifs of the same dimensions and short length are preferred. Or just create multiple copies of the same gif and put them in the array.
Here's a fiddle:
let svg = d3.select("svg"),
width = svg.attr("width"),
height = svg.attr("height"),
speed = 0.3,
movement = 600;
let x = 0;
let y = 50;
let w = 100;
let g = svg.append('g').attr('id', 'gg')
let plane = svg.append("svg:image")
.attr("x", x)
.attr("y", y)
.attr("width", 100)
.attr("height", 100)
.attr("xlink:href", "https://pngimg.com/uploads/plane/plane_PNG5243.png");
/* transition(); */
svg.on("click", function() {
var bombingx = plane.attr("x")
let bomb = svg.append("svg:image")
.attr("x", bombingx - 2.5 + 50)
.attr("y", y + 50)
.attr("width", 15)
.attr("height", 20)
.attr("xlink:href", "https://pngimg.com/uploads/bomb/bomb_PNG38.png");
bomb
.transition()
.duration(1200)
.attr("y", height - 10)
.ease("cubic")
.each("end", function() {
let exp = g.append("g:image")
.attr("x", bombingx)
.attr("y", height - 190)
.attr("height", 250)
.attr("width", 150)
.attr("xlink:href", function() {
// Lets create an array of gif links
let gifs = ["https://media.giphy.com/media/xA6evoIAtqSzK/giphy.gif", "https://media.giphy.com/media/rkkMc8ahub04w/giphy.gif", "https://media.giphy.com/media/oe33xf3B50fsc/giphy.gif"]
// A function to return random integers
function randInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
//randInt(3) will return 0,1 or 2.
//This number should be the number of gif links you have in your gif link array.
return gifs[randInt(3)];
});
setTimeout(function() {
exp.remove();
}, 1500);
bomb.remove();
})
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.3.13/d3.min.js"></script>
<svg width='300' height='300'></svg>

Dataset definition for D3 pie

Below is a simple D3 that loads 'dataset' array into d3 pie. Works fine. What I don't understand is why it doesn't work when I change metrics headers (like count2 instead of count). Where in the code is the piece that defines it?
I'm asking as I want to load other dataset with different header for metrics.
Thanks in advance!
//declare dataset
var dataset = [
{ label: 'Abulia', count: 10 },
{ label: 'Betelgeuse', count: 20 },
{ label: 'Cantaloupe', count: 30 },
{ label: 'Dijkstra', count: 160 }
];
//declare pie variables
var width = 360;
var height = 360;
var radius = Math.min(width, height) / 2;
//load d3 color scheme
var color = d3.scaleOrdinal(d3.schemeCategory20b);
//apply current development to <svg>
var svg = d3.select(this.domNode)
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + (width / 2) + ',' + (height / 2) + ')'); //apply transformation
var arc = d3.arc()
.innerRadius(radius * 0.9)
.outerRadius(radius);
var pie = d3.pie()
.value(function(d) { return d.count; })
.sort(null);
//create chart:
var path = svg.selectAll('path')
.data(pie(dataset))
.enter()
.append('path')
.attr('d', arc)
.attr('fill', function(d, i) {
return color(d.data.label);
});

Trying to incorporate svg circle within a d3 donut

I am trying to add a SVG circle within my d3 donut. My SVG circle displays the percentage as a fill of circle, for example, if the D3 donut is at 50%, the SVG will show 50% filled. I want to put the SVG circle within the inside of my D3 donut.
Here is my codepen for this. http://codepen.io/iamsok/pen/MwdPpx
class ProgressWheel {
constructor(patient, steps, container){
this._patient = patient;
this._steps = steps;
this.$container = $(container);
var τ = 2 * Math.PI,
width = this.$container.width(),
height = this.$container.height(),
innerRadius = Math.min(width,height)/4,
//innerRadius = (outerRadius/4)*3,
fontSize = (Math.min(width,height)/4);
var tooltip = d3.select(".tooltip");
var status = {
haveNot: 0,
taken: 1,
ignored: 2
}
var daysProgress = patient.progress
var percentComplete = Math.round(_.countBy(daysProgress)[status.taken] / daysProgress.length * 100);
var participation = 100;
var color = ["#CCC", "#FDAD42", "#EFD8B5"];
var pie = d3.layout.pie()
.value(function(d) { return 1; })
.sort(null);
var arc = d3.svg.arc();
var svg = d3.select(container).append("svg")
.attr("width", '100%')
.attr("height", '100%')
.attr('viewBox','0 0 '+Math.min(width,height) +' '+Math.min(width,height) )
.attr('preserveAspectRatio','xMinYMin')
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var innerCircle = d3.select("svg")
.append("svg")
.attr("width", 250)
.attr("height", 250);
var grad = innerCircle.append("defs")
.append("linearGradient").attr("id", "grad")
.attr("x1", "0%").attr("x2", "0%").attr("y1", "100%").attr("y2", "0%");
grad.append("stop").attr("offset", percentComplete + "%").style("stop-color", "lightblue");
grad.append("stop").attr("offset", percentComplete + "%").style("stop-color", "white");
innerCircle.append("circle")
.attr("r", 40)
.attr("cx", 70)
.attr("cy", 70)
.style("stroke", "black")
.style("fill", "url(#grad)");
var gs = svg.selectAll(".arc")
.data(pie(daysProgress))
.enter().append("g")
.attr("class", "arc");
var path = gs.append("path")
.attr("fill", function(d, i) { return color[d.data]; })
.attr("d", function(d, i, j) { return arc.innerRadius(innerRadius+(20*j)).outerRadius(innerRadius+20+(20*j))(d); })
.attr("class", function(d, i, j) { if (i>=participation && j<1) return "passed" ; })
svg.append("text")
.attr("dy", "0.5em")
.style("text-anchor", "middle")
.attr("class", "inner-circle")
.attr("fill", "#36454f")
.text(Math.round(_.countBy(daysProgress)[status.taken] / daysProgress.length * 100) + "%");
}
}
var patient = {progress: [0, 2, 2, 1, 0, 0, 0, 1, 1, 0, 1, 1, 2, 2]}
var progressWheel = new ProgressWheel(patient, 14, '.chart-container' )
Simply put the d3 donut and inner circle under the same svg so that they have the same coordinate system.
Check out here http://codepen.io/anon/pen/ojbQNE
Modified code is on codepen

Categories