Need to have only integers in x-axis - d3 - javascript

I am facing issue in order to have integer values in x-axis(graph shown in above link). x-axis should be divided as 1,2,3 but it is getting divided as 0.0, 0.2, 0.4, 0.6,...,2.8 ,3.0 .
I have the following code:
private initSvg() {
d3.select("#svg1")
.attr("width", document.getElementById("svg1").parentElement.offsetWidth - 300 - this.margin.left - this.margin.right)
.attr("height", (document.getElementById("svg1").parentElement.offsetWidth / 3))
this.svg = d3.select('#svg1');
this.width = document.getElementById("svg1").parentElement.offsetWidth - 300 - this.margin.left - this.margin.right;
// this.height = +this.svg.attr("height") - this.margin.top - this.margin.bottom;
this.height = (document.getElementById("svg1").parentElement.offsetHeight - 100)
// console.log(this.width, this.height)
this.g = this.svg.append("g")
.attr("transform", "translate(" + (this.margin.left + 100) + "," + this.margin.top + ")");
}
private initAxis() {
this.y = d3Scale.scaleBand().rangeRound([0, this.height]).padding(0.1);
this.x = d3Scale.scaleLinear().rangeRound([this.width, 0]);
this.y.domain(this.STATISTICS.map((d) => d.qua));
this.x.domain([d3Array.max(this.STATISTICS, (d) => d.value + 2), 0]);
}
private drawAxis() {
this.g.append("g")
.attr("class", "x")
.attr("transform", "translate(0," + this.height + ")")
.call(d3Axis.axisBottom(this.x).tickSize(-this.height))
.selectAll("text")
.attr("dy", "1em")
.selectAll("path")
.attr("class", "opac")
.style("stroke-opacity", "0")
this.g.append("g")
.attr("class", "y")
// .attr("transform", "translate(0," + (- ((document.getElementById("svg1").parentElement.offsetWidth / 3) * 10) / 100) + ")")
.call(d3Axis.axisLeft(this.y).tickSize(-this.width))
.append("text")
.attr("class", "axis-title")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
}
private drawBars() {
var i = 0;
var trans = this.g.selectAll(".bar")
.data(this.STATISTICS)
.enter().append("rect")
.style("fill", (d: any) => { i++; return this.colorrange[i]; })
.attr("y", (d) => this.y(d.qua))
.attr("height", (this.y.bandwidth()))
// .attr("height", 60)
trans.transition()
.duration(2000)
.delay(function (d, i) {
return i * 50;
})
.attr("x", (d) => 0 /*this.x(d.AverageValue)*/)
.attr("width", (d) => /* this.height -*/ this.x((d.value == "" ? 0 : d.value)))
}
private drawLabel() {
this.g.selectAll('.label')
.data(this.STATISTICS)
.enter().append('text')
.attr('class', (d) => this.x(d.value) < 50 ? 'label bar-value-2' : 'label bar-value')
.style('font-size', (d) => (this.y.bandwidth() < 30 ? this.y.bandwidth() * 0.7 : 14) + 'px')
// .attr('x', (d) => this.x(d.AverageValue) < 40 ? 40 : this.x(d.AverageValue) - 7)
.attr('x', 100)
.attr('y', (d) => this.y(d.qua) + this.y.bandwidth() * 0.5 + 6);
}
And I am calling all this in a different method to get result:
setTimeout(() => {
this.initSvg()
this.initAxis()
this.drawAxis()
this.drawBars()
this.drawLabel()
}, 500);
Note:
STATISTICS = [
{qua: "Yes", value: 1}
{qua: "No", value: 1},
];
Tried to change something in x-domain but it didn't worked. Honestly, I am new to d3 and that's why don't know where to make changes.
Need further assistance.
Thanks.

Related

Accessing data properties for d3 v4 stacked bar chart

