Add styling to a column of a D3 heatmap programmatically - javascript

I've got a basic heatmap from here.
Now I want to be able to highlight a whole column of the plot, like drawing a rectangle around all values (Could also be something simpler):
I'm also using react to keep track of which column should be highlighted.
Therefore I need to be able to change this highlighting programmatically without any mouse actions.
Does anyone know how to style a whole column without using mouse events? is that even possible?

You can append a highlight element when you're creating your heatmap, and update the position/opacity of the highlight element when your variable changes.
Note that you will also need to store the d3 scale function in a variable.
const svg = "however you're selecting the svg here"
svg.append('rect')
.attr("id", "highlight")
.attr("width", xScale.bandwidth())
.attr("height", 'height of the heatmap')
.attr("x", 0)
.attr("y", 0)
.attr("opacity", 0)
.attr("stroke", '#ffffff')
.attr("stroke-width", 2)
When the variable is changed programatically, use d3 to select that element and update its position
function changePosition(column) {
const svg = "however you're selecting the svg here"
const xScale = "that variable where you put the xScale function when you appended your heatmap"
svg.select("#highlight")
.attr("x", xScale(column))
.attr("opacity", 1)
}

I solved this by using mouseover. You can get coordinates and size of a single rect by using:
var mouseover = function(event, d) {
x_coordinate = this.x.animVal.value
y_coordinate = his.y.animVal.value
height_of_rect = this.height.animVal.value
width_of_rect = this.width.animVal.value
}
To draw a line over a heatmap you need to apppend that line on its parent component. I used svg:
const drawLine = function(x, rectWidth) {
svg.append('line')
.style("stroke", "black")
.style("stroke-width", 2)
.attr("x1", x)
.attr("y1", 0)
.attr("x2", x)
.attr("y2", height);
svg.append('line')
.style("stroke", "black")
.style("stroke-width", 2)
.attr("x1", x+rectWidth)
.attr("y1", 0)
.attr("x2", x+rectWidth)
.attr("y2", height);
svg.append('line')
.style("stroke", "black")
.style("stroke-width", 2)
.attr("x1", x)
.attr("y1", 0)
.attr("x2", x+rectWidth)
.attr("y2", 0);
svg.append('line')
.style("stroke", "black")
.style("stroke-width", 2)
.attr("x1", x)
.attr("y1", height)
.attr("x2", x+rectWidth)
.attr("y2", height);
}
drawLine function will vertically highlight a column of a rect and it is placed inside of mouseover. Here x is x coordinate, rectWidth is width of one rect component and height is heat map height. To remove highlight put in mouseleave function:
d3.selectAll("line").remove()

Related

D3js dynamically attach circles into a line with drag and drop

I am very new to d3js v3 and I was trying out a new program where there are lines and the according to the data, circles get embedded into them.
This is what I have so far.
var width = 500,
height = 500;
var animals = ['dog', 'cat', 'bat'];
var fruits = ['apple', 'banana'];
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var line1 = svg.append("line")
.attr("x1", 350)
.attr("y1", 5)
.attr("x2", 350)
.attr("y2", 350)
.attr("stroke-width", 2)
.attr("stroke", "black");
var line2 = svg.append("line")
.attr("x1", 80)
.attr("y1", 5)
.attr("x2", 100)
.attr("y2", 350)
.attr("stroke-width", 2)
.attr("stroke", "black");
var animal_scale = d3.scale.ordinal()
.domain(animals)
.rangePoints([5, 350],.2);
var fruit_scale = d3.scale.ordinal()
.domain(fruits)
.rangePoints([5, 350],.2);
var animal_circles = svg.selectAll('circle')
.data(animals)
.enter()
.append('circle')
.attr('cx', function(d) {
// is there a way to calc it automatically according to line 1
})
.attr('cy', function(d) {
return animal_scale(d);
})
.attr('id', function(d) {
return d;
})
.attr('r', 20);
var fruits_circles = svg.selectAll('circle')
.data(fruits)
.enter()
.append('circle')
.attr('cx', function(d) {
// is there a way to calc it automatically according to line 2
})
.attr('cy', function(d) {
return fruit_scale(d);
})
.attr('id', function(d) {
return d;
})
.attr('r', 20);
<!DOCTYPE html>
<html>
<meta charset=utf-8>
<head>
<title></title>
</head>
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
</body>
</html>
I looked at some sources and being new, its kinda hard to understand most of it. I eventually want to be able to move and drag the circles between lines at the end of the project.There are some issues with the current code, as it does not display the second set of circles too.
Could someone please help me understand further how to do this. It would be a great way for me to learn.
You can select objects by class name and set data. Here is my fast solution for drag-n-drop: jsFiddle. You can modify drag function to add limits to cx position

