Javascript d3树状图:替换根保留老树

Javascript d3树状图:替换根保留老树,javascript,d3.js,hierarchy,Javascript,D3.js,Hierarchy,我有一个树状图/聚类图的根使用d3.hierarchy。我正在尝试用一个选定的节点更新根,该节点将成为新的头,并用该节点在顶部绘制一棵新树。这将取代老树。步骤如下: 读入平面数据 使用d3.stratify转换为层次结构 将其转换为簇(带有坐标等) 使用新的select.join绘制(不再需要显式退出/删除) 用户单击节点的圆 将选定节点作为新根更新层次结构,并删除父节点 重新绘制,节点不再出现在通过联接删除的数据(父节点及以上)中 然而,它重新绘制了新的、较小的根和依赖项,但所有旧的SVG仍然

我有一个树状图/聚类图的根使用d3.hierarchy。我正在尝试用一个选定的节点更新根,该节点将成为新的头,并用该节点在顶部绘制一棵新树。这将取代老树。步骤如下:

  • 读入平面数据
  • 使用d3.stratify转换为层次结构
  • 将其转换为簇(带有坐标等)
  • 使用新的select.join绘制(不再需要显式退出/删除)
  • 用户单击节点的圆
  • 将选定节点作为新根更新层次结构,并删除父节点
  • 重新绘制,节点不再出现在通过联接删除的数据(父节点及以上)中
  • 然而,它重新绘制了新的、较小的根和依赖项,但所有旧的SVG仍然存在。我已尝试显式添加退出/删除,但没有帮助

    我做错了什么

    这里可以看到一个简化的、可复制的示例。我还创作了一把小提琴

    
    设treeData=[]
    让currentTreeData=[]
    var flatData=[
    {“ID”:1000,“name”:“根”,“parentID”:null},
    {“ID”:1100,“name”:“Child 1”,“parentID”:1000},
    {“ID”:1110,“name”:“G.Child 1.1”,“parentID”:1100},
    {“ID”:1120,“name”:“G.Child 1.2”,“parentID”:1100},
    {“ID”:1130,“name”:“G.Child 1.3”,“parentID”:1100},
    {“ID”:1200,“name”:“Child 2”,“parentID”:1000},
    {“ID”:1210,“name”:“G.Child 2.1”,“parentID”:1200},
    {“ID”:1211,“name”:“G.G.Child 2.1.1”,“parentID”:1210},
    {“ID”:1212,“name”:“G.G.Child 2.2.2”,“parentID”:1210},
    {“ID”:12111,“name”:“G.G.G.Child 2.1.1.1”,“parentID”:1211},
    {“ID”:1300,“name”:“Child 3”,“parentID”:1000}
    ];
    功能图(thisTreeData){
    让根=丛生(thisTreeData)
    //添加节点(链接)
    svg.append(“g”)
    .attr(“类”、“节点”)
    .attr(“填充”、“无”)
    .attr(“笔划”,“#555”)
    .attr(“笔划不透明度”,0.3)
    .selectAll(“路径”)
    .data(root.links(),函数(d){return“Link”+:“+d.target.data.id})
    .join(“路径”)
    .attr(“d”,d3.linkRadial()
    .角度(d=>d.x)
    .半径(d=>d.y));
    //添加圆圈
    svg.append(“g”)
    .attr(“类”、“节点”)
    .selectAll(“圆圈”)
    .data(root.subjects(),函数(d){return“Circle”+d.data.id;})
    .加入(“圈”)
    .attr(“转换”,d=>`
    旋转(${d.x*180/Math.PI-90})
    翻译(${d.y},0)
    `)
    .attr(“r”,3)
    .on('点击',点击);
    //添加文本
    svg.append(“g”)
    .attr(“类”、“节点”)
    .selectAll(“文本”)
    .data(root.subjects(),函数(d){return“Text”+d.data.id;})
    .join(“文本”)
    .attr(“转换”,d=>`
    旋转(${d.x*180/Math.PI-90})
    翻译(${d.y},0)
    旋转(${d.x>=Math.PI?180:0})
    `)
    .attr(“文本锚定”,d=>d.xd.data.data.name);
    }
    //单击“切换树”以选择“中心”节点
    函数单击(事件,d){
    currentTreeData=findNode(treeData,d.data.id)
    图表(currentTreeData);
    }
    //辅助函数
    // ----------------
    //函数用于将CSV数据强制展开到树中
    函数转换层次结构(数据){
    var stratify=d3.stratify()
    .parentId(函数(d){
    返回d.parentID;
    })
    .id(功能(d){
    返回d.ID;
    });
    设treeData=分层(数据);
    返回(treeData)
    }
    //用于根据分层数据创建具有坐标等的d3群集的函数
    功能集群(treeData){
    tree=d3.cluster().size([2*Math.PI,半径-100])
    设root=tree(d3.hierarchy(treeData)
    .sort((a,b)=>d3.升序(a.name,b.name));
    返回(根)
    }
    函数findNode(根,id){
    console.log(root);
    让selected=root.find(obj=>obj.id==id);
    选中。父项=null;
    console.log(选中);
    返回(选定)
    }
    宽度=800
    高度=600
    半径=宽度/2
    让svg=d3.选择(“#vis”)
    .append('svg')
    .attr('width',width)
    .attr('height',height)
    .append('g')
    .attr('transform','translate('+width/2+','+height/2+'));
    treeData=convertToHierarchy(flatData)
    currentTreeData=treeData
    图表(currentTreeData);
    
    这里有一个更复杂的示例,它使用新的
    .join
    方法正确处理
    enter
    update
    exit
    模式。这允许您添加转换。注意,我删除了您的内部包装
    g
    节点。由于每次单击都会附加一个新节点,因此会将可见节点(路径、圆和文本)的选择弄乱

    
    设treeData=[];
    设currentTreeData=[];
    var flatData=[
    {ID:1000,名称:'The Root',parentID:null},
    {ID:1100,名称:'Child 1',parentID:1000},
    {ID:1110,名称:'G.Child 1.1',parentID:1100},
    {ID:1120,名称:'G.Child 1.2',parentID:1100},
    {ID:1130,名称:'G.Child 1.3',parentID:1100},
    {ID:1200,名称:'Child 2',parentID:1000},
    {ID:1210,名称:'G.Child 2.1',parentID:1200},
    {ID:1211,名称:'G.G.Child 2.1.1',parentID:1210},
    {ID:1212,名称:'G.G.Child 2.2.2',父ID:121
    
    <html>
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script src="https://d3js.org/d3.v6.js"></script>
    </head>
    
    <body>
        <div id="vis"></div>
        <script>
          let treeData = []
          let currentTreeData = []
    
          var flatData = [
            { "ID" : 1000, "name" : "The Root", "parentID":null},
            { "ID" : 1100, "name" : "Child 1", "parentID":1000 },
            { "ID" : 1110, "name" : "G.Child 1.1", "parentID":1100 },
            { "ID" : 1120, "name" : "G.Child 1.2", "parentID":1100 },
            { "ID" : 1130, "name" : "G.Child 1.3", "parentID":1100 },
            { "ID" : 1200, "name" : "Child 2", "parentID":1000 },
            { "ID" : 1210, "name" : "G.Child 2.1", "parentID":1200 },
            { "ID" : 1211, "name" : "G.G.Child 2.1.1", "parentID":1210 },
            { "ID" : 1212, "name" : "G.G.Child 2.2.2", "parentID":1210 },
            { "ID" : 12111, "name" : "G.G.G.Child 2.1.1.1", "parentID":1211 },
            { "ID" : 1300, "name" : "Child 3", "parentID":1000 }
          ];
    
            function chart(thisTreeData) {
              let root = clusterise(thisTreeData)
              // Add nodes (links)
              svg.append("g")
                .attr("class", "node")
                .attr("fill", "none")
                .attr("stroke", "#555")
                .attr("stroke-opacity", 0.3)
                .selectAll("path")
                .data(root.links(), function(d) { return "Link" + ":" + d.target.data.id })
                .join("path")
                .attr("d", d3.linkRadial()
                      .angle(d => d.x)
                      .radius(d => d.y));
    
              // Add circles
              svg.append("g")
                .attr("class", "node")
                .selectAll("circle")
                .data(root.descendants(), function(d) { return "Circle" + d.data.id; })
                .join("circle")
                .attr("transform", d => `
      rotate(${d.x * 180 / Math.PI - 90})
      translate(${d.y},0)
      `)
                .attr("r", 3)
                .on('click', click);
    
              // Add text
              svg.append("g")
                .attr("class", "node")
                .selectAll("text")
                .data(root.descendants(), function(d) { return "Text" + d.data.id; })
                .join("text")
                .attr("transform", d => `
      rotate(${d.x * 180 / Math.PI - 90}) 
      translate(${d.y},0) 
      rotate(${d.x >= Math.PI ? 180 : 0})
      `)
                .attr("text-anchor", d => d.x < Math.PI === !d.children ? "start" : "end")
                .text(d => d.data.data.name);
            }
    
            // Switch tree on click so centre is now selected node
            function click(event,d) {
              currentTreeData = findNode(treeData, d.data.id)
              chart(currentTreeData);
            }
    
            // HELPER FUNCTIONS
            // ----------------
            // Function to Strafify flat CSV data into a tree
            function convertToHierarchy(data) {
              var stratify = d3.stratify()
              .parentId(function (d) {
                return d.parentID;
              })
              .id(function (d) {
                return d.ID;
              });
              let treeData = stratify(data);
              return (treeData)
            }
    
            // Function to Create d3 cluster with coordinates etc from stratified data
            function clusterise(treeData) {
              tree = d3.cluster().size([2 * Math.PI, radius - 100])
              let root = tree(d3.hierarchy(treeData)
                              .sort((a, b) => d3.ascending(a.name, b.name)));
              return (root)
            }
    
            function findNode(root, id) {
              console.log(root);
              let selected = root.find(obj => obj.id === id);
              selected.parent= null;
              console.log(selected);
              return(selected)
            }
    
            width = 800
            height = 600 
            radius = width / 2
    
            let svg = d3.select("#vis")
            .append('svg')
            .attr('width', width)
            .attr('height', height)
            .append('g')
            .attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
    
            treeData = convertToHierarchy(flatData)
            currentTreeData = treeData
            chart(currentTreeData);
        </script>
    </body>
    </html>