I have been working on creating a stacked bar chart using d3 v4 but with a JSON file input. This question is a continuation of a past question:Converting data structure of a JSON array for stacked bar chart.
My data array consists of numerous objects (each object is a hospital and has values such as the count of males, females, and categories).
Based from this, how can I access the individual properties of my data array and populate it accordingly? This means that when I hover over a certain bar, the gender type, count, percentage, and categories change.
**EDIT: I changed my post to show a better understanding about my data structure. Also, I do not think the provided link doesn't solve my problem that I have. I did used that method and it didn't work.
**EDIT2: Added more of my JavaScript code
The data structure is in a JSON format (An array of objects) looks like:
[{
"hospitalName": "hospital1",
"Diseases of the Circulatory System": 1,
"Diseases of the Digestive System": 1,
"Diseases of the Nervous System & Sense Organs": 2,
"Diseases of the Respiratory System": 1,
"Infectious and Parasitic Diseases": 278,
"Injury and Poisoning": 9,
"Mental Disorders": 4,
"Neoplasms": 4,
"No Diagnosis Code in Medical Record": 10,
"Perinatal Period Conditions": 1,
"Females": 223,
"Males": 88,
"Unknown": 0,
"count": 311
},
{
"hospitalName": "hospital2",
"No Diagnosis Code in Medical Record": 1,
"Females": 0,
"Males": 1,
"Unknown": 0,
"count": 1
},
{
"hospitalName": "hospital3",
"Neoplasms": 5,
"Females": 0,
"Males": 2,
"Unknown": 3,
"count": 5
}]
This is my current stacked bar chart.
The output of the tooltip would look something like this:
*My JavaScript code:
var svg = d3.select("svg"),
margin = { top: 20, right: 20, bottom: 120, left: 40 },
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.15)
.align(0.1);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var z = d3.scaleOrdinal()
.range(["#a95e65", "#5eaaa2", "#6b486b"]);
d3.json("hospitals2.json", function(error, data) {
if (error) throw error;
var newData = {};
data.forEach(element => {
var name = element.hospitalName;
var hospital = newData[name];
if (!hospital) {
hospital = { hospitalName: name, Females: 0, Males: 0, Unknown: 0, count: 0 };
newData[name] = hospital;
}
hospital[element.category] = +element.count;
hospital.Females += +element.Females;
hospital.Males += +element.Males;
hospital.Unknown += +element.Unknown;
hospital.count += +element.count;
});
data = [];
for (const key in newData) {
if (newData.hasOwnProperty(key)) {
data.push(newData[key]);
}
}
console.log(data);
var columns = d3.keys(data[0]);
var keys = columns.slice(1, 4);
console.log(keys);
data.sort(function(a, b) { return b.total - a.total; });
x.domain(data.map(function(d, i) { return i; }));
y.domain([0, d3.max(data, function(d) { return d.count; })]).nice();
z.domain(keys);
g.append("g")
.selectAll("g")
.data(d3.stack().keys(keys)(data))
.enter().append("g")
.attr("fill", function(d) { return z(d.key); })
.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d, i) { return x(i); })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.attr("width", x.bandwidth())
.on("mousemove", function(d) {
var xPosition = d3.mouse(this)[0] - 60;
var yPosition = d3.mouse(this)[1] - 60;
tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
tooltip.select(".count").html("Count: " + (d[1] - d[0]) + "<br>")
tooltip.select(".percent").html("(" + (Math.round((d[1] - d[0]) / d.data.count * 100)) + "%" + ")")
})
.on("mouseover", function() {
tooltip.style("display", "inline");
})
.on("mouseout", function() {
tooltip.style("display", "none");
});
g.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).tickFormat(function(d, i) { return data[i].MTFID }))
.selectAll("text")
.attr("x", -11)
.attr("y", 7)
.attr("dy", ".35em")
.attr("transform", "rotate(290)")
.style("text-anchor", "end");
g.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(y).ticks(null, "s"))
//.call(d3.axisLeft(y).tickFormat(d => Math.round(d * 100 / d3.max(data, function(d) { return d.count })) + "%"))
.append("text")
.attr("x", 2)
.attr("y", y(y.ticks().pop()) + 0.5)
.attr("dy", "0.32em")
.attr("fill", "#000")
.attr("font-family", "Montserrat, sans-serif")
.attr("font-size", "13px")
.attr("text-anchor", "start")
.text("Population");
var legend = g.append("g")
.attr("class", "legend")
.attr("text-anchor", "end")
.selectAll("g")
.data(keys.slice().reverse())
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 19)
.attr("width", 19)
.attr("height", 19)
.attr("fill", z);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9.5)
.attr("dy", "0.32em")
.text(function(d) { return d; });
var tooltip = svg.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("rect")
.attr("width", 100)
.attr("height", 105)
.attr("fill", "#DCDCDC")
tooltip.append("text")
.attr("class", "count")
.attr("x", 50)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
tooltip.append("text")
.attr("class", "percent")
.attr("x", 50)
.attr("dy", "2em")
.style("text-anchor", "middle")
tooltip.append("text")
.attr("class", "category")
});

Display more data in d3 v4 grouped bar chart