D3 - How can I show/hide a text element when hovering a circle element created from different attributes of the same data entry?

I have some data with 2 attributes: colour and value
I use the D3 enter selection to create circle elements, and append them to the body of the page. Their fill colour is determined by the "colour" attribute.
Then, I append text elements to the page. The text contents are determined by the "value" attribute.
Here is what I am working with:
// Set up svg element
var svg = d3.select("body")
.append("svg")
.attr("width", 300)
.attr("height", 300)
.style("background", "lightblue");
var dataset = [
{"colour":"red", "value":"First set of text"},
{"colour":"green", "value":"Second attempt"},
{"colour":"blue", "value":"Third and final!"}
];
// Create circles from the data
// On mouseover, give them a border (remove on mouseout)
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("r", 40)
.attr("cy", function(d, i) { return i*80 + 40; })
.attr("cx", 50)
.style("fill", function(d) {return d.colour;})
// HERE
// Can I somehow show and hide the text component that is
// associated with this circle when the circle is hovered, rather
// than the text itself?
.on("mouseover", function(d) {
d3.select(this).style("stroke", "black")
.style("stroke-width", 2)
})
.on("mouseout", function(d) {d3.select(this).style("stroke", "none")});
// Now add the text for each circle
// Same thing with mouseover and mouseout
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.attr("text-anchor", "middle")
.attr("y", function(d, i) { return i*80 + 40; })
.attr("x", 50)
.style("opacity", 0)
.on("mouseover", function(d) {d3.select(this).style("opacity", 1)})
.on("mouseout", function(d) {d3.select(this).style("opacity", 0)})
.text(function(d) { return d.value;});
I would like for the text to be hidden, until the associated circle is hovered over. How can I connect the text element with a particular circle, so that I can toggle whether the text is shown by hovering over the circle?
This fiddle below is an outline of what I am trying to do, and what I have got so far. I have the text showing up only when hovered, but not when the circle is hovered.
https://jsfiddle.net/aj4zpn6z/
There are several ways for achieving this. Since both circles and texts use the same dataset, my solution uses filter.
First, let's name the variables for the texts and circles:
var circles = svg.selectAll("circle")
//etc...
var texts = svg.selectAll("text")
//etc...
Then, inside the circles mouseover function, we filter the texts that have the same colour attribute:
.on("mouseover", function(d){
d3.select(this).style("stroke", "black").style("stroke-width", 2);
var tempTexts = texts.filter(function(e){
return e.colour === d.colour
});
tempTexts.style("opacity", 1);
});
This is your updated fiddle: https://jsfiddle.net/wxh95e9u/

How to modify D3js graph depending on change of value?

