javascript - D3 树和传单 map 意外干扰

标签 javascript html d3.js leaflet

我在一个 div 中有一个基本的 Leaflet Map,在另一个 div 中有一个 d3-collapsible 树(基于此 Fiddle ): enter image description here

Map(右div)加载GeoJSON,D3树(左div)加载相应的分层JSon数据。现在,当我伸展树(Splay Tree)中的节点时, map 元素也会移动(参见红色箭头)。

我尝试重命名所有类名、变量和 ID,但问题仍然存在。 <强> Here是一个带有数据和代码的codepen。我认为它可能与使用分组SVG元素(全部在一个<g>?)作为节点/功能的传单和D3有关。我调整了我的树以使用 var svgTree =没有成功..

如果您无法为我的问题提供具体的解决方案,我也很乐意提供演示如何相邻使用 D3 和 Leaflet 的引用或示例。

[编辑]问题已解决:

我尝试从 Rob Schmuecker 的 Block 7880033 中合并的平移功能需要一个树 svg ( svgTree ) 和一个容器 svg ( svgGroup ),它包含所有 <g> -元素。最后centerNode函数需要调用svgTree.select("g").transition() (而不是 d3.select("g")svgGroup.select("g") )

这是更新后的代码。 Leaflet map 的代码在Body中:

<body>
    <div id="map"></div>
    <div id="d3tree"></div>
    <!--Leaflet Map JS-->
    <script>
        var CartoDB_Positron = L.tileLayer('http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', {
            attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> &copy; <a href="http://cartodb.com/attributions">CartoDB</a>',
            subdomains: 'abcd',
            maxZoom: 19
        });


        var map = new L.Map("map", {
                center: [37.8, -96.9],
                zoom: 4
            })
            .addLayer(CartoDB_Positron);

        var legend = L.control({position: 'topright'});
        legend.onAdd = function (map) {
        var div = L.DomUtil.create('div', 'info legend');
        div.innerHTML = '<select><option>root</option><option>1</option><option>2</option></select>';
        div.firstChild.onmousedown = div.firstChild.ondblclick = L.DomEvent.stopPropagation;
        return div;
        };
        legend.addTo(map);

        //set empty geojson feature layer with pre-defined layout & oneachfeature function handlers
        var geojsonLayer = L.geoJSON(false, {
            style: function(feature) {
                return {
                    stroke: true,
                    weight: 1,
                    color: '#4682B4',
                    weight: 2,
                    fillOpacity: 0.6
                };
            },
            onEachFeature: onEachFeature
        }).addTo(map);


       // add GeoJSON layer to the map once the file is loaded
      geojsonLayer.addData(data);
      map.fitBounds(geojsonLayer.getBounds());


        //OnClick function for features
        function onEachFeature(feature, layer) {
            popupOptions = {
                maxWidth: 200
            };
            layer.bindPopup("<b>Cluster HFID:</b> " + feature.properties.H_FID +
                "<br><b>Reference Tag: goldengatebridge</b>" +
                "<br><br>This photo cluster consists of " + feature.properties.PCount+ " photos from " + feature.properties.UCount + " Flickr users.", popupOptions);
            //Auto zoom to feature
            layer.on({
                click: zoomToFeature
            });
        }
        //Zoom to feature function
        function zoomToFeature(e) {
            map.fitBounds(e.target.getBounds());
        }

        //Change GeoJSON based on click/selection event
        function clean_map() {
            map.eachLayer(function (layer) {
                if (layer instanceof L.GeoJSON)
                {
                    map.removeLayer(layer);

                }
                //console.log(layer);
            });
        }
    </script>
</body>

