Search code examples
javascriptd3.jsdata-visualizationgeojson

"d3 geotransform + join" shows wrong update graphics


I was trying to apply join/update with an animation.

What I intended was

showing the green color for the newly 'entered' continent and turning a previous existing continent grey.

However,

for the first entry point, it showed green properly, but when it is updated, the newly 'entered' point was colored grey as if that is already existing data point and when I come back the map shows 'PARTIALLY GREEN AND PARTIALLY GREY', which I strongly believe 'BUG'

the code is like below.

function drawFeatures(json) {
        var svg = d3.select("#map").select("svg");

        var transform = d3.geoTransform({ point: projectPoint });
        var path = d3.geoPath().projection(transform)

        var featureElement = svg.selectAll("path")
            .data(json.features)
            .join(
                enter => enter.append("path")
                .attr("fill", 'rgba(0,100,0,0.5)'),
                exit => exit
                .attr('fill', 'rgba(10,10,10,0.2)')
            )

        map.on("viewreset", update);
        map.on('zoomend', update)

        update();

        function update() {
            featureElement.attr("d", path);
        }

    }

and the full code is in the following link.

https://codepen.io/jotnajoa/project/editor/ZjaOax

Thank you in advance.


Solution

  • You are not using selection.join() correctly. While you are treatingt the second function as handling the exit, it is handling the update as it is the second parameter passed to .join(). The first function passed to join handles enter, the second handles update, and the third handles exit. If you don't remove the third value, join() removes the exit selection with selection.remove().

    In your case, the behavior can be explained as follows:

    1. You enter some data initially, all works as planned.
    2. You bind a new data array and conduct a join. Any existing paths are assigned items in the new data array, these are colored grey because they are being updated with new data. If there are excess items in the data array, then they are entered, and you enter them green. If there are more elements than items in the data array in the selection, they are removed in the exit selection.

    If you want the exit selection to be colored differently, then you need to pass three arguments to join:

            .join(
                enter => enter.append("path").attr("fill", 'rgba(0,100,0,0.5)'),
                update => update,
                exit => exit.attr('fill', 'rgba(10,10,10,0.2)')
            )
    

    As a side note, unless using the second parameter of .data(), items in the data array are matched to elements in the selection by index, and this will determine which items are entered or what elements are exited (you can't have both with out specifying the second parameter of .data().