So, I am faced with a situation where I have little to no experience coding in typescript and I still have to create a custom visual for PowerBI.
I am slowly slowly getting there through online tutorials and scavenging code...
I do not however understand the difference between update's handling of the barchart boxes and the labels in my code.
I tried implementing them identically, but while the chart itself updates fine, the old labels remain.
I have tried googling but it is a classic case of simply not knowing the term to google for so I am at a loss on how to proceed.
Here is the (hopefully) relevant part of my code. Any help is much appreciated:
const bars = this.barContainer
.selectAll('.bar')
.data(extractedData); /*select all bar elements and then passes data using "data" command and stores it in "bars" variable */
bars.enter()
.append('rect')
.classed('bar', true)
.attr('height', y.bandwidth())
.attr('width', dataPoint => width - cm - x(dataPoint.value))
.attr('x', dataPoint => (x(dataPoint.value) + cm) / 2)
.attr('y', dataPoint => y(dataPoint.category))
.attr('fill', dataPoint => dataPoint.color);
bars
.attr('height', y.bandwidth())
.attr('width', dataPoint => width - cm - x(dataPoint.value))
.attr('x', dataPoint => (x(dataPoint.value) + cm) / 2)
.attr('y', dataPoint => y(dataPoint.category));
const mylabels = this.textLabel
.selectAll('.textlabel')
.data(extractedData);
mylabels.enter()
.append('text')
.classed('textlabel', true)
.attr("class", "label")
.attr('x', dataPoint => (x(dataPoint.value) + cm) / 2)
.attr('y', dataPoint => y(dataPoint.category))
.attr("dy", ".75em")
.text(function (d) { return d.value; });
mylabels
.attr('x', dataPoint => (x(dataPoint.value) + cm) / 2)
.attr('y', dataPoint => y(dataPoint.category))
bars.exit().remove();
mylabels.exit().remove();
Related
I'm building a schedule in d3.js. I've managed to create bars to represent the timeline,
but now I want to append additional rects to the existing rect to highlight events that happen within that time. The data is attached to the the bar data, so I thought it would be as simple as creating an each function and appending another rect using that data, however I cannot get the additional rects to show. They show in the html when I inspect it, but not visible in the browser. Here is the code i am using, can anyone see where I am going wrong?
var innerRects = this.graph.append("rect")
.attr("class", "innerRects")
.attr("rx", 3)
.attr("ry", 3)
.attr("x", (d: any) => this.timeScale(dateFormat(d.startTime)) + this.sidePadding)
.attr("y",(d, i) => i * this.gap + this.topPadding )
.attr("width", (d: any) => (this.timeScale(dateFormat(d.endTime))-this.timeScale(dateFormat(d.startTime))))
.attr("height", this.barHeight)
.attr("stroke", "none")
.style("cursor", "pointer")
.style("fill",(d: any) => d.color)
.each(function(d) {
const parent = d3.select(this)
parent.append("rect")
.data(d)
.attr("class", "innerRects2")
.attr("width", (d: any) => (this.timeScale(dateFormat(d.endTime))-this.timeScale(dateFormat(d.startTime))))
.attr("height", 20)
.attr("x", (d: any) => this.timeScale(dateFormat(d.startTime)) + this.sidePadding)
.attr("y", (d, i) => i * this.gap + this.topPadding)
.style("fill", "red")
})
I am making a stacked bar chart in d3.js and have the following code which currently works for what I need:
const stackedData = d3.stack().keys(keys)
const layers = stackedData(this.data)
let time = this.chart.selectAll('.time').data(layers);
// removes time from the DOM
time.exit().remove();
// adds time to the DOM
this.chart.append("g")
.selectAll("g")
.data(layers)
.join("g")
.style("fill", (d, i) => colors[i])
.selectAll('.time')
.data(d => d)
.enter().append('rect')
.attr('class', 'time')
.attr('x', d => this.xScale(d.data.date))
.attr('y', d => this.yScale(0))
.attr("width", this.xScale.bandwidth())
.attr('height', 0)
.transition()
.delay((d, i) => i * 10)
.attr('y', d => this.yScale(d[1]))
.attr('height', d => this.yScale(d[0]) - this.yScale(d[1]))
However, I am unsure on what the general update pattern should be to update the shapes already present in the DOM on refresh. for a standard (non-stacked) chart, it would be as follows, however these does not work for a stacked chart:
// update existing time
this.chart.selectAll('.time').transition()
.attr('x', d => this.xScale(d.date))
.attr('y', d => this.yScale(d.revenue))
.attr('width', this.xScale.bandwidth())
.attr('height', d => this.graphHeight - this.yScale(d.revenue))
.attr("fill", "#007bff")
My bar charts start growing in height slowly and then rapidly towards the end.
I want them to grow at the same pace
I have set ease to easeLinear but it doesn't appear to have any impact.
As an experiment, I used .ease(d3.easeBounce) but nothing changed. The bars started growing slowly and then rapidly towards the end.
Where am I going wrong?
function renderVerticalBars(data, measurements, metric, countryID) {
let selectDataForBarCharts = d3.select("svg")
.selectAll("rect")
.data(data, d => d[countryID])
selectDataForBarCharts
.enter()
.append("rect")
.attr('width', measurements.xScale.bandwidth())
.attr("height", 0)
.attr('y', d => measurements.yScale(0))
.merge(selectDataForBarCharts)
.attr("fill", d => setBarColor(d))
.attr("transform", `translate(0, ${measurements.margin.top})`)
.attr('width', measurements.xScale.bandwidth())
.attr('x', (d) => measurements.xScale(d[countryID]))
.on('mouseover', (event, barData) => { displayComparisons(event, barData, data, metric, countryID, measurements) })
.on('mouseout', (event) => { renderValuesInVerticalBars(data, metric, countryID, measurements) })
.transition()
.ease(d3.easeLinear)
.duration(4000)
.attr("height", d => measurements.innerHeight - measurements.yScale(d[metric]))
.attr("y", (d) => measurements.yScale(d[metric]))
}
I'm working on a horizontal bar graph and would like to display the values on each bar with a specific format. I've tried to code it in many ways but have been unsuccessful in achieving the results I want. I'm using the following code:
data = d3.json('data.json')
.then(data => {data
.forEach(d => {
d.country = d.country;
d.population = +d.population * 1000;
});
console.log(data);
render(data);
});
const xValuesNumberFormat = number => d3.format(',.0f')(number);
const xValues = d => d.population;
svg.selectAll('text')
.data(data)
.enter()
.append('text')
.attr('class', 'bar-value')
.attr('x', xPos)
.attr('y', yPos)
.text(xValues)
.attr('transform',`translate(5,`+yScale.bandwidth()/1.5+`)`)
;
[
{"country":"China","population":1415046},
{"country":"India","population":1354052},
{"country":"United States","population":326767},
{"country":"Indonesia","population":266795},
{"country":"Brazil","population":210868},
{"country":"Pakistan","population":200814},
{"country":"Nigeria","population":195875},
{"country":"Bangladesh","population":166368},
{"country":"Russia","population":143965},
{"country":"Mexico","population":130759}
]
svg.selectAll('rect')
.data(data)
.enter()
.append('rect')
.attr('class', 'rect')
//.attr('x', xPos)
.attr('y', yPos)
.attr('width', xPos)
.attr('height', yScale.bandwidth())
.on('mouseover', function (d)
{tooltip.style('display', null);})
.on('mouseout', function (d)
{tooltip.style('display', 'none');})
.on('mousemove', function (d)
{
var xPos = d3.mouse(this)[0] ;
var yPos = d3.mouse(this)[1] - 20;
tooltip.attr("transform", "translate("+xPos + "," + yPos +")");
tooltip.select('text').text(d3.format(',.0f')(d.population));
})
;
As soon as I try to format it I get Nan, undefined and various other errors.
enter image description here
As you can see from the image, I want the number next to the bar to be formatted like the number on the bar. Basically, I just want comma separators. Thank you for your help.
I figured it out.
svg.selectAll('text')
.data(data)
.enter()
.append('text')
.attr('class', 'bar-value')
.attr('x', xPos)
.attr('y', yPos)
.text(d => d3.format(',.0f')(+d.population))
.attr('transform', `translate(5,`+yScale.bandwidth()/1.5+`)`);
Thank you for taking the time to contribute.
I have a codepen here - https://codepen.io/anon/pen/GybENz
I've created a simple stacked bar chart with a legend to filter the chart.
I'd like to animated the height of the bar from the bottom axis up.
Currently its animating from the left and down
let layersBar = layersBarArea.selectAll('.layer').data(stackedSeries)
.enter()
.append('g')
.attr('class', 'layer')
.style('fill', (d, i) => {
return colors[i];
});
layersBar.selectAll('rect')
.data((d) => {
return d
})
.enter()
.append('rect')
.attr('height', 100)
.transition()
.duration(400)
.attr('height', (d, i) => {
return y(d[0]) - y(d[1]);
})
.attr('y', 0)
.attr('y', (d) => {
return y(d[1]);
})
.attr('x', (d, i) => {
return x(d.data.date)
})
.attr('width', x.bandwidth());
}
Set the x position, the width, the y position (as the baseline) and the height (as zero) before the transition:
.attr('height', 0)
.attr("y", h - margin.bottom - margin.top)
.attr('x', (d, i) => {
return x(d.data.date)
})
.attr('width', x.bandwidth())
Here is the updated CodePen: https://codepen.io/anon/pen/ypdoMK?editors=0010
PS: It would be a good idea transitioning each rectangle individually. For instance, if the user clicked usedInf, you should transition only those rectangles... however, since you did this...
layersBarArea.selectAll('g.layer').remove();
... at the beginning of your drawChart function, which is a wrong approach, such suggestion will need a big refactor, out of the scope of this question/answer.