0

The svg code below plots four points, each with a different resulting level of transparency due to overlapping points.

The data consists of a position for a circle and the number of circles that will be at that position. The demo data here also demonstrates that this aggregation isn't perfect for various reasons - their may be two entries for the same position both indicating that a few circles are positioned there.

I would like to calculate and show the actual transparency as if I had been given the data in a completely un-aggregated form - e.g. in this example dataset the un-aggregated form would have three entries for the circle at position 3 and the transparency of the circle at position 3 would be the same as the transparency of the circle at the position 4.

How could I calculate the alpha level I should use for each point to get this result? Note that I do not want to manipulate the data before hand to get the un-aggregated form, since the reason it is aggregated is to prevent huge numbers of svg elements being plotted, which would be very intensive to process.

<html>
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script src="http://d3js.org/d3.v3.min.js"></script>
    </head>
    <body>
        <svg id="canvas"></svg>
        <script>
         var svg = d3.select("#canvas");
            svg.selectAll("circle")
                .data(
                        [
                            [1,1], 
                            [2,6], 
                            [3,1],
                            [3,2],
                            [4,1],
                            [4,1],
                            [4,1]
                        ]
                    )
            .enter().append("circle")
                .attr("cy", 60)
                .attr("cx", function(d) { return d[0] * 50; })
                .attr("r", 20)
                .attr("stroke-opacity", 0.3)
                .attr("fill-opacity", 0.3);
         </script>

    </body>
</html>
MattLBeck
  • 5,701
  • 7
  • 40
  • 56

1 Answers1

2

Basically, you will need to sum up all the circles you have at a given position (ie. at position 3 you have 1+2 = 3, position 4 = 1+1+1 = 3), and then divide by the number of circles at each position (position 3 = 2, position 4 = 3). That should give you consistent values for calculating the opacity of the individual circles. See the below snippet. The interesting bit is:

function opacity(d) {
    var count = data.filter(function(e) { return e[0] == d[0]; }).length,
    sum = d3.sum(data.filter(function(e) { return e[0] == d[0]; }), function(e) { return e[1]; });
    return (sum * .1) / count;
}  

var data =  [
                            [1,1], 
                            [2,6], 
                            [3,1],
                            [3,2],
                            [4,1],
                            [4,1],
                            [4,1]
                        ]
function opacity(d) {
    var count = data.filter(function(e) { return e[0] == d[0]; }).length,
    sum = d3.sum(data.filter(function(e) { return e[0] == d[0]; }), function(e) { return e[1]; });
    return (sum * .1) / count;
}  

var svg = d3.select("#canvas");
            svg.selectAll("circle")
                .data(data)
            .enter().append("circle")
                .attr("cy", 60)
                .attr("cx", function(d) { return d[0] * 50; })
                .attr("r", 20)
                .attr("stroke-opacity", opacity)
                .attr("fill-opacity", opacity);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg id="canvas"></div>
Ben Lyall
  • 1,976
  • 1
  • 12
  • 14