I've tried the circle plot example as the following:
var x=20, y=20, r=50;
var sampleSVG = d3.select("#viz")
.append("svg")
.attr("width", 800)
.attr("height", 600);
sampleSVG.append("circle")
.style("stroke", "gray")
.style("fill", "white")
.attr("r", r)
.attr("cx", x)
.attr("cy", y);
But I want to figure out how to plot without a loop a sequence of circles from an array like:
data = [
[10,20,30],
[20,30,15],
[30,10,25]
];
Maybe this example could help?
var data = [
[10,20,30],
[20,30,15],
[30,10,25]
];
var height = 300,
width = 500;
var svg = d3.select('body').append('svg')
.attr('height', height)
.attr('width', width)
.append('g')
.attr('transform', 'translate(30, 30)');
// Bind each nested array to a group element.
// This will create 3 group elements, each of which will hold 3 circles.
var circleRow = svg.selectAll('.row')
.data(data)
.enter().append('g')
.attr('transform', function(d, i) {
return 'translate(30,' + i * 60 + ')';
});
// For each group element 3 circle elements will be appended.
// This is done by binding each element in a nested array to a
// circle element.
circleRow.selectAll('.circle')
.data(function(d, i) { return data[i]; })
.enter().append('circle')
.attr('r', function(d) { return d; })
.attr('cx', function(d, i) { return i * 60; })
.attr('cy', 0);
Live Fiddle
Related
I am new to Javascript and i am trying to draw 4 radar charts. Each chart has different title. I create TitleOptions var and call it below. but it shows everything on every chart. Can I filter the title by using ChartID? I attached my code below. and could anyone help me with this?
<script>
var w = 200;
var h = 200;
var colorscale = d3.scale.category10();
//Legend, titles
var LegendOptions = ['Try Count','Succcess Count', 'Success Rate'];
var TitleOptions=['Try/Success Count Per Skill', 'Try/Success Rate Per Skill', 'Point Get Per Skill', 'Point Lose Per Skill']
////////////////////////////////////////////
/////////// Initiate legend ////////////////
////////////////////////////////////////////
var svg = d3.select('#body')
// .selectAll('svg')
.append('svg')
.attr("width", w+300)
.attr("height", h)
//Create the title for the legend
var text = svg.append('g').append("text")
.attr("class", "title")
.attr('transform', 'translate(90,0)')
.attr("x", 30)
.attr("y", 10)
.attr("font-size", "12px")
.attr("fill", "#404040")
// .text("What % of owners use a specific service in a week");
.text(TitleOptions);
//Initiate Legend
var legend = svg.append("g")
.attr("class", "legend")
.attr("height", 100)
.attr("width", 200)
.attr('transform', 'translate(90,20)')
;
//Create colour squares
legend.selectAll('rect')
.data(LegendOptions)
.enter()
.append("rect")
.attr("x", w - 65)
.attr("y", function(d, i){ return i * 20;})
.attr("width", 10)
.attr("height", 10)
.style("fill", function(d, i){ return colorscale(i);})
;
//Create text next to squares
legend.selectAll('text')
.data(LegendOptions)
.enter()
.append("text")
.attr("x", w - 52)
.attr("y", function(d, i){ return i * 20 + 9;})
.attr("font-size", "11px")
.attr("fill", "#737373")
.text(function(d) { return d; })
;
//Options for the Radar chart, other than default
var mycfg = {
w: w,
h: h,
maxValue: 0.6,
levels: 6,
ExtraWidthX: 300
}
//Load the data and Call function to draw the Radar chart
// dynamic data creation
d3.json("<c:url value='/chart/radarChartData.do?ChartID=${ChartID}&PlayerKey=${PlayerKey}'/>", function(error, data){
RadarChart.draw("#chart", JSONconverter(data.list), mycfg);
});
</script>
Encapsulate the drawing part into a function and call it four times. Something like:
function draw(title) {
const svg = ..
..
.text(title);
}
draw('title1');
draw('title2');
// or better:
['title1', 'title2'].forEach(draw);
I am trying to add color options for my heat-map visualization. I have a predefined colors array at the beginning, and I draw rectangles like this:
plotChart.selectAll(".cell")
.data(data)
.enter().append("rect")
.attr("class", "cell")
.attr("x", function (d) { return x(d.timestamp); })
.attr("y", function (d) { return y(d.hour); })
.attr("width", function (d) { return x(d3.timeWeek.offset(d.timestamp, 1)) - x(d.timestamp); })
.attr("height", function (d) { return y(d.hour + 1) - y(d.hour); })
.attr("fill", function (d) { return colorScale(d.value); });
When I click a link in a dropdown menu, I do this:
$(".colorMenu").click(function (event) {
event.preventDefault();
// remove # from clicked link
var addressValue = $(this).attr("href").substring(1);
// get color scheme array
var newColorScheme = colorDict[addressValue];
// update color scale range
colorScale.range(newColorScheme);
// here I need to repaint with colors
});
My color scale is quantile scale, so I cannot use invert function to find values of each rectangle. I don't want to read the data again because it would be a burden, so how can I change fill colors of my rectangles?
I don't want to read the data again...
Well, you don't need to read the data again. Once the data was bound to the element, the datum remains there, unless you change/overwrite it.
So, this can simply be done with:
.attr("fill", d => colorScale(d.value));
Check this demo:
var width = 500,
height = 100;
var ranges = {};
ranges.range1 = ['#f7fbff','#deebf7','#c6dbef','#9ecae1','#6baed6','#4292c6','#2171b5','#08519c','#08306b'];
ranges.range2 = ['#fff5eb','#fee6ce','#fdd0a2','#fdae6b','#fd8d3c','#f16913','#d94801','#a63603','#7f2704'];
ranges.range3 = ['#f7fcf5','#e5f5e0','#c7e9c0','#a1d99b','#74c476','#41ab5d','#238b45','#006d2c','#00441b'];
ranges.range4 = ['#fff5f0','#fee0d2','#fcbba1','#fc9272','#fb6a4a','#ef3b2c','#cb181d','#a50f15','#67000d'];
var color = d3.scaleQuantile()
.domain([0, 15])
.range(ranges.range1);
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var data = d3.range(15);
var rects = svg.selectAll(".rects")
.data(data)
.enter()
.append("rect");
rects.attr("y", 40)
.attr("x", d => d * 25)
.attr("height", 20)
.attr("width", 20)
.attr("stroke", "gray")
.attr("fill", d => color(d));
d3.selectAll("button").on("click", function() {
color.range(ranges[this.value]);
rects.attr("fill", d => color(d))
})
<script src="https://d3js.org/d3.v4.min.js"></script>
<button value="range1">Range1</button>
<button value="range2">Range2</button>
<button value="range3">Range3</button>
<button value="range4">Range4</button>
I am trying to append images on rectangles and images locations are present in my array named arrFileUrl. Nodes of this color are white and I want to append these images on each of the generated rectangles. How can I do this?
var arrFileUrl = [], arrBrightness = [], arrPattern = [], arrSize = [];
var width = 1260,
height = 1200;
var fill = d3.scale.category10();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.csv("data/Images.csv", function(error, data) {
data.forEach(function(d) {
arrFileUrl.push(d['FingerImageName']);
});
var nodes = d3.range(arrSize.length).map(function(i) {
return {index: i};
});
var force = d3.layout.force()
.nodes(nodes)
.gravity(0.05)
.charge(-1700)
.friction(0.5)
.size([width, height])
.on("tick", tick)
.start();
var node = svg.selectAll(".node")
.data(nodes)
.enter().append("rect")
.attr("class", "node")
.attr("width", 120)
.attr("height", 160)
.style("fill", "#fff")
.style("stroke", "black")
.call(force.drag);
node.append("image")
.attr("xlink:href", "https://github.com/favicon.ico")
.attr("x", 16)
.attr("y", 16)
.attr("width", 100)
.attr("height", 120);
svg.style("opacity", 1e-6)
.transition()
.duration(1000)
.style("opacity", 1);
function tick(e) {
// Push different nodes in different directions for clustering.
var k = 6 * e.alpha;
nodes.forEach(function(o, i) {
o.y += i & 1 ? k : -k;
o.x += i & 2 ? k : -k;
});
node.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; });
}
});
Do it this way:
var node = svg.selectAll(".node")
.data(nodes)
.enter().append("g");//make groups
//add rectangle to the group
node.append("rect")
.attr("class", "node")
.attr("width", 120)
.attr("height", 160)
.style("fill", "#fff")
.style("stroke", "black")
.call(force.drag);
//add image to the group
node.append("image")
.attr("xlink:href", "https://github.com/favicon.ico")
.attr("x", 16)
.attr("y", 16)
.attr("width", 100)
.attr("height", 120);
Tick function translate the full group
function tick(e) {
// Push different nodes in different directions for clustering.
var k = 6 * e.alpha;
nodes.forEach(function(o, i) {
o.y += i & 1 ? k : -k;
o.x += i & 2 ? k : -k;
});
//do transform
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
})
}
Problem in your code:
You were appending the image inside the rectangle DOM that is reason why image is not visible.
In tick function you are moving the x and y alone and not of the image, the approach should have been moving both of them in a group and translating the group as done above.
I have a list of n associative arrays.
[{'path': 'somepath', 'relevant': [7, 8, 9]}, {'path': 'anotherpath', 'relevant': [9], ...}
Within a large SVG, I want to: a) create rects ("buckets") whose dimensions are proportional to the lengths of their 'relevant' sublists, and b) create rects ("pieces"), for each of the elements in the sublists, placed "inside" their respective buckets.
After reading Mike Bostock's response to a similar question, I'm sure that I need to use group ("g") elements to group the pieces together. I can get the code below to produce the DOM tree that I want, but I'm stumped on how to code the y values of the pieces. At the point where I need the value, D3 is iterating over the subarrays. How can I get the index of the current subarray that I'm iterating over from inside it when i no longer points to the index within the larger array?
var piece_bukcets = svg.selectAll("g.piece_bucket")
.data(files)
.enter()
.append("g")
.attr("class", "piece_bucket")
.attr("id", function (d, i) { return ("piece_bucket" + i) })
.append("rect")
.attr("y", function (d, i) { return (i * 60) + 60; })
.attr("class", "bar")
.attr("x", 50)
.attr("width", function (d) {
return 10 * d["relevant"].length;
})
.attr("height", 20)
.attr("fill", "red")
.attr("opacity", 0.2)
var pieces = svg.selectAll("g.piece_bucket")
.selectAll("rect.piece")
.data( function (d) { return d["relevant"]; })
.enter()
.append("rect")
.attr("class", "piece")
.attr("id", function (d) { return ("piece" + d) })
.attr("y", ????) // <<-- How do I get the y value of d's parent?
.attr("x", function (d, i) { return i * 10; })
.attr("height", 10)
.attr("width", 10)
.attr("fill", "black");
Is there a method on d available to find the index of the node it's currently inside? In this case, is there a method I can call on a "piece" to find the index of its parent "bucket"?
You can use the secret third argument to the function:
.attr("y", function(d, i, j) {
// j is the i of the parent
return (j * 60) + 60;
})
There's a simpler way however. You can simply translate the g element and everything you add to it will fall into place.
var piece_buckets = svg.selectAll("g.piece_bucket")
.data(files)
.enter()
.append("g")
.attr("class", "piece_bucket")
.attr("transform", function(d, i) {
return "translate(0," + ((i*60) + 60) + ")";
})
.attr("id", function (d, i) { return ("piece_bucket" + i) });
piece_buckets.append("rect")
.attr("class", "bar")
.attr("x", 50)
.attr("width", function (d) {
return 10 * d["relevant"].length;
})
.attr("height", 20)
.attr("fill", "red")
.attr("opacity", 0.2);
var pieces = piece_buckets.selectAll("rect.piece")
.data(function (d) { return d["relevant"]; })
.enter()
.append("rect")
.attr("class", "piece")
.attr("id", function (d) { return ("piece" + d); })
.attr("x", function (d, i) { return i * 10; })
.attr("height", 10)
.attr("width", 10)
.attr("fill", "black");
I am trying to create a realtime barchart that plots values over time, using d3.js
This is how I am doing it.
var dataset = [ 5, 10, 15, 20, 25 ];
var w = 1800;
var h = 500;
var barPadding = 1;
setInterval(function(){
dataset.push(Math.floor(Math.random()*51));
draw();
},1000);
function draw(){
d3.select("svg").remove();
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("rect").data(dataset)
.enter()
.append("rect")
.attr("x", function(d, i){return 12*i;})
.attr("y", function(d){return h -d*4; })
.attr("width", 11)
.attr("height", function(d) { return d * 4; })
.attr("fill", "teal")
.attr("fill", function(d) { return "rgb(0, 0, " + (d * 10) + ")";});
}
The problem is that I am redrawing the whole graph every time a new value is added to the data array.
How do I append a bar to the bar graph that is already drawn, every time a new value is added to the array, rather than redrawing it every time?
You're close, just stop redrawing the svg element. If you only need to add new elements, then that's what your draw function should do when it's called.
var dataset = [ 5, 10, 15, 20, 25 ];
var w = 1800;
var h = 300;
var barPadding = 1;
var container = d3.select("body").append("svg").attr("width", w).attr("height", h).append("g");
setInterval(function(){
dataset.push(Math.floor(Math.random()*51));
draw();
},1000);
function draw(){
container.selectAll("rect").data(dataset).enter().append("rect")
.attr("x", function(d, i){return 12*i;})
.attr("y", function(d){return h -d*4; })
.attr("width", 11)
.attr("height", function(d) { return d * 4; })
.attr("fill", "teal")
.attr("fill", function(d) { return "rgb(0, 0, " + (d * 10) + ")";});
}
http://jsfiddle.net/Wexcode/LYqfU/
Not sure what kind of effect you're looking for, but have a look at this fiddle.
The following redraw function will keep adding to the barchart so it will continue to grow to the right:
function redraw() {
var rect = svg.selectAll("rect")
.data(dataset);
rect.enter().insert("rect", "line")
.attr("x", function(d, i) { return 12*(i+1); })
.attr("y", function(d) { return h -d*4 })
.attr("width", 11)
.attr("height", function(d) { return d * 4; })
.attr("fill", "teal")
.attr("fill", function(d) { return "rgb(0, 0, " + (d * 10) + ")";})
rect.transition()
.duration(800)
.attr("x", function(d, i) { return 12*i; });
}
Borrowed from an mbostock tutorial.