d3 exit selection comes up empty even with unique id value











up vote
0
down vote

favorite












I'm trying to make my own version of a spinning globe with markers, similar to Patrick Stotz' here. Though my goal is not cities, I'm using that for now and have a smaller version of this geoJSON file for data (with the same structure). The problem is I clearly don't understand how to correctly get an exit selection because it's always empty, even though I've updated the geoJSON file to have a unique id for each feature.



The main difference I'm trying to introduce is to plot 100 cities at a time, then remove those markers and plot the next 100 with some simple transition effects. I do this with a d3.timer function and slicing the data array.



The problem is my exit selection is always empty, even after I've added a unique id following the advice from this question.



Here's the first part of the js that setups the global variables, the globe and countries (very similar to Patrick's original):



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)

});


The following three functions load the cities data, handle some basic functions like a scale for size and radius, plot the initial data and spin the globe (also similar to Patrick's original with some changes):



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);

});

};


I've tried a brute force method for removing the current city markers (svg.selectAll("path.cities").remove()) but the timing doesn't work out right.



The end result is that everything works right -- the glob spins, the cities light up -- but no cities are ever removed. What am I doing wrong?










share|improve this question


















  • 1




    Exit collection is empty if you dont have enough DOM nodes to bind your data to to begin with. If you selection has 20 nodes, your data has 10 elements, exit collection will have 10 nodes, if your selection has 10 nodes, your data has 20 nodes, exit collection will be empty
    – Dummy
    Nov 22 at 17:59

















up vote
0
down vote

favorite












I'm trying to make my own version of a spinning globe with markers, similar to Patrick Stotz' here. Though my goal is not cities, I'm using that for now and have a smaller version of this geoJSON file for data (with the same structure). The problem is I clearly don't understand how to correctly get an exit selection because it's always empty, even though I've updated the geoJSON file to have a unique id for each feature.



The main difference I'm trying to introduce is to plot 100 cities at a time, then remove those markers and plot the next 100 with some simple transition effects. I do this with a d3.timer function and slicing the data array.



The problem is my exit selection is always empty, even after I've added a unique id following the advice from this question.



Here's the first part of the js that setups the global variables, the globe and countries (very similar to Patrick's original):



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)

});


The following three functions load the cities data, handle some basic functions like a scale for size and radius, plot the initial data and spin the globe (also similar to Patrick's original with some changes):



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);

});

};


I've tried a brute force method for removing the current city markers (svg.selectAll("path.cities").remove()) but the timing doesn't work out right.



The end result is that everything works right -- the glob spins, the cities light up -- but no cities are ever removed. What am I doing wrong?










share|improve this question


















  • 1




    Exit collection is empty if you dont have enough DOM nodes to bind your data to to begin with. If you selection has 20 nodes, your data has 10 elements, exit collection will have 10 nodes, if your selection has 10 nodes, your data has 20 nodes, exit collection will be empty
    – Dummy
    Nov 22 at 17:59















up vote
0
down vote

favorite









up vote
0
down vote

favorite











I'm trying to make my own version of a spinning globe with markers, similar to Patrick Stotz' here. Though my goal is not cities, I'm using that for now and have a smaller version of this geoJSON file for data (with the same structure). The problem is I clearly don't understand how to correctly get an exit selection because it's always empty, even though I've updated the geoJSON file to have a unique id for each feature.



The main difference I'm trying to introduce is to plot 100 cities at a time, then remove those markers and plot the next 100 with some simple transition effects. I do this with a d3.timer function and slicing the data array.



The problem is my exit selection is always empty, even after I've added a unique id following the advice from this question.



Here's the first part of the js that setups the global variables, the globe and countries (very similar to Patrick's original):



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)

});


The following three functions load the cities data, handle some basic functions like a scale for size and radius, plot the initial data and spin the globe (also similar to Patrick's original with some changes):



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);

});

};


I've tried a brute force method for removing the current city markers (svg.selectAll("path.cities").remove()) but the timing doesn't work out right.



