1
\$\begingroup\$

I was wondering if I could get some input on how to make my code more concise, since I think in particular with the force simulation sections I have some repetition. And any other input to make my code more readable would be great. I have a mixing of jQuery and D3 but that is just a quick demo for putting some working code together since there is a lot more to the final visualization, and it doesn't actually use jQuery.

This code basically creates a force simulation using D3, reads in data (on sharks), and at first the nodes are sized based on the size of the shark and the nodes are clustered based on shark families. If the user clicks on the "Greenland shark" button then only the node with the common_name property "Greenland Shark" would be visible. And if the user clicks on the button "Sharks by family" then all the nodes would appear again clustered by family.

Plnkr

  var margin = {
      top: 50,
      right: 50,
      bottom: 20,
      left: 50
  };



  var width = 1200 - 300 - margin.left - margin.right;

  var heightInitial = 700;
  var height = heightInitial - margin.top - margin.bottom;


  var svg = d3.select("#graph")
      .style("width", width + margin.left + margin.right + "px")
      .append("svg")
      .style("width", width + margin.left + margin.right + "px")
      .style("height", height + margin.top + margin.bottom + "px")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom).attr("class", "svg")

  var g = svg.append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");


  var forceStrength = 0.03;

  var colorScale = d3.scaleOrdinal()
      .range(['#450303', '#6e0505', '#951f1f', '#d09b9b', '#ad5151', '#e7cdcd',
          '#b30000', '#e34a33'
      ])

  var familyXScale = d3.scaleOrdinal()
      .range([width * 0.2,
          width * 0.4,
          width * 0.55,
          width * 0.7,
          width * 0.8,
          width * 0.9
      ]);

  var familyYScale = d3.scaleOrdinal()
      .range([height * 0.2,
          height * 0.4,
          height * 0.55,
          height * 0.7,
          height * 0.8,
          height * 0.9
      ]);

  var yPositionScale = d3.scaleLinear()
      .domain([1, 50])
      .range([height / 2 - 50, height / 2 + 50])

  var center = {
      x: (width + margin.left + margin.right) / 2,
      y: (height + margin.top + margin.bottom) / 2
  };

  var radiusScale = d3.scaleSqrt()
      .range([0, 1])

  d3.queue()
      .defer(d3.csv, "./data2.csv")
      .await(ready)

  function ready(error, nodes) {

      var families = d3.map(nodes, function(d) {
          return d.family
      }).keys();

      var sharks = nodes.map(function(d) {
          return d.name
      });

      var indexarray = [...Array(families.length).keys()];

      var mapIndex = d3.scaleOrdinal().domain(families).range(indexarray);

      colorScale
          .domain(families)

      familyXScale
          .domain(families)

      var simulation = d3.forceSimulation();

      simulation.force('x', d3.forceX(function(d) {
              var i = mapIndex(d.family);
              return xposition(i)
          }).strength(0.03))
          .force('y', d3.forceY(function(d) {
              var i = mapIndex(d.family);
              return yposition(i)
          }).strength((0.03)))
          .force('collide', d3.forceCollide(function(d) {
              return radiusScale(+d.size)
          })).velocityDecay(0.1).alphaDecay(0.001);


      var circles = g.selectAll(".sharks")
          .data(nodes)
          .enter().append("circle")
          .attr("class", "sharks")
          .attr("r", function(d) {
              return radiusScale(+d.size)
          })
          .attr("fill", function(d) {
              return colorScale(d.family)
          })
          .attr('stroke', '')


      simulation.nodes(nodes)
          .on('tick', ticked);


      nodes.forEach(function(d) {
          d.x = familyXScale(d.family)
          d.y = yPositionScale(sharks.indexOf(d.name))
      })


      function ticked() {
          circles
              .attr("cx", function(d) {
                  return d.x
              })
              .attr("cy", function(d) {
                  return d.y
              })
      }



      function charge(d) {
          return -Math.pow(d.radius, 2.0) * forceStrength;
      }

      $("#greenlandshark").on('click', function() {
          greenlandShark();
      })

      $("#sharksbyfamily").on('click', function() {
          sharksByFamilyRev();
      })



      function sharksByFamilyRev() {

          circles = g.selectAll(".sharks").data(nodes);

          circles.exit().transition().duration(750)
              .attr("r", 0)
              .remove();

          circles.transition().duration(750)
              .attr("fill", function(d) {
                  return colorScale(d.family)
              }).attr("r", function(d) {
                  return radiusScale(+d.size);
              })

          circles = circles.enter().append("circle").attr("class", "sharks")
              .attr("fill", function(d) {
                  return colorScale(d.family)
              }).attr("r", function(d) {
                  return radiusScale(+d.size);
              })
              .attr('stroke', '')
              .merge(circles);

          simulation.force('x', d3.forceX(function(d) {
                  var i = mapIndex(d.family);
                  return xposition(i)
              }).strength(0.03))
              .force('y', d3.forceY(function(d) {
                  var i = mapIndex(d.family);
                  return yposition(i)
              }).strength((0.03)))
              .force('collide', d3.forceCollide(function(d) {
                  return radiusScale(+d.size)
              })).velocityDecay(0.1).alphaDecay(0.001);

          simulation.nodes(nodes)
              .on('tick', ticked);

          simulation.alpha(1).restart();

      }

      function greenlandShark() {

          var newNodes = filterNodes('common_name', 'Greenland shark');

          circles = g.selectAll(".sharks").data(newNodes);

          circles.exit()
              .transition()
              .duration(1000)
              .attr("r", 0)
              .remove();

          circles
              .attr('r', function(d) {
                  return radiusScale(+d.size)
              })
              .attr('fill', function(d) {
                  return colorScale(d.family)
              });

          simulation.nodes(newNodes)
              .on('tick', ticked);

          simulation.force('x', d3.forceX().strength(0.03).x(center.x))
              .force('y', d3.forceY(function(d) {
                  return height / 2
              }).strength((0.03)));

          simulation.alpha(1).restart();

      }

      function filterNodes(key, group) {
          var newnodes = nodes.filter(function(d) {
              return d[key] == group;
          });
          return newnodes;
      }


  }


  function xposition(i) {
      if (i % 6 == 0) {
          return width * 0.2
      } else if (i % 6 == 1) {
          return width * 0.4
      } else if (i % 6 == 2) {
          return width * 0.55
      } else if (i % 6 == 3) {
          return width * 0.7
      } else if (i % 6 == 4) {
          return width * 0.8
      } else {
          return width * 0.9
      }
  }

  function yposition(i) {
      if (i >= 0 && i <= 5) {
          return width * 0.2
      } else if (i >= 6 && i <= 11) {
          return width * 0.4
      } else if (i >= 12 && i <= 17) {
          return width * 0.55
      } else if (i >= 18 && i <= 23) {
          return width * 0.7
      } else if (i >= 24 && i <= 29) {
          return width * 0.8
      } else {
          return width * 0.9
      }
  }
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.