I am using d3 V4 with angular4 to develop grouped bar chart. Here is my code.
imports;
let d3: any = D3;
#Component({
selector: 'grouped-bar-chart',
template: '<ng-content></ng-content>'
})
export class GroupedBarChartComponent {
#Input('chartConfig') config: GroupedBarChartConfigModel;
htmlElement: HTMLElement;
host;
width;
height;
innerWidth;
innerHeight;
margin;
x0Scale;
x1Scale;
yScale;
zScale; // color scale
svg;
tooltip;
breakPoint; // used to check if we are on small screens or not
keys;
dataset;
disabledSeries;
constructor(private element: ElementRef) {
if (this.config == null) {
this.config = new GroupedBarChartConfigModel();
}
this.htmlElement = this.element.nativeElement;
this.host = d3.select(this.element.nativeElement);
this.innerWidth = window.innerWidth;
this.innerHeight = window.innerHeight;
this.breakPoint = 768;
this.keys = []; // this holds bars contains in one cluster
this.disabledSeries = [];
this.margin = this.config.margin;
this.ngOnChanges();
}
#HostListener('window:resize', ['$event'])
onResize(event) {
this.ngOnChanges();
}
/**
* Every time the #Input is updated, rebuild the chart
**/
ngOnChanges(): void {
if (!this.config || !this.host || this.config.dataset.length === 0) return;
this.init();
this.setup();
this.buildSVG();
this.scaleAxises();
this.drawXAxis();
this.drawYAxis();
this.populate();
this.drawLegend();
}
/**
* Initialize chart dataset
*/
init(): void {
let data;
this.dataset = data;
}
/**
* Basically we get the window size and build the container configs
* also we create the xScale, yScale & zScale(color scale) ranges depending on calculations
**/
setup(): void {
this.keys = [];
this.width = this.config.width - this.margin.left - this.margin.right;
this.height = this.config.height - this.margin.top - this.margin.bottom;
// determine the geometry of the bars and spaces the individual bars within a cluster
this.x0Scale = d3.scaleBand().rangeRound([0, this.width]).paddingInner(0.01);
// spaces the different clusters of bars across the page
this.x1Scale = d3.scaleBand().padding(0.05);
this.yScale = d3.scaleLinear().rangeRound([this.height, 0]);
this.zScale = d3.scaleOrdinal()
.range(GraphColorPalette.PALETTE);
this.dataset[0].values.map((d: any) => {
if (this.keys.indexOf(d.key) === -1) {
this.keys.push(d.key);
}
});
}
/**
* build SVG element using configurations
**/
buildSVG(): void {
this.host.html('');
this.svg = this.host.append('svg')
.attr('width', this.width + this.margin.left + this.margin.right)
.attr('height', this.height + this.margin.top + this.margin.bottom)
.append('g')
.attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');
this.svg.append('rect')
.attr('x', 0)
.attr('y', 0)
.attr('width', this.width)
.attr('height', this.height)
.style('fill', '#eee')
.append('g')
.attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');
}
/**
**/
scaleAxises(): void {
this.x0Scale.domain(this.dataset.map((d: any) => {
return d.key;
}));
this.x1Scale.domain(this.keys).rangeRound([0, this.x0Scale.bandwidth()]);
let maxDomain = d3.max(this.dataset, (d: any) => {
return d3.max(this.keys, (key: any, i: number) => {
return d.values[i].value;
})
});
this.yScale.domain([0, maxDomain + 1]).nice();
}
/**
* Populate the chart using
**/
populate(): void {
this.drawColumns(this.x1Scale, this.keys, this.dataset);
this.createToolTips();
// add a title to the graph
this.svg.append("text")
.attr("x", (this.width / 2))
.attr("y", 0 - (this.margin.top / 2))
.attr("text-anchor", this.config.titlePosition)
.attr("class", "title")
.style("font-size", "18px")
.style("text-decoration", "underline")
.text(this.config.title);
}
drawColumns(x1Scale: any, keys: Array<string>, data: any): void {
this.svg.append("g")
.attr("class", "chart-layer")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", (d: any) => {
return "translate(" + this.x0Scale(d.key) + ",0)";
})
.selectAll("rect")
.data((d: any) => {
return keys.map((key, i) => {
return {id: d.key, key: key, name: d.name, value: d.values[i].value};
});
})
.enter().append("rect")
.attr("fill", (d) => {
return this.zScale(d.key);
})
.attr("class", "bar")
.style('cursor', 'pointer')
.on("mousemove", (d: any) => {
this.showTooltip(d);
})
.on("mouseout", () => {
this.hideTooltip();
})
.attr("x", (d: any) => {
return x1Scale(d.key);
})
.attr("y", this.height)
.attr("width", x1Scale.bandwidth())
.transition()
.ease(d3.easeLinear)
.duration(1000)
.delay((d, i) => {
return i * 50;
})
.attr("y", (d) => {
return this.yScale(d.value);
})
.attr("height", (d) => {
return Math.abs(this.height - this.yScale(d.value));
})
}
createToolTips(): void {
// Define the div for the tooltip
this.tooltip = d3.select("body")
.append("div")
.attr("class", "tooltip");
}
showTooltip(d: any) {
// Show tooltip
this.tooltip.transition()
.duration(200)
.style("display", "inline");
this.tooltip
.html(d.id + "<br>" + d.name + "<br>" + d.key + ": " + d.value)
.style("left", d3.event.pageX + 10 + "px")
.style("top", d3.event.pageY - 25 + "px")
.style("position", "absolute")
.style("width", "auto")
.style("height", "auto")
.style("border", "0 none")
.style("border-radius", "8px")
.style("border-shadow", "-3px 3px 15px #888888")
.style("padding", "5px")
.style("text-align", "center")
.style("font", "12px sans-serif")
.style("background", "none repeat scroll 0 0 #fff")
.style('color', '#000');
}
hideTooltip() {
this.tooltip.transition()
.duration(500)
.style("display", "none");
}
/**
* Create X-axis
**/
drawXAxis(): void {
this.svg.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + this.height + ")")
.call(d3.axisBottom(this.x0Scale)
.tickSize(0)
.tickPadding(6));
// Add title to x axis
this.svg.append("text")
.attr("class", "axis-x--label")
.attr("dy", "0.32em")
.attr("fill", "#000")
.attr("font-weight", "bold")
.attr("text-anchor", this.config.xLabelPosition)
.attr("transform", () => {
switch (this.config.xLabelPosition) {
case ChartLabelPositions.START:
return "translate(" + (0) + "," + (this.height - (this.margin.bottom / 10) + 40) + ")";
case ChartLabelPositions.MIDDLE:
return "translate(" + (this.width / 2) + "," + (this.height - (this.margin.bottom / 10) + 40) + ")";
case ChartLabelPositions.END:
return "translate(" + (this.width) + "," + (this.height - (this.margin.bottom / 10) + 40) + ")";
default:
//do nothing
break;
}
}
)
.text(this.config.xLabel);
}
/**
* Create Y-axis
**/
drawYAxis(): void {
this.svg.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(this.yScale).tickSize(-this.width).ticks(null, "s"))
.append("text")
.attr("x", 2)
.attr("y", this.yScale(this.yScale.ticks().pop()) + 0.5)
.attr("dy", "0.32em")
.attr("fill", "#000")
.attr("font-weight", "bold")
.attr("text-anchor", this.config.yLabelPosition)
.text(this.config.yLabel);
}
}
I have more than 500 data to display in the chart. The problem is if there are more data then the bars are not showing. But if I hide some series on legend click then bars will be showing. How can I show more data in this graph.
Any suggestions are appreciated.
Thank You!

How can I make a SVG group follow a gradient in a circle

