Javascript 如何将svg组或外来对象正确定位到d3地球仪上?

Javascript 如何将svg组或外来对象正确定位到d3地球仪上?,javascript,html,css,svg,d3.js,Javascript,Html,Css,Svg,D3.js,我是d3的新手,现在正在为一个问题奋斗两周 我的目标是将多个svg:foreignobjects(包含xhtml:divs)渲染到一个d3球体上。xhtml:div的内容/数据来自csv。到目前为止,初始设置仍然有效,因此当页面最初加载时,div(当前显示城市名称,从csv获取的城市位置)位于其相应的正确位置 d3.select("#f" + i).attr("x", projection([ d.coordinates[0], d.coordinates

我是d3的新手,现在正在为一个问题奋斗两周

我的目标是将多个svg:foreignobjects(包含xhtml:divs)渲染到一个d3球体上。xhtml:div的内容/数据来自csv。到目前为止,初始设置仍然有效,因此当页面最初加载时,div(当前显示城市名称,从csv获取的城市位置)位于其相应的正确位置

        d3.select("#f" + i).attr("x",
            projection([ d.coordinates[0], d.coordinates[1] ])[0]); 
        d3.select("#f" + i).attr("y",
            projection([ d.coordinates[0], d.coordinates[1] ])[1]);

但是当用鼠标旋转地球仪时,xhtml:foreignobjects(或xhtml:divs)的位置会被弄乱:

这里有人能帮忙吗?或者能给我们一个正确方向的提示吗

代码:


变量宽度=960,高度=500,旋转=[0,0],分划=d3.geo.graticule();
var projection=d3.geo.orthographic()标度(宽度/(2*Math.PI)).clipAngle(90);
var-mercator=d3.geo.mercator().scale(宽度/(2*Math.PI));
var path=d3.geo.path().projection(projection);
var m0,o0;
var cx,cy={};
var-svg;
var cities_csv;
var drag=d3.behavior.drag().on(“dragstart”,function()){
d3.event.sourceEvent.stopPropagation();
//改编自http://mbostock.github.io/d3/talk/20111018/azimuthal.html 并针对D3V3进行了更新
var proj=projection.rotate();
m0=[d3.event.sourceEvent.pageX,d3.event.sourceEvent.pageY];
o0=[-proj[0],-proj[1]];
}).在(
“拖动”,
函数(){
if(m0){
变量m1=[d3.event.sourceEvent.pageX,d3.event.sourceEvent.pageY],o1=[
o0[0]+(m0[0]-m1[0])/4,o0[1]+(m1[1]-m0[1])/4];
投影.旋转([-o1[0],-o1[1]]);
}
//更新地图
path=d3.geo.path().projection(projection);
d3.选择全部(“路径”).attr(“d”,路径);
var group=svg.selectAll(“g”);
//控制台日志(d3);
d3.选择全部(“.点”)。每个(功能(d,i){
控制台日志(d);
d3.选择(“#f”+i).attr(“x”,
投影([d坐标[0],d坐标[1]])[1]+200);
d3.选择(“#f”+i).attr(“y”,
投影([d坐标[0],d坐标[1]])[0]-200);
控制台日志(“NR:+i”);
});
});
d3.选择(“svg”)。打开(“鼠标向下”,函数(){
log(“鼠标:+projection.invert(d3.mouse(this)));
});
svg=d3。选择(“#映射”)。追加(“svg”).attr(“宽度”,宽度)。attr(“高度”,高度)。调用(拖动)。调用(
d3.behavior.zoom().on(“zoom”,redraw));
函数重画(){
//然而,我却被注释掉了,因为我只是想缩放,而不是翻译
//attr(“transform”、“translate”(+d3.event.translate+))比例(+d3.event.scale+);
};
svg.append(“defs”).append(“path”).datum({
类型:“球体”
}).attr(“id”,“sphere”).attr(“d”,路径);
svg.append(“use”).attr(“class”,“stroke”).attr(“xlink:href”,“#sphere”);
svg.append(“use”).attr(“class”,“fill”).attr(“xlink:href”,“#sphere”);
svg.append(“path”).datum(gracile).attr(“class”,“gracile”).attr(“d”,path);
style(“形状渲染”,null);
d3.json(“world-110m2.json”),函数(错误,数据){
插入(“路径”,“分划”).datum(topojson.feature(data,data.objects.countries))
.attr(“类别”、“土地”).attr(“d”,路径);
});
d3.csv(“cities.csv”,函数(错误,数据){
var cities_csv=数据;
data.forEach(函数(d,i){
var group=d3.选择(“svg”).追加(“svg:g”).attr(“类”、“组”).attr(“id”、“g”+i);
var point=group.append(“路径”,“前景”).datum({
键入:“点”,
坐标:[d['lon'],d['lat']]
}).attr(“类”、“点”).attr(“id”、“p”+i).attr(“数据id”,i).attr(“d”,路径)。在(“单击”,
函数(){
窗口打开(“http://google.com");
}).attr(“r”,4)。样式(“填充”、“红色”);
point.select(“div”).html(“+//结束标记
“
”+d.close); //检查位置是否已剪裁 var-clipped=false; clip_test_path=d3.geo.path().projection(projection); if(类型)(剪辑测试路径({ 类型:“多点”, 坐标:[[d.lon,d.lat]] }))=“未定义”){ 剪裁=真 } if(clipped==false){ group.append(“foreignObject”).attr(“d”,path).attr(“id”,“f”+i).attr(“数据id”,i).attr(“类”, 'city').attr('width','100px').attr('height','100px').attr(“x”, 投影([d.lon,d.lat])[0])。属性(“y”,投影([d.lon,d.lat])[1])。追加( 'xhtml:div').style(“宽度”、“20px”).style(“高度”、“20px”).style(“填充”、“2px”).html( “+d.city+”); } }); });
我想我已经按照你期望的方式工作了

更新后的代码是

var width = 960, height = 500, rotate = [ 0, 0 ], graticule = d3.geo.graticule();

var projection = d3.geo.orthographic().scale(width / (2 * Math.PI)).clipAngle(90);

var mercator = d3.geo.mercator().scale(width / (2 * Math.PI));

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

var m0, o0;

var cx, cy = {};
var svg;

var cities_csv;

var drag = d3.behavior.drag().on("dragstart", function() {
    d3.event.sourceEvent.stopPropagation();

    // Adapted from http://mbostock.github.io/d3/talk/20111018/azimuthal.html and updated for d3 v3
    var proj = projection.rotate();
    m0 = [ d3.event.sourceEvent.pageX, d3.event.sourceEvent.pageY ];
    o0 = [ -proj[0], -proj[1] ];
}).on(
    "drag",
    function() {

        if (m0) {
        var m1 = [ d3.event.sourceEvent.pageX, d3.event.sourceEvent.pageY ], o1 = [
            o0[0] + (m0[0] - m1[0]) / 4, o0[1] + (m1[1] - m0[1]) / 4 ];
        projection.rotate([ -o1[0], -o1[1] ]);
        }

        // Update the map
        path = d3.geo.path().projection(projection);
        d3.selectAll("path").attr("d", path);
        d3.selectAll("g").attr("d", path);


        var group = svg.selectAll("g");

        //console.log(d3);

        d3.selectAll(".point").each(function(d, i) {
            console.log(d);

            var clipped = false;

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

            if (typeof (clip_test_path({
                type : "MultiPoint",
                coordinates : [ [ d.coordinates[0], d.coordinates[1] ] ]
            })) === "undefined") {
                clipped = true
            }

            console.log(d, clipped)

            if (clipped) {
                d3.select("#f" + i).style("display", "none");
            } else {
                d3.select("#f" + i).style("display", null);
            }

            d3.select("#f" + i).attr("x",
                projection([ d.coordinates[0], d.coordinates[1] ])[0]); 
            d3.select("#f" + i).attr("y",
                projection([ d.coordinates[0], d.coordinates[1] ])[1]);

            console.log("NR:" + i);
        });

    });

d3.select("svg").on("mousedown", function() {
    console.log("mouse: " + projection.invert(d3.mouse(this)));
});

svg = d3.select("#map").append("svg").attr("width", width).attr("height", height).call(drag).call(
    d3.behavior.zoom().on("zoom", redraw));

function redraw() {
    //Yet commented out because I just want to scale, not translate
    //svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
};

svg.append("defs").append("path").datum({
    type : "Sphere"
}).attr("id", "sphere").attr("d", path);

svg.append("use").attr("class", "stroke").attr("xlink:href", "#sphere");

svg.append("use").attr("class", "fill").attr("xlink:href", "#sphere");

svg.append("path").datum(graticule).attr("class", "graticule").attr("d", path);
svg.style("shape-rendering", null);

d3.json("world-110m2.json", function(error, data) {
    svg.insert("path", ".graticule").datum(topojson.feature(data, data.objects.countries))
        .attr("class", "land").attr("d", path);
});

d3.csv("cities.csv", function(error, data) {
    var cities_csv = data;
    data.forEach(function(d, i) {

    var group = d3.select("svg").append("svg:g").attr("class", "group").attr("id", "g" + i);

    var point = group.append("path", ".foreground").datum({
        type : "Point",
        coordinates : [ d['lon'], d['lat'] ]
    }).attr("class", "point").attr("id", "p" + i).attr("data-id", i).attr("d", path).on("click",
        function() {
            window.open("http://google.com");
        }).attr("r", 4).style("fill", "red");

    point.select("div").html('<a href= "http://google.com">' + // The first <a> tag
    (d.date) + "</a>" + // closing </a> tag
    "<br/>" + d.close);

    group.append("foreignObject").attr("d", path).attr("id", "f" + i).attr("data-id", i).attr('class',
        'city').attr('width', '100px').attr('height', '100px').attr("x",
        projection([ d.lon, d.lat ])[0]).attr("y", projection([ d.lon, d.lat ])[1]).append(
        'xhtml:div').style("width", "20px").style("height", "20px").style("padding", "2px").html(
        "<small>" + d.city + "</small>");

    //check if location is clipped
    var clipped = false;
    clip_test_path = d3.geo.path().projection(projection);
    if (typeof (clip_test_path({
        type : "MultiPoint",
        coordinates : [ [ d.lon, d.lat ] ]
    })) === "undefined") {
        clipped = true
    }

    if (clipped) {
        d3.select("#f" + i).style("display", "none");
    }
    });

});
  • 拖动
    处理程序中,检查剪裁,如果该点被剪裁,则隐藏
    外来对象
    。我只是重复使用了您稍后用于检测剪裁的代码

            var clipped = false;
    
            clip_test_path = d3.geo.path().projection(projection);
    
            if (typeof (clip_test_path({
                type : "MultiPoint",
                coordinates : [ [ d.coordinates[0], d.coordinates[1] ] ]
            })) === "undefined") {
                clipped = true
            }
    
            console.log(d, clipped)
    
            if (clipped) {
                d3.select("#f" + i).style("display", "none");
            } else {
                d3.select("#f" + i).style("display", null);
            }
    
  • 在处理初始添加
    外来对象的代码中
    我将此更改为始终添加
    外来对象
    ,无论是否剪裁,如果剪裁,请将
    显示
    设置为
    ,以隐藏对象

    group.append("foreignObject").attr("d", path).attr("id", "f" + i).attr("data-id", i).attr('class',
        'city').attr('width', '100px').attr('height', '100px').attr("x",
        projection([ d.lon, d.lat ])[0]).attr("y", projection([ d.lon, d.lat ])[1]).append(
        'xhtml:div').style("width", "20px").style("height", "20px").style("padding", "2px").html(
        "<small>" + d.city + "</small>");
    
    //check if location is clipped
    var clipped = false;
    clip_test_path = d3.geo.path().projection(projection);
    if (typeof (clip_test_path({
        type : "MultiPoint",
        coordinates : [ [ d.lon, d.lat ] ]
    })) === "undefined") {
        clipped = true
    }
    
    if (clipped) {
        d3.select("#f" + i).style("display", "none");
    }
    
    group.append(“foreignObject”).attr(“d”,path).attr(“id”,“f”+i).attr(“数据id”,i).attr(“类”,
    'city').attr('width','100px').attr('height','100px').attr(“x”,
    投影([d.lon,d.lat])[0])。属性(“y”,投影([d.lon,d.lat])[1])。追加(
    'xhtml:div').style(“宽度”、“20px”).style(“高度”、“20px”).style(“填充”、“2px”).html(
    “+d.city+”);
    //检查位置是否已剪裁
    var-clipped=false;
    clip_test_path=d3.geo.path().projection(projection);
    if(类型)(剪辑测试路径({
    类型:“多点”,
    坐标:[[d.lon,d.lat]]
    }))=“未定义”){
    剪裁=真
    }
    如果(剪裁){
    d3.选择(“#f”+i).样式(“显示”、“无”);
    }
    
  • group.append("foreignObject").attr("d", path).attr("id", "f" + i).attr("data-id", i).attr('class',
        'city').attr('width', '100px').attr('height', '100px').attr("x",
        projection([ d.lon, d.lat ])[0]).attr("y", projection([ d.lon, d.lat ])[1]).append(
        'xhtml:div').style("width", "20px").style("height", "20px").style("padding", "2px").html(
        "<small>" + d.city + "</small>");
    
    //check if location is clipped
    var clipped = false;
    clip_test_path = d3.geo.path().projection(projection);
    if (typeof (clip_test_path({
        type : "MultiPoint",
        coordinates : [ [ d.lon, d.lat ] ]
    })) === "undefined") {
        clipped = true
    }
    
    if (clipped) {
        d3.select("#f" + i).style("display", "none");
    }