D3 滚动地图更新 d3.geo.circle,但不更新弧线

D3 Rolling map updates d3.geo.circle, but not arcs

我在 http://bl.ocks.org/patricksurry/6621971 的顶部 构建了一个可视化

基本上我在地图上添加了d3.geo.circled3.svg.arc

我观察到的是,当我pan/zoom地图时,圆圈仍然完好无损,但圆弧消失了。

当我检查 chrome 中的元素时,我发现圆弧路径的属性 'd' 消失了,但是对于圆形路径,它得到了适当的更新。

任何人都可以帮助我理解为什么更新的投影应用于圆形路径元素而不是弧形。有没有办法强制重新投影弧线而不必删除并重新创建它们?

更新 1:因为这个问题似乎很难重现,而且 jsfiddle 不允许上传地理数据文件,我在这里发布源代码:

<!DOCTYPE html>
<meta charset="utf-8">
<style>

    svg {
        background-color: lavender;
        border: 1px solid black;
    }

    path {
        fill: oldlace;
        stroke: #666;
        stroke-width: .5px;
    }

    path.circle {
        fill: red;
        stroke: #666;
        stroke-width: .5px;
    }

    path.arc1 {
        fill: green;
    }


</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script>

    var width = 1600,
            height = 400,
            rotate = 60,        // so that [-60, 0] becomes initial center of projection
            maxlat = 83;        // clip northern and southern poles (infinite in mercator)

    var projection = d3.geo.mercator()
            .rotate([rotate,0])
            .scale(1)           // we'll scale up to match viewport shortly.
            .translate([width/2, height/2]);

    // find the top left and bottom right of current projection
    function mercatorBounds(projection, maxlat) {
        var yaw = projection.rotate()[0],
                xymax = projection([-yaw+180-1e-6,-maxlat]),
                xymin = projection([-yaw-180+1e-6, maxlat]);
        return [xymin,xymax];
    }

    // set up the scale extent and initial scale for the projection
    var b = mercatorBounds(projection, maxlat),
            s = width/(b[1][0]-b[0][0]),
            scaleExtent = [s, 10*s];

    projection.scale(scaleExtent[0]);

    var zoom = d3.behavior.zoom()
            .scaleExtent(scaleExtent)
            .scale(projection.scale())
            .translate([0,0])               // not linked directly to projection
            .on("zoom", redraw);

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

    var svg = d3.selectAll('body')
            .append('svg')
            .attr('width',width)
            .attr('height',height)
            .attr('id', 'svg')
            .call(zoom);

    d3.json("js/data/world-110m2.json", function ready(error, world) {

        // adding geo paths
        svg.selectAll('path')
                .data(topojson.feature(world, world.objects.countries).features)
                .enter().append('path')

        // adding a circle
        svg.append("path")
                .datum(d3.geo.circle().angle(2).origin([-10, 0]))
                .attr("d", path)
                .attr("class", "circle");

        redraw();


        // adding a pie arc
        var r = 10;
        var p = Math.PI * 2;
        var arc1 = d3.svg.arc()
                .innerRadius(r - 5)
                .outerRadius(r)
                .startAngle(0);

        var arcData = JSON.parse('[{ "lon" : "0", "lat":"0", "endAngle":"6.4" }]');
        var arcs1 = svg.selectAll("path.arc1");
        arcs1 = arcs1.data(arcData)
                .enter()
                .append("path")
                .attr("class", "arc1")
                .attr("fill", "green")
                .attr("transform", function(d, i) {     return "translate(" + projection([d.lon, d.lat])[0] + ", " + projection([d.lon, d.lat])[1] + ")"; })
                .attr("d", arc1);

    });

    // track last translation and scale event we processed
    var tlast = [0,0],
            slast = null;

    function redraw() {
        if (d3.event) {
            var scale = d3.event.scale,
                    t = d3.event.translate;

            console.log(d3.event.scale + " [" +d3.event.translate + "]");

            // if scaling changes, ignore translation (otherwise touch zooms are weird)
            if (scale != slast) {
                projection.scale(scale);
            } else {
                var dx = t[0]-tlast[0],
                        dy = t[1]-tlast[1],
                        yaw = projection.rotate()[0],
                        tp = projection.translate();

                // use x translation to rotate based on current scale
                projection.rotate([yaw+360.*dx/width*scaleExtent[0]/scale, 0, 0]);
                // use y translation to translate projection, clamped by min/max
                var b = mercatorBounds(projection, maxlat);
                if (b[0][1] + dy > 0) dy = -b[0][1];
                else if (b[1][1] + dy < height) dy = height-b[1][1];
                projection.translate([tp[0],tp[1]+dy]);
            }
            // save last values.  resetting zoom.translate() and scale() would
            // seem equivalent but doesn't seem to work reliably?
            slast = scale;
            tlast = t;
        }

        svg.selectAll('path').attr('d', path);
    }


</script>

我终于弄清楚出了什么问题。我应该对 arc 元素应用转换。所以基本上,在我做的 redraw() 方法中:

var scaleRatio = 1;
function redraw() {

    if (d3.event) {
        var scale = d3.event.scale,
                t = d3.event.translate;

        //console.log(d3.event.scale + " [" +d3.event.translate + "]");

        // if scaling changes, ignore translation (otherwise touch zooms are weird)
        if (scale != slast) {
            projection.scale(scale);
        } else {
            var dx = t[0]-tlast[0],
                    dy = t[1]-tlast[1],
                    yaw = projection.rotate()[0],
                    tp = projection.translate();

            // use x translation to rotate based on current scale
            projection.rotate([yaw+360.*dx/width*scaleExtent[0]/scale, 0, 0]);
            // use y translation to translate projection, clamped by min/max
            var b = mercatorBounds(projection, maxlat);
            if (b[0][1] + dy > 0) dy = -b[0][1];
            else if (b[1][1] + dy < height) dy = height-b[1][1];
            projection.translate([tp[0],tp[1]+dy]);
        }
        // save last values.  resetting zoom.translate() and scale() would
        // seem equivalent but doesn't seem to work reliably?
        if(slast==null)
            scaleRatio=1;
        else
            scaleRatio = scaleRatio * (scale/slast);
        console.log(slast+'-' + scaleRatio);
        slast = scale;
        tlast = t;
    }

    svg.selectAll('path').attr('d', path);
    svg.selectAll("path.arc1")
        .attr("transform", function(d, i) {     return "translate(" + projection([d.lon, d.lat])[0] + ", " + projection([d.lon, d.lat])[1] + ")scale(" + scaleRatio + ")" })
        .attr("d", arc1);

}

但是,关于为什么 svg.path 元素需要显式重新投影,这与 d3.geo.path 元素不同,我仍然存在知识空白。希望有人能帮助我。