1

I have some rookie question. I am trying to rescale scatter plot elements (circles along with X axis) based on button click - to create 'zoom in' activity. I am lowering X axis domain for that.

It works very well with Xaxis, however its harder with circles. I must remove all of them and draw them again (the place marked in the code). This makes the process slow when lot of elements involved and also doesn't allow transition which is applied for axis scaling.

Is it possible just to change attributes of circles that i won't need to remove and redraw everything again?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<script>
    // Define Canvas size
    var width = 500;
    var height = 500;

    // Generate Toy Data
    var randomX = d3.randomUniform(0, 880);
    var randomY = d3.randomUniform(0, 800);
    data_x = [];
    data_y = [];
    for (i = 0; i < 10000; i++) {
        nume = Math.floor((Math.random() * 100));
        data_x.push(nume);
        data_y.push(nume);
    }

    // Scaling Coeffs
    var coeffX = 1;
    var coeffY = 1;
    var coeffR = 1;

    var coordX = (Math.max.apply(null, data_x)/coeffX);
    var coordy = (Math.max.apply(null, data_y)/coeffY);

    var x = d3.scaleLinear()
              .domain([0, coordX])
              .range([ 0, width]).nice();

    var y = d3.scaleLinear()
              .domain([0, coordy])
              .range([height, 0]).nice();

    var yAxis = d3.axisLeft(y).ticks(5);
    var xAxis = d3.axisBottom(x).ticks(5)


    //Create SVG element
    var canvas = d3.select("body")
            .append("svg")
            .attr("width", width+50)
            .attr("height", height+50)
            .append("g");

    // --------------- Draw Elements ---------------------
    var circles = canvas.selectAll("circlepoint")
            .data(data_x)
            .enter()
            .append("g");

        circles.append("circle")
            .attr("cx",function(d, i){
                var tempe = data_x[i];
                return x(tempe);
            })
            .attr("cy", function(d, i){
                var tempe = data_y[i];
                return y(tempe);
            })
            .attr("r", function(){
                return 3;
            })
            .style("stroke", "steelblue")
            .style("fill", "none")
            .attr("transform", "translate(40, -20)");

    var xsCont = canvas.append("g")
                .attr("transform", "translate(40, " + (height-20)  +")")
                .attr("class","xaxis")
                .call(xAxis);
    var ysCont = canvas.append("g")
                .attr("transform", "translate(40, -20)")
                .call(yAxis);

    function rescaleAxisX(tempCoeffX, tempCoeffR){

        coordX = (Math.max.apply(null, data_x)/tempCoeffX);
        x.domain([0, Math.max.apply(null, data_x)/tempCoeffX])

        // -------- This part works well-------
        canvas.select(".xaxis")
            .transition().duration(750)
            .call(xAxis);
        // -------- End -------------

        // -------- This one doesn't as expected-------
        circles.remove();
        circles = canvas.selectAll("circlepoint")
            .data(data_x)
            .enter()
            .append("g");

        circles.append("circle")
            .attr("cx",function(d, i){
                var tempe = data_x[i];
                return x(tempe);
            })
            .attr("cy", function(d, i){
                var tempe = data_y[i];
                return y(tempe);

            })
            .attr("r", function(d, i){
                return tempCoeffR;
            })
            .style("stroke", "steelblue")
            .style("fill", "none")
            .attr("transform", "translate(40, -20)");
    }
    // -------- End -------


    // Zoom in button
    var buttonZoomPlusX = d3.select("body")
            .append("input")
            .attr("type","button")
            .attr("class","button")
            .attr("value", "+")
            .on("click", function(){
                coeffX = coeffX + 1;
                coeffR = coeffR + 1;
                rescaleAxisX(coeffX, coeffR);
            })

</script>
</body>
</html>

Here is implemented Fiddle https://jsfiddle.net/sma76ntq/

Thanks a lot in advance

Darius
  • 596
  • 1
  • 6
  • 22
  • first your data binding is wrong, make an array of objects with `x` and `y` fields. You use the GW-Basic method, very error prone in the long run. Examine an example of a scatter plot on how to do it correctly – rioV8 Dec 18 '18 at 16:57
  • thank you for the notice, but i don't feel its the core problem why this cased doesn't work in the way i would like. Isn't it? – Darius Dec 18 '18 at 18:24

1 Answers1

0

Well it was rookie mistake as always. Two things were missing: 1. Appending some class to circles in order not too select everything on canvas if there were more circles drawn

circles.append("circle")
        .attr("cx",function(d, i){
            var tempe = data_x[i];
            return x(tempe);
        })
        .attr("cy", function(d, i){
            var tempe = data_y[i];
            return y(tempe);
            //return y(d.y);
        })
        .attr("r", function(){
            return 3;
        })
        .attr("class","eles")
        .style("stroke", "steelblue")
        .style("fill", "none")
        .attr("transform", "translate(40, -20)");
  1. on redrawn just select classes and change attributes.

        canvas.selectAll(".eles").data(data_x)
        .transition()
        .duration(750)
        .attr("cx",function(d, i){
            var tempe = data_x[i];
            return x(tempe);
        })
        .attr("cy", function(d, i){
            var tempe = data_y[i];
            return y(tempe);
        });
    

Working Fiddle here: https://jsfiddle.net/v7q14nbm/1/

Darius
  • 596
  • 1
  • 6
  • 22