Home > Mobile >  d3 toggle button with a if-else loop
d3 toggle button with a if-else loop

Time:06-19

I am trying to create a data visualization webpage to display bounding boxes(polygons) by taking the coordinates from an annotation file.

The code is working perfectly fine in terms of displaying the polygons but when I added a hide/unhide d3.select(button) and when I click on the toggle button it displays all the polygons but when I click on it again to hide all the polygons, only one polygon gets hidden and I understood why that is happening

because in the for loop it keeps looping through the coordinates of each polygon one by one and displays it,so during the last iteration of for loop the value of bb is saved and given to 'if(bb)',so it takes only the last bounding box coordinates and hides only that polygon and not all.

I tried pushing all the bb values into an array and providing that as a parameter to the if() but it still is not working.

...

          <button>toggle polygon </button>

...

...

              var bb;
              d3.select('button').on('click', function() {
                 if ( bb ) {
                              bb.remove();
                              // Remove bounding box
                             bb = null;}
                      
                else{
                          for (var line = 2; line < lines.length; line  ) { //array to loop through each lines of the annotation file
                        console.log(line   " --> "   lines[line]);
                        var annotationline = lines[line].split(' '),
                            x1 = annotationline[0],
                            y1 = annotationline[1],
                            x2 = annotationline[2],
                            y2 = annotationline[3],
                            x3 = annotationline[4],
                            y3 = annotationline[5],
                            x4 = annotationline[6],
                            y4 = annotationline[7],
                
                             bb= d3.select(template).append("svg")
                            .attr("width", width)
                            .attr("height", height),
                            poly = [{
                                    "x": x1,
                                    "y": y1
                                },
                                {
                                    "x": x2,
                                    "y": y2
                                },
                                {
                                    "x": x3,
                                    "y": y3
                                },
                                {
                                    "x": x4,
                                    "y": y4
                                }
                            ];

                        bb.selectAll("polygon")
                            .data([poly])
                            .enter().append("polygon")
                            .attr("points", function(d) {
                                return d.map(function(d) {
                                    return [d.x, d.y].join(",");
                                }).join(" ");
                            })
                            .attr("stroke","red")
                            .attr("fill", "none");  }
                      }

                   }

...

CodePudding user response:

There are two problems in your code.

  1. You are appending an svg for each line.
  2. If you append a single svg, you are using data.enter to append it with last poly data which overwrites all other polygons

I have updated your code to solve both the issues

  1. Append an svg once before for loop. for each polygon append a group element ( which is a better way to do than svg)
  2. We update only group and not complete svg.

Best way to do is to write data[poly].update method where you can update svg with new polygon data. Read on web on how to enter data and update it. https://www.d3indepth.com/enterexit/

For Now below is the code which is working and toggle will remove all the polygons. Your code could have worked with

bb.remove(); d3.select('svg').remove(); // Removing all svgs after removing the latest reference

Better version of code ( this does not move your coordinates as well). Learn to check and read html generated by d3 as well. You should see the output below in full page and read the html generated.

var bb;
d3.select('button').on('click', function() {
  if (bb) {
    bb.remove();
    // Remove bounding box
    bb = null;
  } else {

    bb = d3.select(".template").append("svg")
      .attr("width", '300')
      .attr("height", '400');
    for (var line = 2; line < 4; line  ) { //array to loop through each lines of the annotation file

      var annotationline = 9,
        x1 = 23 * line / 2,
        y1 = 34   line,
        x2 = 27 * line / 2,
        y2 = 34   line,
        x3 = 145 * line / 2,
        y3 = 65 * line / 2,
        x4 = 145 * line / 2,
        y4 = 59 * line / 2,


        poly = [{
            "x": x1,
            "y": y1
          },
          {
            "x": x2,
            "y": y2
          },
          {
            "x": x3,
            "y": y3
          },
          {
            "x": x4,
            "y": y4
          }
        ];
      let g = bb.append('g');
      g.selectAll("polygon")
        .data([poly])
        .enter().append("polygon")
        .attr("points", function(d) {
          return d.map(function(d) {
            return [d.x, d.y].join(",");
          }).join(" ");
        })
        .attr("stroke", "red")
        .attr("fill", "none");
    }
  }

})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.4.4/d3.min.js" integrity="sha512-hnFpvCiJ8Fr1lYLqcw6wLgFUOEZ89kWCkO cEekwcWPIPKyknKV1eZmSSG3UxXfsSuf z/SgmiYB1zFOg3l2UQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

<button>toggle polygon </button>
<div >
</div>

  • Related