The end result is that everything works right -- the glob spins, the cities light up -- but no cities are ever removed. What am I doing wrong?










share|improve this question













I'm trying to make my own version of a spinning globe with markers, similar to Patrick Stotz' here. Though my goal is not cities, I'm using that for now and have a smaller version of this geoJSON file for data (with the same structure). The problem is I clearly don't understand how to correctly get an exit selection because it's always empty, even though I've updated the geoJSON file to have a unique id for each feature.



The main difference I'm trying to introduce is to plot 100 cities at a time, then remove those markers and plot the next 100 with some simple transition effects. I do this with a d3.timer function and slicing the data array.



The problem is my exit selection is always empty, even after I've added a unique id following the advice from this question.



Here's the first part of the js that setups the global variables, the globe and countries (very similar to Patrick's original):



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)

});


The following three functions load the cities data, handle some basic functions like a scale for size and radius, plot the initial data and spin the globe (also similar to Patrick's original with some changes):



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);

});

};


I've tried a brute force method for removing the current city markers (svg.selectAll("path.cities").remove()) but the timing doesn't work out right.



The end result is that everything works right -- the glob spins, the cities light up -- but no cities are ever removed. What am I doing wrong?







javascript d3.js data-visualization






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 22 at 17:27









tchaymore

1,825123878




1,825123878








  • 1




    Exit collection is empty if you dont have enough DOM nodes to bind your data to to begin with. If you selection has 20 nodes, your data has 10 elements, exit collection will have 10 nodes, if your selection has 10 nodes, your data has 20 nodes, exit collection will be empty
    – Dummy
    Nov 22 at 17:59
















  • 1




    Exit collection is empty if you dont have enough DOM nodes to bind your data to to begin with. If you selection has 20 nodes, your data has 10 elements, exit collection will have 10 nodes, if your selection has 10 nodes, your data has 20 nodes, exit collection will be empty
    – Dummy
    Nov 22 at 17:59










1




1




Exit collection is empty if you dont have enough DOM nodes to bind your data to to begin with. If you selection has 20 nodes, your data has 10 elements, exit collection will have 10 nodes, if your selection has 10 nodes, your data has 20 nodes, exit collection will be empty
– Dummy
Nov 22 at 17:59






Exit collection is empty if you dont have enough DOM nodes to bind your data to to begin with. If you selection has 20 nodes, your data has 10 elements, exit collection will have 10 nodes, if your selection has 10 nodes, your data has 20 nodes, exit collection will be empty
– Dummy
Nov 22 at 17:59














2 Answers
2






active

oldest

votes

















up vote
2
down vote



accepted










Perhaps the problem is that you do not reassign cities variable after update. If my understanding is correct, the first time update_points is called, everything should go as expected. But when it will be called the second time, cities selection still refers to the first 100 cities, which have been deleted.



The solution is to assign cities = newCities.enter()....



The idiomatic way is to assign cities = newCities.enter() .... .merge(newCities) ..., but is is not required in your case, since the next 100 cities do not intersect previous cities and update section is empty.






share|improve this answer





















  • This is great, helped a lot. I've got the selections to exit now, and it also helped switching to d3.interval(). Now I just need to get the transitions to work correctly. Thanks!
    – tchaymore
    Nov 22 at 20:35


















up vote
-1
down vote













you want to work with changing selections and bind data so do not keep record of the (now) "old" selection.



// 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();


The init-draw and the update are near identical so combine them into one, call the update for the init-draw.