D3-Tree 的代码已加载到 header 中:

    //Script initialized on load to fit to browser size (root center)
    //TODO: add root center update function upon window resize
    //D3 Tree JS
    //zoom/pan to node http://bl.ocks.org/robschmuecker/7880033

    window.onload = function () {
        var margin = {
            top: 20,
            right: 120,
            bottom: 20,
            left: 120
        },
            width = document.getElementById("d3tree").offsetWidth,
            height = document.getElementById("d3tree").offsetHeight;

        // size of the diagram
        var viewerWidth = width
        var viewerHeight = height


        var i = 0,
            duration = 750,
            nodeRectW = 60,
            nodeRectH = 30;

        //Set Fixed Node Size
        var tree = d3.layout.tree().nodeSize([nodeRectW + 10, nodeRectH + 10]);
        var diagonal = d3.svg.diagonal()
            .projection(function (d) {
                return [d.x + nodeRectW / 2, d.y + nodeRectH / 2];
            });

        // Define the redraw function for the whole (zoomable) tree
        function redraw() {
            svgGroup.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
        }

        // define the zoomListener which calls the zoom function on the "zoom" event constrained within the scaleExtents
        // this updates the center of the tree, needed for auto panning
        var zoomListener = d3.behavior.zoom().scaleExtent([0.1, 3]).on("zoom", redraw);

        // Append a group which holds all nodes and which the zoomListener can act upon
        var svgTree = d3.select("#d3tree").append("svg")
            .attr("width", viewerWidth)
            .attr("height", viewerHeight)
            .call(zoomListener);

        // Add tooltip div
        var tooltipDiv = d3.select("body").append("div")
            .attr("class", "tooltip")
            .style("opacity", 1e-6);

        //Set position of treeRoot
        //root = treeData; -->not necessary because of var treeRoot in subfile
        treeRoot.x0 = 0;
        treeRoot.y0 = height / 2;

        //Collapse function
        function collapse(d) {
            if (d.children) {
                d._children = d.children;
                d._children.forEach(collapse);
                d.children = null;
            }
        }

        // Append a group which holds all nodes and which the zoom Listener can act upon.
        var svgGroup = svgTree.append("g");

        // Collapse all nodes upon start
        treeRoot.children.forEach(collapse);
        update_tree(treeRoot);
        centerNode(treeRoot);

        //Update tree on event
        function update_tree(source) {               
            // Compute the new tree layout.
            var nodes = tree.nodes(treeRoot).reverse(),
                links = tree.links(nodes);

            // Normalize for fixed-depth (depth = vertival distance of nodes)
            nodes.forEach(function (d) {
                d.y = d.depth * 110;
            });

            // Update the nodes
            var node = svgGroup.selectAll("g.node")
                .data(nodes, function (d) {
                    return d.id || (d.id = ++i);
                });

            // Enter any new nodes at the parent's previous position.
            var nodeEnter = node.enter().append("g")
                .attr("class", "node")
                .attr("transform", function (d) {
                    return "translate(" + source.x0 + "," + source.y0 + ")";
                })
                .on("click", click_tree)
                .on("mouseover", function (d) {
                    var g = d3.select(this) // The node
                    // The class is used to remove the additional text later
                    var info = g.append('text')
                        .classed('info', true)
                        .attr('x', 20)
                        .attr('y', -20)
                        .text(function (d) {
                            return d.id; //Feature ID
                        });                        
                })
                .on("mouseout", function () {
                    // Remove the info text on mouse out.
                    tooltipDiv.transition()
                        .duration(300)
                        .style("opacity", 1e-6)
                    d3.select(this).select('text.info').remove();
                });

            nodeEnter.append("rect")
                .attr("width", nodeRectW)
                .attr("height", nodeRectH)
                .attr("stroke", "black")
                .attr("stroke-width", 0.25)
                .style("fill", function (d) {
                    return d._children ? "lightsteelblue" : "#fff";
                });

            nodeEnter.append("text")
                .attr("x", nodeRectW / 2)
                .attr("y", nodeRectH / 2)
                .attr("dy", ".35em")
                .attr("text-anchor", "middle")
                .text(function (d) {
                    return d.name;
                });

            // Transition nodes to their new position.
            var nodeUpdate = node.transition()
                .duration(duration)
                .attr("transform", function (d) {
                    return "translate(" + d.x + "," + d.y + ")";
                });

            nodeUpdate.select("rect")
                .attr("width", nodeRectW)
                .attr("height", nodeRectH)
                .attr("stroke", "black")
                .attr("stroke-width", 0.25)
                .style("fill", function (d) {
                    return d._children ? "lightsteelblue" : "#fff";
                });

            nodeUpdate.select("text")
                .style("fill-opacity", 1);

            // Transition exiting nodes to the parent's new position.
            var nodeExit = node.exit().transition()
                .duration(duration)
                .attr("transform", function (d) {
                    return "translate(" + source.x + "," + source.y + ")";
                })
                .remove();

            nodeExit.select("rect")
                .attr("width", nodeRectW)
                .attr("height", nodeRectH)
                .attr("stroke", "black")
                .attr("stroke-width", 1);

            nodeExit.select("text");

            // Update the links
            var link = svgGroup.selectAll("path.link")
                .data(links, function (d) {
                    return d.target.id;
                });

            // Enter any new links at the parent's previous position.
            link.enter().insert("path", "g")
                .attr("class", "link")
                .attr("x", nodeRectW / 2)
                .attr("y", nodeRectH / 2)
                .attr("d", function (d) {
                    var o = {
                        x: source.x0,
                        y: source.y0
                    };
                    return diagonal({
                        source: o,
                        target: o
                    });
                });

            // Transition links to their new position.
            link.transition()
                .duration(duration)
                .attr("d", diagonal);

            // Transition exiting nodes to the parent's new position.
            link.exit().transition()
                .duration(duration)
                .attr("d", function (d) {
                    var o = {
                        x: source.x,
                        y: source.y
                    };
                    return diagonal({
                        source: o,
                        target: o
                    });
                })
                .remove();

            // Stash the old positions for transition.
            nodes.forEach(function (d) {
                d.x0 = d.x;
                d.y0 = d.y;
            });
        }

        // Toggle children on click.
        function click_tree(d) {
            if (d.children) {
                d._children = d.children;
                d.children = null;
            } else {
                d.children = d._children;
                d._children = null;
            }

            update_tree(d);
            centerNode(d); //PanToNode
        }

        // Function to center node when clicked so node doesn't get lost when collapsing/moving with large amount of children.
        function centerNode(source) {
            //find the current zoom level by calling:
            scale = zoomListener.scale();
            x = -source.x0;
            y = -source.y0;
            x = x * scale + width / 2;
            y = y * scale + height / 2;
            svgTree.select("g").transition()
                .duration(duration)
                .attr("transform", "translate(" + x + "," + y + ")scale(" + scale + ")");
            zoomListener.scale(scale);
            zoomListener.translate([x, y]);
        }
    }

最佳答案

在您的 centerNode 函数中,而不是:

d3.select('g').transition()

应该是:

svgTree.select('g').transition()

这是您更新的 CodePen:http://codepen.io/anon/pen/NjRdGB?editors=0010

关于javascript - D3 树和传单 map 意外干扰,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43586326/

相关文章:

javascript - 箭头函数不触发

javascript - 如何使用 bolt 协议(protocol)(javascript)将对象数组(批量插入)插入到 neo4j 中

javascript - 在模块、JS、NODE 中创建对象及其实例

javascript - 画一条连接数据点的线

javascript - typescript : Argument Type Function is not assignable to parameter type function

javascript - 嵌套路由: How to query Rails DB and store values in JavaScript variables?

javascript - 点击 <a> 中的 <strong> 标签时,谷歌标签不会触发

java - 选择/选项值相当于java?

javascript - 用于 html、php、css 等网络代码的语法荧光笔

javascript - 堆积条形图未正确更新 d3js