How to keep d3 orthographic projection centered when zooming

ぃ、小莉子 提交于 2019-12-07 05:20:30

问题


I am trying to replicate the zoom functionality shown in Jason Davies "Rotate the World" visualization (https://www.jasondavies.com/maps/rotate/)

I am able to rotate and zoom, however, if I zoom after rotating, my projection is zoomed at an angle (meaning, if I turn the globe 15 degrees to the left and then zoom, the globe no longer stays centered in the canvas). Below is my code, any help would be greatly appreciated!

d3.select(window)
    .on("mousemove", mousemove)
    .on("mouseup", mouseup);

var width = 960,
height = 500;

var proj = d3.geo.orthographic()
    .scale(220)
    .translate([width / 2, height / 2])
    .clipAngle(90);


var path = d3.geo.path().projection(proj).pointRadius(1.5);

var graticule = d3.geo.graticule();

var svg = d3.select("#content").append("svg")
            .attr("width", width)
            .attr("height", height)
            .on("mousedown", mousedown);


var zoom = d3.behavior.zoom()
    .center([width / 2, height / 2])
    //.scaleExtent([.5, 10])
    .on("zoom", zoomed);

svg.call(zoom);


queue()
    .defer(d3.json, "/static/json/world.json")
    .await(ready);

function ready(error, world) {
    /*
        Define gradients
     */

    // ocean
    var ocean_fill = svg.append("defs").append("radialGradient")
        .attr("id", "ocean_fill")
        .attr("cx", "75%")
        .attr("cy", "25%");
    ocean_fill.append("stop").attr("offset", "5%").attr("stop-color", "#777");
    ocean_fill.append("stop").attr("offset", "100%").attr("stop-color", "#555");

    // globe highlight
    var globe_highlight = svg.append("defs").append("radialGradient")
        .attr("id", "globe_highlight")
        .attr("cx", "75%")
        .attr("cy", "25%");
    globe_highlight.append("stop")
        .attr("offset", "5%").attr("stop-color", "#bbb")
        .attr("stop-opacity","0.6");
    globe_highlight.append("stop")
        .attr("offset", "100%").attr("stop-color", "#999")
        .attr("stop-opacity","0.2");

    // globe shadow
    var globe_shading = svg.append("defs").append("radialGradient")
        .attr("id", "globe_shading")
        .attr("cx", "50%")
        .attr("cy", "40%");
    globe_shading.append("stop")
        .attr("offset","50%").attr("stop-color", "#333")
        .attr("stop-opacity","0");
    globe_shading.append("stop")
        .attr("offset","100%").attr("stop-color", "#111")
        .attr("stop-opacity","0.3");

    // drop shadow
    var drop_shadow = svg.append("defs").append("radialGradient")
        .attr("id", "drop_shadow")
        .attr("cx", "50%")
        .attr("cy", "50%");
    drop_shadow.append("stop")
        .attr("offset","20%").attr("stop-color", "#000")
        .attr("stop-opacity",".5");
    drop_shadow.append("stop")
        .attr("offset","100%").attr("stop-color", "#000")
        .attr("stop-opacity","0");

    /*
        Draw globe objects
     */

    // drop shadow
    svg.append("ellipse")
        .attr("cx", 440).attr("cy", 450)
        .attr("rx", proj.scale()*.90)
        .attr("ry", proj.scale()*.25)
        .attr("class", "noclicks")
        .style("fill", "url(#drop_shadow)");

    // globe
    svg.append("circle")
        .attr("cx", width / 2).attr("cy", height / 2)
        .attr("r", proj.scale())
        .attr("class", "noclicks")
        .style("fill", "url(#ocean_fill)");

    // land
    svg.append("path")
        .datum(topojson.feature(world, world.objects.land))
        .attr("class", "land")
        .attr("d", path);

    svg.append("path")
        .datum(graticule)
        .attr("class", "graticule noclicks")
        .attr("d", path);

    svg.append("circle")
        .attr("cx", width / 2).attr("cy", height / 2)
        .attr("r", proj.scale())
        .attr("class","noclicks")
        .style("fill", "url(#globe_highlight)");

    svg.append("circle")
        .attr("cx", width / 2).attr("cy", height / 2)
        .attr("r", proj.scale())
        .attr("class","noclicks")
        .style("fill", "url(#globe_shading)");

/*    svg.append("g").attr("class","points")
        .selectAll("text").data(places.features)
        .enter().append("path")
        .attr("class", "point")
        .attr("d", path);

    svg.append("g").attr("class","labels")
        .selectAll("text").data(places.features)
        .enter().append("text")
        .attr("class", "label")
        .text(function(d) { return d.properties.name })*/

    svg.append("g").attr("class","countries")
      .selectAll("path")
        .data(topojson.feature(world2, world2.objects.countries).features)
      .enter().append("path")
        .attr("d", path);
}

// modified from http://bl.ocks.org/1392560
var m0, o0;

function mousedown() {
    m0 = [d3.event.pageX, d3.event.pageY];
    o0 = proj.rotate();
    d3.event.preventDefault();
}
function mousemove() {
    if (m0) {
        var m1 = [d3.event.pageX, d3.event.pageY]
            , o1 = [o0[0] + (m1[0] - m0[0]) / 6, o0[1] + (m0[1] - m1[1]) / 6];
        o1[1] = o1[1] > 30  ? 30  :
            o1[1] < -30 ? -30 :
            o1[1];
        proj.rotate(o1);
        refresh();
    }
}
function mouseup() {
    if (m0) {
        mousemove();
        m0 = null;
    }
}

function refresh() {
    svg.selectAll(".land").attr("d", path);
    svg.selectAll(".countries path").attr("d", path);
    svg.selectAll(".graticule").attr("d", path);
    svg.selectAll(".point").attr("d", path);
    //position_labels();
}

var slast = 1;

function zoomed() {
    if (slast != d3.event.scale) {
        svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
        slast = d3.event.scale;
    };
}

来源:https://stackoverflow.com/questions/24497194/how-to-keep-d3-orthographic-projection-centered-when-zooming

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!