d3出口选择显示为空,即使具有唯一的id值



我正在尝试制作我自己版本的带有标记的旋转地球仪,类似于这里的Patrick Stotz。虽然我的目标不是城市,但我现在正在使用它,并有一个较小版本的数据geoJSON文件(具有相同的结构(。问题是,我显然不明白如何正确地获得退出选择,因为它总是空的,尽管我已经更新了geoJSON文件,使每个功能都有一个唯一的id。

我想介绍的主要区别是,一次绘制100个城市,然后删除这些标记,用一些简单的过渡效果绘制下一个100个城市。我使用d3.timer函数和对数据数组进行切片来完成此操作。

问题是我的exit选择总是空的,即使在我按照这个问题的建议添加了一个唯一的id之后也是如此。

以下是js的第一部分,它设置了全球变量、全球和国家(与Patrick的原始版本非常相似(:

var width  = 820;
var height = 620;
var rScale = d3.scale.sqrt();
var amountPerPixel = 12500;
var max_population = [];
var index = 0;
// Configuration for the spinning effect
var time = Date.now();
var rotate = [0, 0];
var velocity = [0.025, -0];
// set projection type and paremetes
var projection = d3.geo.orthographic()
.scale(300)
.translate([(width / 2) + 100, height / 2])
.clipAngle(90)
.precision(0.3);
// create path variable, empty svg element and group container
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("svg");
var g = svg.append("g");
// drawing dark grey spehere as landmass
g.append("path")
.datum({type: "Sphere"})
.attr("class", "sphere")
.attr("d", path)
.attr("fill", "#0D0D0D");
var countries = svg.append("g").selectAll("path.countries");
var cities = svg.append("g").selectAll("path.cities");
// draw country lines
d3.json("countries.geojson", function(error, data) {
countries.data(data.features)
.enter().append("path")
.attr("class", "countries")
.attr("d", path)
});

以下三个函数加载城市数据,处理一些基本函数,如大小和半径的比例,绘制初始数据和旋转地球(也类似于Patrick的原始函数,但有一些变化(:

d3.json("cities.geojson", function(error, data) {
// Handle errors getting and parsing the data
if (error) { console.log(error); return error; }
// setting the circle size (not radius!) according to the number of inhabitants per city
amount_array = [];
for (i = 0; i < data.features.length; i++) {
data.features[i].properties.id = i;
amount_array.push(data.features[i].properties.population);
}
max_amount = amount_array.sort(d3.descending)[0]
var rMin = 0;
var rMax = Math.sqrt(max_amount / (amountPerPixel * Math.PI));
rScale.domain([0, max_amount]);
rScale.range([rMin, rMax]);
path.pointRadius(function(d) {
return d.properties ? rScale(d.properties.population) : 1;
});
initialData = data.features.slice(index,index+100);
// Drawing transparent circle markers for cities
cities.data(initialData, function(d) { return d.properties.id; })
.enter().append("path")
.attr("id", function(d) {return d.properties.id;})
.attr("class", "cities")
.attr("d", path)
.attr("fill", "#ffba00")
.attr("fill-opacity", 0.1);
svg.selectAll("path.cities").transition()
.duration(250)
.ease("quad")
.attr("fill-opacity", 0.75);
// start spinning!
spinning_globe();
// update new data points
update_points(data);
});
function update_points(data) {
d3.timer(function() {
index += 100;
newData = data.features.slice(index,index+100)
var newCities = cities.data(newData, function(d) { return d.properties.id; });
newCities.enter()
.append("path")
.attr("id", function(d) {return d.properties.id;})
.attr("class", "cities")
.attr("d", path)
.attr("fill", "#ffba00")
.attr("fill-opacity", 0.1);

newCities.exit().remove();
svg.selectAll("path.cities").transition()
.duration(250)
.ease("quad")
.attr("fill-opacity", 0.75);
if(newData.length == 0) stop();
}, 1000);
};
function spinning_globe(){
d3.timer(function() {
// get current time
var dt = Date.now() - time;
// get the new position from modified projection function
projection.rotate([rotate[0] + velocity[0] * dt, rotate[1] + velocity[1] * dt]);
// update cities position = redraw
svg.selectAll("path.countries").attr("d",path);
svg.selectAll("path.cities").attr("d", path);
});
};

我尝试了一种强力方法来删除当前的城市标记(svg.selectAll("path.cities").remove()(,但时机不对。

最终的结果是一切正常——地球自转,城市点亮——但没有一个城市被拆除。我做错了什么?

也许问题在于更新后没有重新分配cities变量。如果我的理解是正确的,那么第一次调用update_points时,一切都应该按预期进行。但当它将被第二次调用时,cities选择仍然是指前100个城市,这些城市已经被删除。

解决方案是分配cities = newCities.enter()...

惯用的方法是分配cities = newCities.enter() .... .merge(newCities) ...,但在您的情况下不是必需的,因为接下来的100个城市不与以前的城市相交,并且更新部分为空。

您希望处理更改选择和绑定数据,因此不要保留(现在("旧"选择的记录。

// init
var cities = svg.append("g");
// init-draw
cities.selectAll(".cities")
.data(initialData, function(d) { return d.properties.id; })
.enter()
.append("path")
.attr("id", function(d) {return d.properties.id;})
.attr("class", "cities")
.attr("d", path)
.attr("fill", "#ffba00")
.attr("fill-opacity", 0.1);
// update
var newCities = cities.selectAll(".cities")
.data(newData, function(d) { return d.properties.id; });
newCities.enter()
.append("path")
.attr("id", function(d) {return d.properties.id;})
.attr("class", "cities")
.attr("d", path)
.attr("fill", "#ffba00")
.attr("fill-opacity", 0.1)
.merge(newCities);
.transition()
.duration(250)
.ease("quad")
.attr("fill-opacity", 0.75);
newCities.exit().transition().duration(250).attr("fill-opacity", 0).remove();

init draw和update几乎相同,所以将它们合并为一个,调用init draw的update。

最新更新