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

时间:2015-02-19 10:47:15

标签: d3.js geojson

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

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

Result

我观察到的是当我平移/缩放地图时,圆圈保持不变,但弧线消失了。 enter image description here

当我检查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>

1 个答案:

答案 0 :(得分:0)

我终于弄清楚出了什么问题。我本来应该对弧元素进行转换。所以基本上,在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);

}

但是,与d3.geo.path元素不同,我仍然知道svg.path元素为什么需要显式重新投影的知识差距。希望有人帮助我。