share|improve this answer





















    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53435839%2fd3-exit-selection-comes-up-empty-even-with-unique-id-value%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    2
    down vote



    accepted










    Perhaps the problem is that you do not reassign cities variable after update. If my understanding is correct, the first time update_points is called, everything should go as expected. But when it will be called the second time, cities selection still refers to the first 100 cities, which have been deleted.



    The solution is to assign cities = newCities.enter()....



    The idiomatic way is to assign cities = newCities.enter() .... .merge(newCities) ..., but is is not required in your case, since the next 100 cities do not intersect previous cities and update section is empty.






    share|improve this answer





















    • This is great, helped a lot. I've got the selections to exit now, and it also helped switching to d3.interval(). Now I just need to get the transitions to work correctly. Thanks!
      – tchaymore
      Nov 22 at 20:35















    up vote
    2
    down vote



    accepted










    Perhaps the problem is that you do not reassign cities variable after update. If my understanding is correct, the first time update_points is called, everything should go as expected. But when it will be called the second time, cities selection still refers to the first 100 cities, which have been deleted.



    The solution is to assign cities = newCities.enter()....



    The idiomatic way is to assign cities = newCities.enter() .... .merge(newCities) ..., but is is not required in your case, since the next 100 cities do not intersect previous cities and update section is empty.






    share|improve this answer





















    • This is great, helped a lot. I've got the selections to exit now, and it also helped switching to d3.interval(). Now I just need to get the transitions to work correctly. Thanks!
      – tchaymore
      Nov 22 at 20:35













    up vote
    2
    down vote



    accepted







    up vote
    2
    down vote



    accepted






    Perhaps the problem is that you do not reassign cities variable after update. If my understanding is correct, the first time update_points is called, everything should go as expected. But when it will be called the second time, cities selection still refers to the first 100 cities, which have been deleted.



    The solution is to assign cities = newCities.enter()....



    The idiomatic way is to assign cities = newCities.enter() .... .merge(newCities) ..., but is is not required in your case, since the next 100 cities do not intersect previous cities and update section is empty.






    share|improve this answer












    Perhaps the problem is that you do not reassign cities variable after update. If my understanding is correct, the first time update_points is called, everything should go as expected. But when it will be called the second time, cities selection still refers to the first 100 cities, which have been deleted.



    The solution is to assign cities = newCities.enter()....



    The idiomatic way is to assign cities = newCities.enter() .... .merge(newCities) ..., but is is not required in your case, since the next 100 cities do not intersect previous cities and update section is empty.







    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Nov 22 at 17:46









    Yaroslav Sergienko

    40016




    40016












    • This is great, helped a lot. I've got the selections to exit now, and it also helped switching to d3.interval(). Now I just need to get the transitions to work correctly. Thanks!
      – tchaymore
      Nov 22 at 20:35


















    • This is great, helped a lot. I've got the selections to exit now, and it also helped switching to d3.interval(). Now I just need to get the transitions to work correctly. Thanks!
      – tchaymore
      Nov 22 at 20:35
















    This is great, helped a lot. I've got the selections to exit now, and it also helped switching to d3.interval(). Now I just need to get the transitions to work correctly. Thanks!
    – tchaymore
    Nov 22 at 20:35




    This is great, helped a lot. I've got the selections to exit now, and it also helped switching to d3.interval(). Now I just need to get the transitions to work correctly. Thanks!
    – tchaymore
    Nov 22 at 20:35












    up vote
    -1
    down vote













    you want to work with changing selections and bind data so do not keep record of the (now) "old" selection.



    // 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();


    The init-draw and the update are near identical so combine them into one, call the update for the init-draw.






    share|improve this answer

























      up vote
      -1
      down vote













      you want to work with changing selections and bind data so do not keep record of the (now) "old" selection.



      // 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();


      The init-draw and the update are near identical so combine them into one, call the update for the init-draw.






      share|improve this answer























        up vote
        -1
        down vote










        up vote
        -1
        down vote









        you want to work with changing selections and bind data so do not keep record of the (now) "old" selection.



        // 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();


        The init-draw and the update are near identical so combine them into one, call the update for the init-draw.






        share|improve this answer












        you want to work with changing selections and bind data so do not keep record of the (now) "old" selection.



        // 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();


        The init-draw and the update are near identical so combine them into one, call the update for the init-draw.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 22 at 21:34









        rioV8

        3,8342211




        3,8342211






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53435839%2fd3-exit-selection-comes-up-empty-even-with-unique-id-value%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            What visual should I use to simply compare current year value vs last year in Power BI desktop

            How to ignore python UserWarning in pytest?

            Alexandru Averescu