Ben Whitely
Ben Whitely

Reputation: 59

JQuery SVG graph - overlapping bars

I'm trying to learn SVG graphs, I've nearly completed my graph however I currently have overlapping bars, I believe this is due to a combination of my x axis return data and my json data. My x axis lists the months from my JSON data, but my JSON data can have multiple entries from the same month. and if this is the case then my entries will overlap for that month. What I need to do is return the month + day seperately, however I haven't been able to figure out how just yet.

JSON data example where there's 2 entries for the same month:

1: {dayNo: 2, monthNo: 7, monthName: "July", year: 2018, recordDate: "2018-07-02T00:00:00",…}
avgVolume: 3585.53
dayNo: 2
monthName: "July"
monthNo: 7
recordDate: "2018-07-02T00:00:00"
volumeLastYear: 4109.47
volumeToday: 3575.34
year: 2018
2: {dayNo: 30, monthNo: 7, monthName: "July", year: 2018, recordDate: "2018-07-30T00:00:00",…}
avgVolume: 3291.55
dayNo: 30
monthName: "July"
monthNo: 7
recordDate: "2018-07-30T00:00:00"
volumeLastYear: 3996.31
volumeToday: 3414.44
year: 2018

And here is the svg code:

            $request.done(function (response) {
            // remove old svg
            $('svg').remove();

            // create svg element
            var $svg = d3.select('.card.mb-3.clickable-card.highlight')
                .append('svg')
                .attr('width', '100%')
                .attr('height', '400px')
                .attr('style', 'background-color: lavender')
                .classed('svg display-svg', true);

            // 2 determine size of svg element
            var w = $svg.node().getBoundingClientRect().width;
            var h = $svg.node().getBoundingClientRect().height;

            // 10 chart margins
            var chartMargin = new Object();
            chartMargin.left = 40;
            chartMargin.right = 25;
            chartMargin.top = 25;
            chartMargin.bottom = 40;

            // 10 cont
            h = h - chartMargin.bottom - chartMargin.top;

            // Max volume
            var maxVolume = d3.max(response, d => d.avgVolume);

            // bar Margins / width
            var barMargin = 10;
            var barWidth = (w - chartMargin.left - chartMargin.right) / response.length;

            // yScale
            var yScale = d3.scaleLinear()
                .domain([0, maxVolume])
                .range([h, chartMargin.top]);

            // xScale
            var xScale = d3.scaleBand()
                .domain(response.map(function (d) { return d.monthName; }))
                .range([0, w - chartMargin.left - chartMargin.right])
                .paddingInner(0.15);

            // chartGroup
            var chartGroup = $svg.append('g')
                .classed('chartGroup', true)
                .attr('transform', 'translate(' + chartMargin.left + ',' + chartMargin.top + ')');

            // 5
            var barGroups = chartGroup
                .selectAll('g')
                .data(response);

            // 6
            var newBarGroups = barGroups.enter()
                .append('g')
                .attr('transform', function (d, i) {
                    return 'translate('
                        + (xScale(d.monthName))
                        + ','
                        + (yScale(d.avgVolume))
                        + ')';
                })

            // yAxis label
            var yAxis = d3.axisLeft(yScale);
            chartGroup.append('g')
                .classed('axis y', true)
                .attr('transform', 'translate(' + -20 + ', ' + h / 2 + ')')
                .append('text')
                .attr('transform', 'rotate(-90)')
                .attr('dx', '-0.8em')
                .attr('dy', '0.25em')
                .attr("text-anchor", 'middle')
                .text("Reservoir Volume (ML)");


            // xAxis label
            var xAxis = d3.axisBottom(xScale);
            chartGroup.append('g')
                .attr('transform', 'translate(0,' + h + ')')
                .classed('axis x', true)
                .call(xAxis);

            newBarGroups
                .append('rect')
                .attr('x', 0)
                .attr('height', function (d, i) {
                    return h - yScale(d.avgVolume);
                })

                // animation
                .style('fill', 'transparent')
                .transition().duration(function (d, i) { return i * 500; })
                .delay(function (d, i) { return i + 200; })

                .attr('width', barWidth - barMargin)
                .style('fill', function (d, index) {
                    return "hsl(240, 100%, " + (d.avgVolume / maxVolume * 80) + "%)"
                });

            // bar text
            newBarGroups
                .append('text')
                .attr('transform', 'rotate(-45)')
                .attr('text-anchor', 'middle')
                .attr('x', function () { return 0; })
                .attr('y', 50)
                .attr('fill', 'white')
                .text(function (d, i) { return d.avgVolume; })
                .style('font-size', '1em')

        });

in the xScale, I have tried changing the return data from

return d.monthName

to

return d.monthName + ' ' + d.dayNo

But then all my bars overlap and I get this error for each g element: d3.js:1211 Error: attribute transform: Expected number, "translate(undefined,25.183…".

Here is what I have currently with returning d.monthName: enter image description here

Here is d.monthName + ' ' + d.dayNo: enter image description here

Upvotes: 0

Views: 178

Answers (1)

Ben Whitely
Ben Whitely

Reputation: 59

Nevermind sorry, I fixed it

I needed to change

            var newBarGroups = barGroups.enter()
            .append('g')
            .attr('transform', function (d, i) {
                return 'translate('
                    + (xScale(d.monthName))
                    + ','
                    + (yScale(d.avgVolume))
                    + ')';
            })

to

                var newBarGroups = barGroups.enter()
                .append('g')
                .attr('transform', function (d, i) {
                    return 'translate('
                        + (xScale(d.monthName + ' ' + d.dayNo))
                        + ','
                        + (yScale(d.avgVolume))
                        + ')';
                })

Upvotes: 0

Related Questions