I'm trying to animate a circle being drained, with is working well. However I want a "marker" to follow the drain level, and I'm having difficulties understanding how to do this.
I already made a example embedded in this post, where the circle fill animates, and the number animates.
The final state can be seen here:
The issue is; that I want to place the "XXX used" marker based on the drain percentage. But I have had little luck figuring out how to achieve this.
So it has to move up and down, but also left and right depending on the percentage.
My code is as follows:
const usedAmount = 200;
const totalAmount = 400;
const radius = 120;
const valuePercent = (usedAmount / totalAmount) * 100;
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
let grad = svg
.append("defs")
.append("linearGradient")
.attr("id", "grad")
.attr("x1", "0%")
.attr("x2", "0%")
.attr("y1", "0%")
.attr("y2", "0%");
grad.append("stop").attr("offset", "1%").style("stop-color", '#000');
grad.append("stop").attr("offset", "1%").style("stop-color", '#ccc');
let arc = d3.arc()
.innerRadius( radius - 40 )
.outerRadius( radius )
.endAngle(2 * Math.PI)
.startAngle(0 * Math.PI);
let cutout = svg.select('defs')
.append('clipPath')
.attr('clip-rule', 'evenodd')
.attr('id', 'cutout')
.append("path")
.attr('transform', 'translate(' + radius + ',' + radius + ')')
.attr('d', arc)
.attr('clip-rule', 'evenodd')
.attr('fill', '#ccc');
svg.append("circle")
.attr("cx", radius)
.attr("cy", radius)
.attr("r", radius)
.attr("clip-path", "url(#cutout)") // Apply the mask
.attr("fill", "url(#grad)");
grad
.transition()
.duration(3000)
.ease(d3.easeQuad)
.delay(300)
.attr("y1", valuePercent + 1 + '%');
var marker = svg.append('g')
.attr('class', 'gauge__fillup__follow')
.attr("transform", "translate(" + 200 + "," + 50 + ")");
marker.append("rect")
.attr("x", 10)
.attr("y", 6)
.attr("fill", "#000")
.attr("width", 35)
.attr("height", 3);
marker.append('svg:text')
.attr('class', 'label')
.attr('z-index', '4')
.attr('x', 50)
.attr('y', 0)
.attr('dy', 12)
.attr('text-anchor', 'left')
.datum({textContent: ''})
.text(200)
.transition()
.duration(3000)
.delay(300)
.ease(d3.easeQuad)
.tween("text", function(d) {
const i = d3.interpolate(0, this.textContent, d);
return (t) => {
d3.select(this).text(Math.round(i(t)));
};
});
marker.append('svg:text')
.attr('class', 'label')
.attr('color', 'white')
.attr('z-index', '4')
.attr('x', 50)
.attr('y', 16)
.attr('dy', 12)
.attr('text-anchor', 'left')
.text('used');
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js"></script>
<svg height="500" width="500"></svg>
Ended up with this result: https://codepen.io/Saturate/pen/BROzBe
You just have to translate the group down by the same amount:
marker.transition()
.duration(3000)
.ease(d3.easeQuad)
.delay(300)
.attr("transform", "translate(" + 220 + ","
+ ((usedAmount / totalAmount) * (radius * 2)) + ")");
Here is the demo:
const usedAmount = 200;
const totalAmount = 400;
const radius = 120;
const valuePercent = (usedAmount / totalAmount) * 100;
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
let grad = svg
.append("defs")
.append("linearGradient")
.attr("id", "grad")
.attr("x1", "0%")
.attr("x2", "0%")
.attr("y1", "0%")
.attr("y2", "0%");
grad.append("stop").attr("offset", "1%").style("stop-color", '#000');
grad.append("stop").attr("offset", "1%").style("stop-color", '#ccc');
let arc = d3.arc()
.innerRadius(radius - 40)
.outerRadius(radius)
.endAngle(2 * Math.PI)
.startAngle(0 * Math.PI);
let cutout = svg.select('defs')
.append('clipPath')
.attr('clip-rule', 'evenodd')
.attr('id', 'cutout')
.append("path")
.attr('transform', 'translate(' + radius + ',' + radius + ')')
.attr('d', arc)
.attr('clip-rule', 'evenodd')
.attr('fill', '#ccc');
svg.append("circle")
.attr("cx", radius)
.attr("cy", radius)
.attr("r", radius)
.attr("clip-path", "url(#cutout)") // Apply the mask
.attr("fill", "url(#grad)");
grad
.transition()
.duration(3000)
.ease(d3.easeQuad)
.delay(300)
.attr("y1", valuePercent + 1 + '%');
var marker = svg.append('g')
.attr('class', 'gauge__fillup__follow')
.attr("transform", "translate(" + 220 + "," + 0 + ")");
marker.append("rect")
.attr("x", 10)
.attr("y", 1)
.attr("fill", "#000")
.attr("width", 35)
.attr("height", 3);
marker.append('svg:text')
.attr('class', 'label')
.attr('z-index', '4')
.attr('x', 50)
.attr('y', -6)
.attr('dy', 12)
.attr('text-anchor', 'left')
.datum({
textContent: ''
})
.text(200)
.transition()
.duration(3000)
.delay(300)
.ease(d3.easeQuad)
.tween("text", function(d) {
const i = d3.interpolate(0, this.textContent, d);
return (t) => {
d3.select(this).text(Math.round(i(t)));
};
});
marker.append('svg:text')
.attr('class', 'label')
.attr('color', 'white')
.attr('z-index', '4')
.attr('x', 50)
.attr('y', 10)
.attr('dy', 12)
.attr('text-anchor', 'left')
.text('used');
marker.transition()
.duration(3000)
.ease(d3.easeQuad)
.delay(300)
.attr("transform", "translate(" + 220 + "," + ((usedAmount / totalAmount )*(radius*2)) + ")");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js"></script>
<svg height="500" width="500"></svg>
EDIT: Here is the translation with attrTween, going from up to down and left to right:
marker.transition()
.duration(3000)
.ease(d3.easeQuad)
.delay(300)
.attrTween("transform", function() {
return function(t) {
return "translate(" + (radius * (1 + (Math.sin(Math.PI / 2 * t)))) + ","
+ (((usedAmount / totalAmount) * (radius * 2)) * (1 - (Math.cos(Math.PI / 2 * t)))) + ")"
}
});
Here is the demo:
const usedAmount = 200;
const totalAmount = 400;
const radius = 120;
const valuePercent = (usedAmount / totalAmount) * 100;
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
let grad = svg
.append("defs")
.append("linearGradient")
.attr("id", "grad")
.attr("x1", "0%")
.attr("x2", "0%")
.attr("y1", "0%")
.attr("y2", "0%");
grad.append("stop").attr("offset", "1%").style("stop-color", '#000');
grad.append("stop").attr("offset", "1%").style("stop-color", '#ccc');
let arc = d3.arc()
.innerRadius(radius - 40)
.outerRadius(radius)
.endAngle(2 * Math.PI)
.startAngle(0 * Math.PI);
let cutout = svg.select('defs')
.append('clipPath')
.attr('clip-rule', 'evenodd')
.attr('id', 'cutout')
.append("path")
.attr('transform', 'translate(' + radius + ',' + radius + ')')
.attr('d', arc)
.attr('clip-rule', 'evenodd')
.attr('fill', '#ccc');
svg.append("circle")
.attr("cx", radius)
.attr("cy", radius)
.attr("r", radius)
.attr("clip-path", "url(#cutout)") // Apply the mask
.attr("fill", "url(#grad)");
grad
.transition()
.duration(3000)
.ease(d3.easeQuad)
.delay(300)
.attr("y1", valuePercent + 1 + '%');
var marker = svg.append('g')
.attr('class', 'gauge__fillup__follow')
.attr("transform", "translate(" + (radius) + "," + 0 + ")");
marker.append("rect")
.attr("x", 10)
.attr("y", 1)
.attr("fill", "#000")
.attr("width", 35)
.attr("height", 3);
marker.append('svg:text')
.attr('class', 'label')
.attr('z-index', '4')
.attr('x', 50)
.attr('y', -6)
.attr('dy', 12)
.attr('text-anchor', 'left')
.datum({
textContent: ''
})
.text(200)
.transition()
.duration(3000)
.delay(300)
.ease(d3.easeQuad)
.tween("text", function(d) {
const i = d3.interpolate(0, this.textContent, d);
return (t) => {
d3.select(this).text(Math.round(i(t)));
};
});
marker.append('svg:text')
.attr('class', 'label')
.attr('color', 'white')
.attr('z-index', '4')
.attr('x', 50)
.attr('y', 10)
.attr('dy', 12)
.attr('text-anchor', 'left')
.text('used');
marker.transition()
.duration(3000)
.ease(d3.easeQuad)
.delay(300)
.attrTween("transform", function() {
return function(t) {
return "translate(" + (radius * (1 + (Math.sin(Math.PI / 2 * t)))) + "," + (((usedAmount / totalAmount) * (radius * 2)) * (1 - (Math.cos(Math.PI / 2 * t)))) + ")"
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js"></script>
<svg height="500" width="500"></svg>
Your question made me realize that i didn't understand trigonometry as well as i would like, so i took a stab at it. Once i learned the fundamentals of pi and sine, the answer became quite clear.
Great article i read that led me to my answer: https://betterexplained.com/articles/intuitive-understanding-of-sine-waves/
https://jsfiddle.net/zk0wsq5a/2/
marker.transition()
.duration(3000)
.ease(d3.easeQuad)
.delay(300)
.attrTween("transform", function() {
return function(t) {
let distance = Math.PI * t * (usedAmount / totalAmount)
let x = radius + (radius * Math.sin(distance))
let y = radius * (1-Math.cos(distance))
return `translate(${x} ,${y})`
}
});

Responsive d3 brushing

I have a d3 timeline based on Mike Bostock's 'Focus + Context via brushing' which I'm trying to make responsive.
I've been able to achieve this with most of it, but I'm struggling with the extent of the brush. As a workaround I've tried just making it the new width of the context, but it behaves extremely erratically. Everything else I've tried seems to have no effect – the extent rect doesn't change width.
I need a way to find the x and width of the extent rect and apply them to my x-scale (named xContext) on resize. There's a 'working' version of it here and the full code is below. The resize function is towards the bottom.
Many thanks in advance.
var marginTimeline = {top: 0, right: 18, bottom: 260, left: 0},
marginContext = {top: 400, right: 18, bottom: 80, left: 0},
w = parseInt(d3.select("#chart").style("width")) - marginTimeline.left - marginTimeline.right,
hTimeline = parseInt(d3.select("#chart").style("height")) - marginTimeline.top - marginTimeline.bottom,
hContext = parseInt(d3.select("#chart").style("height")) - marginContext.top - marginContext.bottom;
//Height of the bars drawn. Context bars are half this.
var barHeight = hTimeline * 0.04;
var formatDate = d3.time.format("%Y%m%d"),
parseDate = formatDate.parse;
var xTimeline = d3.time.scale().range([0, w]),
xContext = d3.time.scale().range([0, w]),
yTimeline = d3.scale.linear().domain([0, 6]).range([hTimeline, 0]).nice(),
yContext = d3.scale.linear().range([hContext, 0]);
var thous = d3.format(",");
var displayDate = d3.time.format("%d %b %Y");
var displayMonthYear = d3.time.format("%b %Y");
var displayYear = d3.time.format("%Y");
var xAxisTimeline = d3.svg.axis().scale(xTimeline).orient("bottom"),
xAxisContext = d3.svg.axis().scale(xContext).orient("bottom"),
yAxisTimeline = d3.svg.axis().scale(yTimeline).orient("left").outerTickSize(0).ticks(0),
yAxisContext = d3.svg.axis().scale(yContext).orient("left").outerTickSize(0).ticks(0);
var svg = d3.select("#chart")
.attr("width", w + marginTimeline.left + marginTimeline.right)
.attr("height", hTimeline + marginTimeline.top + marginTimeline.bottom)
.append("g");
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", w)
.attr("height", hTimeline);
var opTimeline = svg.append("g")
.attr("class", "timeline")
.attr("width", w)
.attr("height", hTimeline)
.attr("transform", "translate(10,0)");
var opContext = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(10," + marginContext.top + ")");
var brush = d3.svg.brush()
.x(xContext)
.extent([0, 1])
.on("brush", brushed);
queue()
.defer(d3.json, "http://pasi.com.au/omarpasha/api/get_category_posts/?slug=shows&include=title,url,content,custom_fields")
.defer(d3.json, "http://pasi.com.au/omarpasha/api/get_category_posts/?slug=timeline&include=title,url,content,custom_fields")
.await(ready);
function ready(error, shows, history) {
shows.posts.forEach(function(d) {
d.id = d.id;
d.title = d.title;
d.showpage = d.url;
d.startDate = parseDate(d.custom_fields.starting_date[0]);
d.endDate = parseDate(d.custom_fields.finishing_date[0]);
})
history.posts.forEach(function(d) {
d.id = d.id;
d.title = d.title;
d.startDate = parseDate(d.custom_fields.starting_date[0]);
d.endDate = parseDate(d.custom_fields.finishing_date[0]);
d.line = d.custom_fields.line;
d.dateFormat = d.custom_fields.date_format;
});
var minDateShows = d3.min(shows.posts.map(function(d) { return d.startDate; }));
var minDateHistory = d3.min(history.posts.map(function(d) { return d.startDate; }));
var minDate = (minDateShows < minDateHistory ? minDateShows : minDateHistory);
var leftDate = new Date(minDate.getTime());
leftDate.setDate(leftDate.getDate()-40);
var maxDateShows = d3.max(shows.posts.map(function(d) { return d.endDate; }));
var maxDateHistory = d3.max(history.posts.map(function(d) { return d.endDate; }));
var maxDate = (maxDateShows > maxDateHistory ? maxDateShows : maxDateHistory);
var rightDate = new Date(maxDate.getTime());
rightDate.setDate(rightDate.getDate()+1400);
xTimeline.domain([leftDate, rightDate]);
xContext.domain(xTimeline.domain());
yContext.domain(yTimeline.domain());
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset(function(d) { if (xTimeline(d.endDate) > 800) { return [-10, 8] } else { return [-10, -8] } })
.direction(function(d) { if (xTimeline(d.endDate) > 800) { return 'nw' } else { return 'ne' } })
.html(function(d) {
if (displayMonthYear(d.startDate) == displayMonthYear(d.endDate)) {
return d.title + "<br/><p class='yellow'>" + displayMonthYear(d.startDate) + "</p>"; }
else {
return d.title + "<br/><p class='yellow'>"+ displayMonthYear(d.startDate) + " to " + displayMonthYear(d.endDate) + "</p>"; }
});
var tip2 = d3.tip()
.attr('class', 'd3-tip')
.direction(function(d) { if (xTimeline(d.endDate) > 800) { return 'nw' } else { return 'ne' } })
.offset(function(d) {
if (xTimeline(d.endDate) > 800) {
return [-10, 8];
} else {
return [-10, -8];
}
})
.html(function(d) {
var toolTipContent = "";
if ((xTimeline(d.endDate) - xTimeline(d.startDate) == 0)) {
toolTipContent = getToolTipContent(d, true);
} else {
toolTipContent = getToolTipContent(d, false);
}
return toolTipContent;
});
function getToolTipContent(d, sameDates) {
var toolTipContent = d.title + "<br/><p class='yellow'>";
if (d.dateFormat == "Year only") {
toolTipContent += (sameDates)
? displayYear(d.startDate) + "</p>" + d.content
: displayYear(d.startDate) + " to " + displayYear(d.endDate);
} else if (d.dateFormat == "Month and year") {
toolTipContent += (sameDates)
? displayMonthYear(d.startDate) + "</p>" + d.content
: displayMonthYear(d.startDate) + " to " + displayMonthYear(d.endDate);
} else {
toolTipContent += (sameDates)
? displayDate(d.startDate) + "</p>" + d.content
: displayDate(d.startDate) + " to " + displayDate(d.endDate);
}
toolTipContent += "</p>" + d.content;
return toolTipContent;
}
svg.call(tip);
svg.call(tip2);
opTimeline.append("line")
.attr("class", "show show-line")
.attr("x1", 0)
.attr("x2", w)
.attr("y1", yTimeline(5))
.attr("y2", yTimeline(5));
opTimeline.append("line")
.attr("class", "ost ost-line")
.attr("x1", 0)
.attr("x2", w)
.attr("y1", yTimeline(3))
.attr("y2", yTimeline(3));
opTimeline.append("line")
.attr("class", "blackart blackart-line")
.attr("x1", 0)
.attr("x2", w)
.attr("y1", yTimeline(1))
.attr("y2", yTimeline(1));
opContext.append("line")
.attr("class", "context show context-show-line")
.attr("x1", 0)
.attr("x2", w)
.attr("y1", yContext(5))
.attr("y2", yContext(5));
opContext.append("line")
.attr("class", "context ost context-ost-line")
.attr("x1", 0)
.attr("x2", w)
.attr("y1", yContext(3))
.attr("y2", yContext(3));
opContext.append("line")
.attr("class", "context blackart context-blackart-line")
.attr("x1", 0)
.attr("x2", w)
.attr("y1", yContext(1))
.attr("y2", yContext(1));
opTimeline.append("text")
.attr("class", "show show-text")
.attr("x", 10)
.attr("y", yTimeline(5) + 26)
.text("Shows");
opTimeline.append("text")
.attr("class", "ost ost-text")
.attr("x", 10)
.attr("y", yTimeline(3) + 26)
.text("Ostrowsky Family");
opTimeline.append("text")
.attr("class", "blackart blackart-text")
.attr("x", 10)
.attr("y", yTimeline(1) + 26)
.text("Black Art");
svg.append("text")
.attr("class", "explanation")
.attr("x", 10)
.attr("y", 380)
.text("Move the handles below to adjust the time period");
opTimeline.append("g")
.selectAll("rect")
.data(shows.posts)
.enter()
.append("svg:a")
.attr("xlink:href", function(d){return d.showpage;})
.append("rect")
.attr("class", "event show-event show")
.attr("clip-path", "url(#clip)")
.attr("x", (function(d) { return xTimeline(d.startDate); }))
.attr("width", (function(d) { if ((xTimeline(d.endDate) - xTimeline(d.startDate) > 12)) {
return (xTimeline(d.endDate) - xTimeline(d.startDate));}
else {
return 12
} }))
.attr("y", yTimeline(5) - (barHeight * 0.5))
.attr("height", barHeight)
.attr("rx", 10)
.attr("ry", 10);
opTimeline.append("g")
.selectAll("rect")
.data(history.posts)
.enter()
.append("rect")
.attr("class", (function(d) { if (d.line == "Ostrowsky family") { return "event ost-event ost" } else { return "event blackart-event blackart" } }))
.attr("clip-path", "url(#clip)")
.attr("x", (function(d) { return xTimeline(d.startDate); }))
.attr("width", (function(d) { if ((xTimeline(d.endDate) - xTimeline(d.startDate) > 12)) {
return (xTimeline(d.endDate) - xTimeline(d.startDate));}
else {
return 12
} }))
.attr("y", (function(d) { if (d.line == "Ostrowsky family") { return yTimeline(3) - (barHeight * 0.5) } else { return yTimeline(1) - (barHeight * 0.5) } }))
.attr("height", barHeight)
.attr("rx", 10)
.attr("ry", 10);
opContext.append("g")
.selectAll("rect")
.data(shows.posts)
.enter()
.append("rect")
.attr("class", "event show-event show")
.attr("x", (function(d) { return xContext(d.startDate); }))
.attr("width", (function(d) { if ((xTimeline(d.endDate) - xTimeline(d.startDate) > 6)) {
return (xTimeline(d.endDate) - xTimeline(d.startDate));}
else {
return 6
} }))
.attr("y", yContext(5) - (barHeight * 0.25))
.attr("height", barHeight/2)
.attr("rx", 5)
.attr("ry", 5);
opContext.append("g")
.selectAll("rect")
.data(history.posts)
.enter()
.append("rect")
.attr("class", (function(d) { if (d.line == "Ostrowsky family") { return "event ost-event ost" } else { return "event blackart-event blackart" } }))
.attr("x", (function(d) { return xContext(d.startDate); }))
.attr("width", (function(d) { if ((xTimeline(d.endDate) - xTimeline(d.startDate) > 6)) {
return (xTimeline(d.endDate) - xTimeline(d.startDate));}
else {
return 6
} }))
.attr("y", (function(d) { if (d.line == "Ostrowsky family") { return yContext(3) - (barHeight * 0.25) } else { return yContext(1) - (barHeight * 0.25) } }))
.attr("height", barHeight/2)
.attr("rx", 5)
.attr("ry", 5);
opTimeline.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + hTimeline + ")")
.call(xAxisTimeline);
opContext.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + hContext + ")")
.call(xAxisContext);
var brushg = opContext.append("g")
.attr("class", "x brush")
.call(brush)
.selectAll("rect")
.attr("y", -6)
.attr("height", hContext + 7);
opContext.selectAll(".e")
.append("image")
.attr("xlink:href",'../wp-content/themes/omarpasha/img/right-handle.png')
.attr("width", 10)
.attr("height", 70)
.attr("y", -6);
opContext.selectAll(".w")
.append("image")
.attr("xlink:href",'../wp-content/themes/omarpasha/img/left-handle.png')
.attr("width", 10)
.attr("height", 70)
.attr("x", -10)
.attr("y", -6);
opTimeline.selectAll(".show-event")
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
opTimeline.selectAll(".ost-event, .blackart-event")
.on('mouseover', tip2.show)
.on('mouseout', tip2.hide);
function resize() {
marginContext = {top: 400, right: 18, bottom: 80, left: 0},
w = parseInt(d3.select("#chart").style("width")) - marginTimeline.left - marginTimeline.right,
hTimeline = parseInt(d3.select("#chart").style("height")) - marginTimeline.top - marginTimeline.bottom,
hContext = parseInt(d3.select("#chart").style("height")) - marginContext.top - marginContext.bottom;
var barHeight = hTimeline * 0.04;
xTimeline.range([0, w]),
xContext.range([0, w]),
yTimeline.range([hTimeline, 0]).nice(),
yContext.range([hContext, 0]);
svg
.attr("width", w + marginTimeline.left + marginTimeline.right)
.attr("height", hTimeline + marginTimeline.top + marginTimeline.bottom);
svg.select("#clip rect")
.attr("width", w)
.attr("height", hTimeline);
d3.select(".background")
.attr("width", w);
opTimeline
.attr("width", w)
.attr("height", hTimeline)
.attr("transform", "translate(10,0)");
opContext
.attr("transform", "translate(10," + marginContext.top + ")");
opTimeline.select('.x.axis')
.attr("transform", "translate(0," + hTimeline + ")")
.call(xAxisTimeline);
opContext.select('.x.axis')
.attr("transform", "translate(0," + hContext + ")")
.call(xAxisContext);
opTimeline.select(".show-line")
.attr("x2", w);
opTimeline.select(".ost-line")
.attr("x2", w);
opTimeline.select(".blackart-line")
.attr("x2", w);
opContext.select(".context-show-line")
.attr("x2", w);
opContext.select(".context-ost-line")
.attr("x2", w);
opContext.select(".context-blackart-line")
.attr("x2", w);
opTimeline.selectAll(".event")
.attr("x", (function(d) { return xTimeline(d.startDate); }))
.attr("width", (function(d) { if ((xTimeline(d.endDate) - xTimeline(d.startDate) > 12)) {
return (xTimeline(d.endDate) - xTimeline(d.startDate));}
else {
return 12
} }));
opContext.selectAll(".event")
.attr("x", (function(d) { return xContext(d.startDate); }))
.attr("width", (function(d) { if ((xContext(d.endDate) - xContext(d.startDate) > 6)) {
return (xContext(d.endDate) - xContext(d.startDate));}
else {
return 6
} }));
brush
.x(xContext)
.extent([0, 1])
.on("brush", brushed);
}
d3.select(window).on('resize', resize);
resize();
};
function brushed() {
xTimeline.domain(brush.empty() ? xContext.domain() : brush.extent());
opTimeline.selectAll("rect").attr("x", (function(d) { return xTimeline(d.startDate); }))
.attr("width", (function(d) { if ((xTimeline(d.endDate) - xTimeline(d.startDate) > 12)) { return (xTimeline(d.endDate) - xTimeline(d.startDate));} else { return 12 } }));
opTimeline.select(".x.axis").call(xAxisTimeline);
}
I had someone outside Stack Overflow sort this out for me. The solution was straightforward – capture the state of the brush's extent at the beginning of the resize function. Nothing else was changed. So the resize function now looks like this (still rather verbose, but working):
function resize() {
var extent = brush.extent();
w = parseInt(d3.select("#chart").style("width")) - marginTimeline.left - marginTimeline.right,
hTimeline = parseInt(d3.select("#chart").style("height")) - marginTimeline.top - marginTimeline.bottom;
var barHeight = hTimeline * 0.04;
xTimeline.range([0, w]),
xContext.range([0, w]),
yTimeline.range([hTimeline, 0]).nice(),
yContext.range([hContext, 0]);
svg
.attr("width", w + marginTimeline.left + marginTimeline.right);
svg.select("#clip rect")
.attr("width", w);
opTimeline
.attr("width", w)
.attr("transform", "translate(10,0)");
opContext
.attr("transform", "translate(10," + marginContext.top + ")");
opTimeline.select('.x.axis')
.attr("transform", "translate(0," + hTimeline + ")")
.call(xAxisTimeline);
opContext.select('.x.axis')
.attr("transform", "translate(0," + hContext + ")")
.call(xAxisContext);
opTimeline.select(".show-line")
.attr("x2", w);
opTimeline.select(".ost-line")
.attr("x2", w);
opTimeline.select(".blackart-line")
.attr("x2", w);
opContext.select(".context-show-line")
.attr("x2", w);
opContext.select(".context-ost-line")
.attr("x2", w);
opContext.select(".context-blackart-line")
.attr("x2", w);
opTimeline.selectAll(".event")
.attr("x", (function(d) { return xTimeline(d.startDate); }))
.attr("width", (function(d) { if ((xTimeline(d.endDate) - xTimeline(d.startDate) > 12)) {
return (xTimeline(d.endDate) - xTimeline(d.startDate));}
else {
return 12
} }));
opContext.selectAll(".event")
.attr("x", (function(d) { return xContext(d.startDate); }))
.attr("width", (function(d) { if ((xContext(d.endDate) - xContext(d.startDate) > 6)) {
return (xContext(d.endDate) - xContext(d.startDate));}
else {
return 6
} }));
brush.extent(extent);
// Now just call the methods to update the brush.
opContext.select("g.x.brush").call(brush);
brushed();
}

D3.js Day Hour Heatmap dynamic update

I'm very new to D3.js and I'm trying to learn how to put things together correctly. I have a day/hour heatmap based on this example here: http://bl.ocks.org/tjdecke/5558084
I'm trying to write an updateHeatMap method so that I can dynamically update the heatmap with new data. I've been researching and researching, and I honestly haven't found a successful solution. Any help would be appreciated, code snippets below
I have changed the original day/hour heatmap example slightly to look like this:
$scope.heatMapData = $http.get(...
$scope.initHeatMap = function() {
var margin = { top: 50, right: 0, bottom: 100, left: 30 },
width = 960 - margin.left - margin.right,
height = 430 - margin.top - margin.bottom,
gridSize = Math.floor(width / 24),
legendElementWidth = gridSize*2,
buckets = 9,
colors = ["#ffffd9","#edf8b1","#c7e9b4","#7fcdbb","#41b6c4","#1d91c0","#225ea8","#253494","#081d58"], // alternatively colorbrewer.YlGnBu[9]
days = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
times = ["1a", "2a", "3a", "4a", "5a", "6a", "7a", "8a", "9a", "10a", "11a", "12a", "1p", "2p", "3p", "4p", "5p", "6p", "7p", "8p", "9p", "10p", "11p", "12p"];
$scope.colorScale = d3.scale.quantile()
.domain([0, buckets - 1, d3.max($scope.heatMapData, function (d) { return d.value; })])
.range(colors);
var svg = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var dayLabels = svg.selectAll(".dayLabel")
.data(days)
.enter().append("text")
.text(function (d) { return d; })
.attr("x", 0)
.attr("y", function (d, i) { return i * gridSize; })
.style("text-anchor", "end")
.attr("transform", "translate(-6," + gridSize / 1.5 + ")")
.attr("class", function (d, i) { return ((i >= 0 && i <= 4) ? "dayLabel mono axis axis-workweek" : "dayLabel mono axis"); });
var timeLabels = svg.selectAll(".timeLabel")
.data(times)
.enter().append("text")
.text(function(d) { return d; })
.attr("x", function(d, i) { return i * gridSize; })
.attr("y", 0)
.style("text-anchor", "middle")
.attr("transform", "translate(" + gridSize / 2 + ", -6)")
.attr("class", function(d, i) { return ((i >= 7 && i <= 16) ? "timeLabel mono axis axis-worktime" : "timeLabel mono axis"); });
var heatMap = svg.selectAll(".hour")
.data($scope.heatMapData)
.enter().append("rect")
.attr("x", function(d) { return (d.hour - 1) * gridSize; })
.attr("y", function(d) { return (d.day - 1) * gridSize; })
.attr("rx", 4)
.attr("ry", 4)
.attr("class", "hour bordered")
.attr("width", gridSize)
.attr("height", gridSize)
.style("fill", colors[0]);
heatMap.transition().duration(1000)
.style("fill", function(d) { return $scope.colorScale(d.value); });
heatMap.append("title").text(function(d) { return d.value; });
var legend = svg.selectAll(".legend")
.data([0].concat($scope.colorScale.quantiles()), function(d) { return d; })
.enter().append("g")
.attr("class", "legend");
legend.append("rect")
.attr("x", function(d, i) { return legendElementWidth * i; })
.attr("y", height)
.attr("width", legendElementWidth)
.attr("height", gridSize / 2)
.style("fill", function(d, i) { return colors[i]; });
legend.append("text")
.attr("class", "mono")
.text(function(d) { return "≥ " + Math.round(d); })
.attr("x", function(d, i) { return legendElementWidth * i; })
.attr("y", height + gridSize);
};
Here is what I have so far with the update method. This isn't working, because I'm not sure how to get the updated $scope.heatMapData into the new heatMap variable. I also need to update the legend to match the new color scale, but that's second in priority under this.
$scope.rowSelected() = function(){
$http.get(...).then(function(result){
$scope.heatMapData = result.data;
$scope.updateHeatMap();
});
}
$scope.updateHeatMap = function(){
var svg = d3.select("body").transition();
var heatMap = svg.selectAll(".hour")
.duration(250)
.attr("x", function(d) { return (d.hour - 1) * gridSize; })
.attr("y", function(d) { return (d.day - 1) * gridSize; })
.attr("rx", 4)
.attr("ry", 4)
.attr("class", "hour bordered")
.attr("width", gridSize)
.attr("height", gridSize)
.style("fill", colors[0]);
heatMap.transition().duration(1000)
.style("fill", function(d) { return colorScale(d.value); });
}

Categories

Resources