Как правильно отсортировать данные для моей пузырьковой карты d3, чтобы маленькие пузырьки отображались поверх больших пузырьков?

#javascript #d3.js

Вопрос:

Я делаю пузырчатую карту, похожую на эту: https://observablehq.com/@d3/bubble-map

Все работает, за исключением того, что мои маленькие пузырьки не всегда появляются поверх больших. Я не понимаю, почему, так как я отсортировал данные, прежде чем рисовать круги. Кто-нибудь может понять, что я делаю не так?

Вот планкер: https://plnkr.co/edit/JKWeQKkhN2TQwvNZ?open=lib/script.js

Код приведен ниже. Другие файлы слишком велики для переполнения стека, но к ним можно получить доступ через плунжер.

 <!DOCTYPE html>
<meta charset="utf-8">
<style>
  .states {
    fill: #d3d3d3;
    stroke: #ffffff;
    stroke-linejoin: round;
  }

  div.tooltip {
    position: absolute;
    left: 75px;
    text-align: center;
    height: 12px;
    padding: 8px;
    font-size: 13px;
    font-family: 'Proxima-Nova', sans-serif;
    background: #FFFFFF;
    border: 1px solid #989898;
    pointer-events: none;
  }

  .block {
    width: 18%;
    height: 15px;
    display: inline-block;
  }
</style>

<body>

  <div class="g-chart"></div>
</body>

<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://unpkg.com/geo-albers-usa-territories@0.1.0/dist/geo-albers-usa-territories.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/queue-async/1.0.7/queue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/1.6.19/topojson.min.js"></script>

<script>
  var div = d3.select("body").append("div")
    .attr("class", "tooltip")
    .style("opacity", 0);

  var margin = { top: 10, left: 10, bottom: 10, right: 10 },
    width = window.outerWidth,
    width = width - margin.left - margin.right,
    mapRatio = .5,
    height = width * mapRatio;

  const epsilon = 1e-6;

  var projection = geoAlbersUsaTerritories.geoAlbersUsaTerritories()
    .scale(width)
    .translate([width / 2, height / 2]);

  var path = d3.geoPath()
    .projection(projection);

  var map = d3.select(".g-chart").append("svg")
    .style('height', height   'px')
    .style('width', width   'px')
    .call(d3.zoom().on("zoom", function () {
      map.attr("transform", d3.event.transform)
      d3.selectAll()
    }))
    .append("g");


  queue()
    .defer(d3.json, "us.json")
    .defer(d3.csv, "test.csv")
    .await(ready);

  d3.selection.prototype.moveToFront = function () {
    return this.each(function () {
      this.parentNode.appendChild(this);
    });
  };

  d3.selection.prototype.moveToBack = function () {
    return this.each(function () {
      var firstChild = this.parentNode.firstChild;
      if (firstChild) {
        this.parentNode.insertBefore(this, firstChild);
      }
    });
  };

  function ready(error, us, data) {
    if (error) throw error;


    data.forEach(function (d) {
      d.amount =  d.amount;
    })

    map.append("g")
      .attr("class", "states")
      .selectAll("path")
      .data(topojson.feature(us, us.objects.states).features)
      .enter().append("path")
      .attr("d", path);

    // sort by descending size so that the smaller circles are drawn on top - not working

    map.append('g')
      .attr('class', 'facility')
      .selectAll("circle")
      .data(data.sort((a, b) =>  b.amount -  a.amount))
      .enter()
      .append("circle")
      .attr("cx", function (d) {
        return projection([d.longitude, d.latitude])[0];
      })
      .attr("cy", function (d) {
        return projection([d.longitude, d.latitude])[1];
      })
      .attr('r', function (d) {
        if (d.amount <= 25) { return 3 }
        else if (d.amount > 25 amp;amp; d.amount <= 50) { return 5 }
        else if (d.amount > 50 amp;amp; d.amount <= 75) { return 7 }
        else { return 9 }
      })
      .style("fill", "#EF4136")
      .style("stroke", "#BE2C2D")
      .style("stroke-width", 1)
      .style("opacity", 0.5)

      .on("mouseover", function (d) {
        var sel = d3.select(this);
        sel.moveToFront();
        d3.select(this).transition().duration(0)
          .style("opacity", 0.8)
          .style("stroke", "#FFFFFF")
        div.transition().duration(0)
          .style("opacity", .85)
        div.text(d.amount)
          .style("left", (d3.event.pageX)   "px")
          .style("top", (d3.event.pageY - 30)   "px");
      })
      .on("mouseout", function () {
        var sel = d3.select(this);
        d3.select(this)
          .transition().duration(0)
          .style("opacity", 0.5)
          .style("stroke", "#BE2C2D")
        div.transition().duration(0)
          .style("opacity", 0);
      });


    //RESPONSIVENESS
    d3.select(window).on('resize', resize);

    function resize() {

      var w = d3.select(".g-chart").node().clientWidth;

      width = w - margin.left - margin.right;
      height = width * mapRatio;

      var newProjection = d3.geoAlbersUsa()
        .scale(width)
        .translate([width / 2, height / 2]);

      path = d3.geoPath()
        .projection(newProjection);

      map.selectAll("circle")
        .attr("cx", function (d) {
          return newProjection([d.longitude, d.latitude])[0];
        })
        .attr("cy", function (d) {
          return newProjection([d.longitude, d.latitude])[1];
        })
        .attr("r", 5)

      map
        .style('width', width   'px')
        .style('height', height   'px');

      map.selectAll("path").attr('d', path);
    }

  }

</script>
 

Ответ №1:

Я бы предложил вам разделить ваши данные на пару отдельных наборов данных, сгруппированных по размеру, и создать отдельную группу ( g элемент) для каждого из них. Это также устранит проблемы с выделением кругов.

Я немного обновил ваш планкер, чтобы он работал так, как описано (проверьте строки 91-167). https://plnkr.co/edit/rayo5IZQrBqfqBWR?open=lib/script.jsamp;preview

Также проверьте методы повышения и понижения. Они могут быть хорошей заменой вашим moveToFront методам и moveToBack методам. https://riptutorial.com/d3-js/example/18029/svg—the-drawing-order

Комментарии:

1. Большое спасибо. И спасибо также за то, что сообщили мне о методах повышения и понижения. Действительно ценю это!