如何防止新元素覆盖d3.js中的现有元素?

时间:2014-06-25 13:06:30

标签: javascript d3.js geolocation

我正在玩mbostock's example of using d3.js with google maps,尝试使用我自己数据的二维数组进行调整。

我的数据是一个对象数组,每个对象都有一个时间戳和一个GeoCoordinates数组Coordinates

[{"TimeStamp":"2014-06-18T22:18:07+04:30","Coordinates":[{"Latitude":40.416775,"Longitude":-3.70379},{"Latitude":40.415793,"Longitude":-3.707424},{"Latitude":40.414142,"Longitude":-3.707982}]}
,{"TimeStamp":"2014-06-18T22:23:07+04:30","Coordinates":[{"Latitude":40.411365,"Longitude":-3.708712},{"Latitude":40.411986,"Longitude":-3.705021},{"Latitude":40.406774,"Longitude":-3.711716}]}
,{"TimeStamp":"2014-06-18T22:28:07+04:30","Coordinates":[{"Latitude":40.401365,"Longitude":-3.720449},{"Latitude":40.388455,"Longitude":-3.731843},{"Latitude":40.383568,"Longitude":-3.738881}]}]

我无法找到迭代所有Coordinate对象的方法,因此为第一级数组的每个元素添加了for循环:

// Add the container when the overlay is added to the map.
overlay.onAdd = function() {
    var layer = d3.select(this.getPanes().overlayLayer).append("div")
        .attr("class", "stations");

    // Draw each marker as a separate SVG element.
    // We could use a single SVG, but what size would it have?
    overlay.draw = function() {
        var projection = this.getProjection(),
          padding = 10;
        for( var i = 0; i < data.length; i++)
        {
            var marker = layer.selectAll("svg")
              .data(data[i].Coordinates)
              .each(transform) // update existing markers
              .enter()
              .append("svg:svg")
              .each(transform)
              .attr("class", "marker");

            // Add a circle.
            marker.append("svg:circle")
              .attr("r", 4.5)
              .attr("cx", padding)
              .attr("cy", padding);

            // Add a label.
            marker.append("svg:text")
              .attr("x", padding + 7)
              .attr("y", padding)
              .attr("dy", ".31em")
              .text(function(d, i){
                    return i;
                    });
        }

        function transform(d) {
                d = new google.maps.LatLng(d.Latitude, d.Longitude);
                d = projection.fromLatLngToDivPixel(d);
                return d3.select(this)
                    .style("left", (d.x - padding) + "px")
                    .style("top", (d.y - padding) + "px");
        }
    };
};

但是,d3会在每次迭代中覆盖标记svg元素,在这种情况下只显示3个标记而不是9个。

为什么?

3 个答案:

答案 0 :(得分:1)

要直接回答这个问题,d3会覆盖每个循环上的SVG元素,因为它们是按照它们在数组中的位置编制索引的。您必须提供id函数,这是data()函数的第二个参数。代码要遵循。

嵌套选择不适合这种情况,因为您不想要嵌套的html,原因在于链接代码中指出的原因:&#34;我们可以使用单个SVG,但是大小会是多少它有?&#34;。所以我们确实想在某个时候使用for循环。

另请注意,只有“进入阶段”才会进入&#39;这需要进入for循环,但如果你把渲染放在那里也不应该有很大的不同。

var map = new google.maps.Map(d3.select("#map").node(), {
  zoom: 8,
  center: new google.maps.LatLng(40.411365, -3.708712),
  mapTypeId: google.maps.MapTypeId.ROADMAP
});

 // Load the station data. When the data comes back, create an overlay.
d3.json("soQuest.json", function(data) {
  var overlay = new google.maps.OverlayView();

  // Add the container when the overlay is added to the map.
  overlay.onAdd = function() {
    var layer = d3.select(this.getPanes().overlayLayer).append("div")
      .attr("class", "stations");

    // Draw each marker as a separate SVG element.
    // We could use a single SVG, but what size would it have?
    overlay.draw = function() {
      var projection = this.getProjection(),
        padding = 10,
        marker;


      for (i = 0; i < data.length; i++) {
      marker = layer.selectAll("svg")
          .data(data[i].Coordinates, function(d, index){
            return data[i].TimeStamp + index + d.Latitude + d.Longitude;
          })
          .enter().append("svg");
      }

      marker = layer.selectAll("svg")
        .each(transform) // update existing markers
      .attr("class", "marker");

      // Add a circle.
      marker.append("svg:circle")
        .attr("r", 4.5)
        .attr("cx", padding)
        .attr("cy", padding);

      // Add a label.
      marker.append("svg:text")
        .attr("x", padding + 7)
        .attr("y", padding)
        .attr("dy", ".31em")
        .text(function(d) {
          return "MARKER"; //d.key;
        });

      function transform(d) {
        d = new google.maps.LatLng(d.Latitude, d.Longitude);
        d = projection.fromLatLngToDivPixel(d);
        return d3.select(this)
          .style("left", (d.x - padding) + "px")
          .style("top", (d.y - padding) + "px");
      }
    };
  };

  // Bind our overlay to the map…
  overlay.setMap(map);
});

答案 1 :(得分:0)

我建议你尝试在for循环之外定义变量marker。如果能解决问题,请告诉我。 :)

答案 2 :(得分:0)

可能最简单的方法就是将阵列弄平,

var flat_data = [];

for (var i=0; i<data.length; i++){
   for (var j=0; j<data[i].length; j++){
       flat_data.push(data[i][j])
   }
}

然后跳过你的for循环,然后执行

var layer = d3.select(this.getPanes().overlayLayer).append("div")
        .attr("class", "stations");

    // Draw each marker as a separate SVG element.
    // We could use a single SVG, but what size would it have?
    overlay.draw = function() {
        var projection = this.getProjection(),
          padding = 10;

        var marker = layer.selectAll("svg")
           .data(flat_data)
           .each(transform) // update existing markers
           .enter()
           .append("svg:svg")
           .each(transform)
           .attr("class", "marker");

        // Add a circle.
        marker.append("svg:circle")
           .attr("r", 4.5)
           .attr("cx", padding)
           .attr("cy", padding);

        // Add a label.
        marker.append("svg:text")
           .attr("x", padding + 7)
           .attr("y", padding)
           .attr("dy", ".31em")
           .text(function(d, i){
              return i;
           });
        }

    function transform(d) {
        d = new google.maps.LatLng(d.Latitude, d.Longitude);
        d = projection.fromLatLngToDivPixel(d);
           return d3.select(this)
              .style("left", (d.x - padding) + "px")
              .style("top", (d.y - padding) + "px");
        }
    };
};