Below is the code i use to draw axis and circle. I need to add rectangle based on two date and placed it under the circle. Appreciate your help. Thanks.
let scale: any = d3select.scaleTime()
//.domain([d3select.min(data), d3select.max(data)])
.domain([new Date(2020, 0, 1), new Date(2021, 0, 1)])
.range([20, axes_width - 800]);
let x_axis: any = d3select.axisBottom(scale)
.scale(scale);
axes_svg.append("g")
.call(x_axis);
var myData = [new Date(2020, 3, 1), new Date(2020, 6, 1)];
axes_svg
.selectAll('circle')
.data(myData)
.enter()
.append('circle')
.style("fill", "#69b3a2")
.attr('r', 6)
.attr('cy', 40)
.attr('cx', function (d) {
return scale(d);
});
I think your problem here is that you pass all dates in a one dimensional array. Instead of having
var myData = [new Date(2020, 3, 1), new Date(2020, 6, 1)];
like you do for the circles, you should have something like this:
var myRecData = [ {"startDate": new Date(2020, 3, 1), "endDate": new Date(2020, 6, 1)}, {"startDate": new Date(2020, 4, 1), "endDate": ....];
And then access it with d.startDate and d.endDate
EDIT:
Based on your last comment I recreated the whole code (for future questions please add a complete reproducible code, so people who want to help you can test it fast...):
<body>
<div id="mainDiv"></div>
<script>
const width = 500
const height = 500
const axes_svg = d3.select("#mainDiv").append("svg")
.attr("viewBox", [0,0, width, height])
.attr("width", "100%")
.attr("height", "100%")
let scale = d3.scaleTime()
//.domain([d3select.min(data), d3select.max(data)])
.domain([new Date(2020, 0, 1), new Date(2021, 0, 1)])
.range([20, width - 200]);
let x_axis = d3.axisBottom(scale)
.scale(scale);
axes_svg.append("g")
.call(x_axis);
var myData = [new Date(2020, 3, 1), new Date(2020, 6, 1)];
axes_svg
.selectAll('circle')
.data(myData)
.enter()
.append('circle')
.style("fill", "#69b3a2")
.attr('r', 6)
.attr('cy', 40)
.attr('cx', function (d) {
return scale(d);
});
var testdata = [{ start: new Date(2020, 1, 1), close: new Date(2020, 3, 1) }];
axes_svg
.data(testdata)
.append("rect")
.style("fill", "#00abff")
.attr("x", function (d) { return scale(d.start); })
.attr("y", 80) //distance from axis
.attr("width", function (d) { return scale(d.close) - scale(d.start); })
.attr("height", 30); //height
</script>
</body>
Related
How can I add tooltips in this piechart?
`
``
<div>
<button onclick="update(data2006)">2006</button>
<button onclick="update(data2007)">2007</button>
</div>
<select id="selectButton">
<option></option>
</select>
<div id="pie_chart"></div>
<script>
var width = 450;
height = 450;
margin = 40;
var radius = Math.min(width, height) / 2 - margin;
var tooltip = d3
.select('#pie_chart')
.append('div')
.attr('class', 'tooltip')
.style('opacity', 0);
var svg = d3
.select('#pie_chart')
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
var data2006 = {
AsiaMusic: 2,
Movies: 1,
Lifestyle: 1,
Hiphop: 1,
Entertainment: 2,
Actiongame: 1,
Music: 3,
};
var data2007 = {
AsiaMusic: 2,
Movies: 1,
Lifestyle: 1,
Hiphop: 1,
Entertainment: 2,
Actiongame: 1,
Music: 3,
};
function update(data) {
var pie = d3
.pie()
.value(function (d) {
return d.value;
})
.sort(function (a, b) {
console.log(a);
return d3.ascending(a.key, b.key);
});
var data_ready = pie(d3.entries(data));
var u = svg.selectAll('path').data(data_ready);
u.enter()
.append('path')
.merge(u)
.transition()
.duration(1000)
.attr('d', d3.arc().innerRadius(0).outerRadius(radius))
.attr('fill', function (d) {
return color(d.data.key);
})
.attr('stroke', 'white')
.style('stroke-width', '2px')
.style('opacity', 1);
u.exit().remove();
});
}
update(data2006);
</script>
I have been trying to find out the way to put some legend or tooltips in this pie chart but I don't know how to do it. I tried to tool tip and legend in this piechart but I cannot figure it out how to do it
Also, I hope I can know where I can find officical documentation of d3js.
js experts, Need your help. I have been working hard for last 2 days but could not get thru. I have COVID-19 data grouped by cumulative cases as on month end (28th Feb, 31st Mar & so on). Transformed the x,y location for 9 months as under:
var margin = {
top: 0,
right: 0,
bottom: 0,
left: 0
},
width = 700 - margin.left - margin.right,
height = 700 - margin.top - margin.bottom
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", function(d, i) {
return "translate(" + [width / 2, height / 2] + ")";
})
var parseDate = d3.timeParse("%m/%d/%y");
var formatDate = d3.timeFormat("%b-%y");
var angleScale = d3.scaleLinear().range([0, 2 * Math.PI - 0.5])
angleScale.domain([new Date(2020, 2, 1), new Date(2020, 2, 31)])
var radius = width * 0.4
var radiusScale = d3.scaleLinear().range([0, radius]).domain([0, 100000]);
var data = [];
["Afghanistan", "Albania", "Algeria", "Andorra"].forEach(function(c) {
var cum = 1;
var date = new Date(angleScale.domain()[0]);
while (date <= angleScale.domain()[1]) {
data.push({
country: c,
date: new Date(date),
// Simulate exponential growth
cum: Math.floor(cum *= (1 + Math.random() * 0.3)),
});
date.setDate(date.getDate() + 1);
}
});
var nestedData = d3.nest()
.key(function(d) {
return d.date
})
.entries(data)
nestedData.sort(function(a, b) {
return a.values - b.values
})
packableData = {
id: "root",
values: nestedData
}
var packLayout = d3.pack()
.size([100, 100])
root = d3.hierarchy({
values: nestedData
}, function(d) {
return d.values;
})
.sum(function(d) {
return d.cum;
})
.sort(function(a, b) {
return b.height - a.height || b.value - a.value;
})
date_location_data = root.leaves()
packLayout(root);
const circle_data = [new Date(2020, 2, 28), new Date(2020, 3, 31), new Date(2020, 4, 30), new Date(2020, 5, 31), new Date(2020, 6, 30), new Date(2020, 7, 31), new Date(2020, 8, 31), new Date(2020, 9, 30), new Date(2020, 10, 16)]
var Maincell = svg.selectAll('.date-group')
.data(circle_data)
.enter()
.append("g")
.attr("class", 'date-group')
.attr("transform", function(d, i) {
var x = radiusScale(90000) * Math.cos(angleScale(d)),
y = radiusScale(90000) * Math.sin(angleScale(d))
return "translate(" + [x, y] + ")";
})
var cell = Maincell
.selectAll(".node")
.data(date_location_data)
.enter().append("g")
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
})
.attr("class", "node")
cell.append("circle")
.attr("id", function(d) {
return "node-" + d.name;
})
.attr("r", function(d) {
return d.r;
})
.attr("class", "circle")
.style("fill", function(d) {
return "red";
})
.style("opacity", 1)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
The specimen csv file is as:
Country,Date,cum
Afghanistan,2/28/20,1
Albania,2/28/20,0
Algeria,2/28/20,1
Andorra,2/28/20,0
....
Yemen,10/16/20,2055
Zambia,10/16/20,15659
Zimbabwe,10/16/20,8099
I am getting the circle pack for entire data repeating at all 9 locations. I need your help in such that circle pack for that particular date (28th Feb, 31st Mar) appear at transformed location defined above.
Many thanks,
I'm currently stuck at a problem with d3.js regarding the positioning.
This is the code I have right now
var center = {name: "sun", count: 20 }; //Will have more complex data in the future
var planets = [
{name: "Mercury", count: 2},
{name: "Venus", count: 3} ,
{name: "Earth", count: 5},
{name: "Mars", count: 4},
{name: "Jupiter", count: 11},
{name: "Saturn", count: 10},
{name: "Uranus", count: 7},
{name: "Neptune", count: 8} ];
var svg = d3.select("#planet-chart").append("svg")
.attr("width", 800)
.attr("height", 800);
var circleContainer = svg.selectAll("g mySolarText")
.data(planets);
var circleContainerEnter = circleContainer.enter()
.append("g")
.attr("transform", function(d,i){
return "translate("+ i*10 +",80)"
});
var circle = circleContainerEnter.append("circle")
.attr("r", function(d){return d.count * 5} )
.attr("cx", function(d,i){return (i+1) * 30} )
.attr("cy", function(d,i){return (i+1) * 30} )
.attr("stroke","black")
.attr("fill", "white");
circleContainerEnter.append("text")
.attr("dx", function(d){return -20})
.text(function(d){
return d.name}
);
Issues I am having right now are:
Currently, I can only pass the planets variable into the circle container, but I wish to also include the sun variable into it but I do not know how. I cannot simply include the sun variable into the planets array variable because I will need to put more data into it in the future.
I am having a hard time trying to figure out how to radially position the
planets around the sun, and add connecting lines to them which I am hoping to do. I have tried looking into arc but I am stuck.
For now, the sizes of the planets are being adjusted with their count values which I just multiply with the radius. Forgive me I am just a student and wish to learn more about d3 js. If you guys can help me, I would be so much grateful. Or if you can lead me to references I would be so much indebted.
Thank you so much in advance.
#tomshanley helped me via d3 official help forums. he posted this code for the fix to the issues I was encountering.
http://blockbuilder.org/tomshanley/840d381a9ca87ab404f63adda1ba8452
const radians = 0.0174532925
var center = {name: "sun", count: 20 };
var planets = [
{name: "Mercury", count: 2},
{name: "Venus", count: 3} ,
{name: "Earth", count: 5},
{name: "Mars", count: 4},
{name: "Jupiter", count: 11},
{name: "Saturn", count: 10},
{name: "Uranus", count: 7},
{name: "Neptune", count: 8} ];
var w = 800, h = 800;
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h);
let orbitRadius = d3.scaleLinear()
.domain([0,8]) //number of planets
.range([90,w/2]) //you may need adjust this later
let angle = d3.scaleLinear()
.domain([0,9]) //number of planets + 1
.range([0,360]) //you may need adjust this later
svg.selectAll("line")
.data(planets)
.enter()
.append("line")
.attr("x1", function(d,i){
let a = angle(i);
let r = orbitRadius(i);
return xCoord = x(a, r) + (w/2)
})
.attr("y1", function(d,i){
let a = angle(i);
let r = orbitRadius(i);
return yCoord = y(a, r) + (h/2)
})
.attr("x2", (w/2))
.attr("y2", (h/2))
.style("stroke", "black")
.style("stroke-width", 2)
var circleContainer = svg.selectAll("g mySolarText")
.data(planets);
var circleContainerEnter = circleContainer.enter()
.append("g")
.attr("transform", function(d,i){
let a = angle(i);
let r = orbitRadius(i);
let xCoord = x(a, r) + (w/2)
let yCoord = y(a, r) + (h/2)
return "translate("+ xCoord +"," + yCoord + ")"
});
var circle = circleContainerEnter.append("circle")
.attr("r", function(d){return d.count * 5} )
.attr("cx", 0)
.attr("cy", 0)
.attr("stroke","black")
.attr("fill", "white");
circleContainerEnter.append("text")
.attr("dx", function(d){return -20})
.text(function(d){
return d.name}
);
function x (angle, radius) {
// change to clockwise
let a = 360 - angle
// start from 12 o'clock
return radius * Math.sin(a * radians)
}
function y (angle, radius) {
// change to clockwise
let a = 360 - angle
// start from 12 o'clock
return radius * Math.cos(a * radians)
}
again, thanks so much! :)
I'm trying to make arcs (on a geographic projection) which start at one point and grow to another. However, the transition never seems to occur - the arcs just appear fully formed! Any ideas why this is?
jsfiddle here
leeroyjenkins = function(){
var data = [{"lat": 0, "lon": 0},
{"lat": 10, "lon": 10},
{"lat": 20, "lon": 20},
{"lat": 30, "lon": 30},
{"lat": 40, "lon": 40},]
main(data);
};
var main = function(points) {
var width = 960,
height = 500;
var startpt = {"lat": 15, "lon": 15};
var projection = d3.geo.orthographic()
.scale(250)
.translate([width / 2, height / 2])
.clipAngle(90);
var arcpath = function (startpt, endpt) {
var p = d3.geo.path()
.projection(projection);
return p({type: "LineString", coordinates: [[startpt.lon, startpt.lat],
[endpt.lon, endpt.lat]]});
}
var λ = d3.scale.linear()
.domain([0, width])
.range([-180, 180]);
var φ = d3.scale.linear()
.domain([0, height])
.range([90, -90]);
var drag = d3.behavior.drag()
.origin(function() { var r = projection.rotate();
return {x: λ.invert(r[0]), y: φ.invert(r[1])}; })
.on("drag", function() {
projection.rotate([λ(d3.event.x), φ(d3.event.y)]);
svg.selectAll("path.arc").attr("d", function(d) {return arcpath(d, startpt)});
});
var tooltip = d3.select("body")
.append("div")
.attr("id", "tooltip");
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.call(drag);
svg.append("path")
.attr("id", "outline")
.datum({type: "Sphere"})
.attr("d", d3.geo.path().projection(projection));
svg.selectAll("path.arc")
.data(points)
.enter()
.append("path")
.attr("class", "arc")
.attr("d", function (d) {return arcpath(d, d)})
.transition()
.duration(1000)
.attr("d", function (d) {return arcpath(d, startpt)});
};
leeroyjenkins();
I am a total beginner to d3.js so please be kind :)
considering this jsbin example
I have the following dataset:
var dataset = [
[d3.time.hour.utc.offset(now, -5), 1, 10],
[d3.time.hour.utc.offset(now, -4), 2, 20],
[d3.time.hour.utc.offset(now, -3), 3, 30],
[d3.time.hour.utc.offset(now, -2), 4, 40],
[d3.time.hour.utc.offset(now, -1), 5, 50],
[now, 6, 60],
];
Two questions.
Does d3 provide a better approach to finding the max value for my y-axis data (all columns but the 0th, the 0th column is x-axis (time)) in my dataset array? Currently I am just looping through the entire dataset array and making a second array, excluding the first column. Perhaps there is a better datastructure other than an array I should be using for this entirely?
var data_arr = [];
for (row in dataset){
for (col=1;col < dataset[row].length; col++){
data_arr.push(dataset[row][col]);
}
}
var yScale = d3.scale.linear()
.domain([0, d3.max(data_arr)])
.range([h - padding, padding]);
Once thats resolved, I still need to determine how to graph multiple y-axis values in general! This worked fine before I needed multiple y-axis values:
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) {
return xScale(d[0]);
})
.attr("cy", function(d) {
return yScale(d[1]);
})
.attr("r", 2);
Please take a look at the graph w/ code here now for full context: http://jsbin.com/edatol/1/edit
Any help is appreciated!
I've made a couple of changes to your example and you can see the results at http://jsbin.com/edatol/2/edit.
First, I modified your data a little bit. This is mostly just a style thing, but I find it's easier to work with objects instead of arrays:
//Static dataset
var dataset = [
{x: d3.time.hour.utc.offset(now, -5), y1: 1, y2: 10},
{x: d3.time.hour.utc.offset(now, -4), y1: 2, y2: 20},
{x: d3.time.hour.utc.offset(now, -3), y1: 3, y2: 30},
{x: d3.time.hour.utc.offset(now, -2), y1: 4, y2: 40},
{x: d3.time.hour.utc.offset(now, -1), y1: 5, y2: 50},
{x: now, y1: 6, y2: 60},
];
Then you can find your domains and ranges like this:
var xDomain = d3.extent(dataset, function(i) { return i.x; });
var maxY = d3.max(dataset, function(i) { return Math.max(i.y1, i.y2); });
Then to add multiple y-values, you just have to append an additional circle with the appropriate values. I gave them different classes so that you can use that to select them if you want to do transitions or updates later on.
//Create circles
svg.selectAll(".y1")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) { return xScale(d.x); })
.attr("cy", function(d) { return yScale(d.y1); })
.attr("class", "y1")
.attr("r", 2);
//Create circles
svg.selectAll(".y2")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) { return xScale(d.x); })
.attr("cy", function(d) { return yScale(d.y2); })
.attr("class", "y2")
.attr("r", 2);