I have a graph which I build using d3js and I want to draw red line or other kind of indicator on the graph to indicate when the graphs blue line's value hits 0(in this case its 6th of june). The example of a graph http://jsfiddle.net/wRDXt/81/
My starting point is:
if(dataset.value = 0){
//draw line
}
But I have no clue where to go from here. Any help would be greatly appreciated.
There are a few different ways to do this. One way would be to filter the dataset to create a new array that only had the entries with a value of 0.
var zeroData = dataset.filter(function(d){
if (d.value == 0) return d;
});
This new array could be used to draw lines at each zero point with a separate svg.selectAll(".redlines") using the new array as the data.
// plot lines
svg.selectAll(".redline")
.data(zeroData)
.enter()
.append("line")
.attr("class", "redline")
.attr("x1", function(d) { return xScale(d.date); })
.attr("x2", function(d) { return xScale(d.date); })
.attr("y1", 0)
.attr("y2", 250)
.style("stroke", "#ff0000");
http://jsfiddle.net/wRDXt/85/
Another way is to start by appending a "g" element for each data point instead of a circle then adding a circle and a line for each point. Styling can be used to make only the value == 0 points visible.
// plot data points as g elements
var datapts = svg.selectAll(".data-point")
.data(dataset);
var dataptsEnter = datapts
.enter()
.append("g")
.attr("class", "data-point")
.attr("transform", function(d) {
return "translate(" + xScale(d.date) + "," + yScale(d.value) + ")";
});
dataptsEnter
.append("circle")
.attr("class", "data-point-circle")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", 5);
dataptsEnter
.append("line")
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", 0)
.attr("y2", -50)
.style("stroke", "#ff0000")
.style("opacity", function(d) {
if (d.value == 0) return 1;
return 0;
});
http://jsfiddle.net/wRDXt/82/
In the second approach, there is a red line for each datapoint but opacity is used to make only the zero value visible.
The first approach is the one I'd use.

Drawing a straight line D3.js

I feel like this has a really simple solution but I have been struggling for a while now. I have a d3 line graph and I want to draw a line on the graph that represents the average of the data. I am trying to do this with the line attribute but I keep getting the same errors. The errors I keep getting are:
"Unexpected value NaN parsing y1 attribute.
Unexpected value NaN parsing y2 attribute. "
Here is the code where I try to draw the line:
student_av_data.forEach(function(d) {
d.student_average = +d.student_average;
});
svg.append("line")
.style("stroke", "orange")
.style("opacity", 1)
.attr("x1", 0)
.attr("y1", y0(function(d) {return y0(d.student_average);}))
.attr("x2", width)
.attr("y2", y0(function(d) {return y0(d.student_average);}));
function(d) {return y0(d.student_average);} should just be a number and that should work in the y0 arguments.
Not really sure what is wrong. Any suggestions would be appreciated. Thanks!
EDIT:
As a reference, this works and is basically what I am trying to do but with d.student_average in y0():
svg.append("line")
.style("stroke", "black")
.style("opacity", .2)
.attr("x1", 0)
.attr("y1", y0(24))
.attr("x2", width)
.attr("y2", y0(24));
Try replacing with this:
svg.append("line")
.style("stroke", "orange")
.style("opacity", 1)
.data(student_av_data)
.attr("x1", 0)
.attr("y1", function(d) {return y0(d.student_average);})
.attr("x2", width)
.attr("y2", function(d) {return y0(d.student_average);});
which will take d from the data elem

Incorrect text in x.ticks( ) using d3.js

In one bar chart I am working on I have created parallel ticks to x Axis in order to give references to the viewer.
The problem is in text I have added. The text is ordered inversely as it should and I have no idea how to keep correct order.
I understand the problem is here:
chart.selectAll("line")
.data(y.ticks(4))
.enter().append("line")
.attr("x1", 60)
.attr("x2", (barWidth + 50) * data.length)
.attr("y1", y)
.attr("y2", y)
.style("stroke", "#ccc");
chart.selectAll(".rule")
.data(y.ticks(4))
.enter().append("text")
.attr("class", "rule")
.attr("x", 30)
.attr("y", y)
.attr("text-anchor", "middle")
.text(String);
But I don't know how to fix it.
Here is the code in jsFiddle.
Thanks in advance.
The problem is that the y coordinates are counted from the top and not the bottom, so you need to reverse all the values. The updated jsfiddle is here; the relevant changes are below.
var y = d3.scale.linear()
.domain([0, d3.max(data, function(datum) { return datum.value; })])
.rangeRound([height-20, 0]);
Reversed the values in rangeRound such that the lowest value is mapped to the top.
chart.selectAll("rect")
.data(data)
.enter()
.append("svg:rect")
.attr("x", function(datum, index) { return x(index); })
.attr("y", function(datum) { return y(datum.value); })
.attr("height", function(datum) { return height - y(datum.value); })
...
Changed how y and height are computed accordingly.

Categories