How to separate d3.js drag from the leaflet map drag?

715 views
Skip to first unread message

Nav N

unread,
Apr 24, 2014, 2:44:32 AM4/24/14
to leafl...@googlegroups.com
I've managed to get a d3.js force layout working on the leaflet map, but when I try to drag any of the nodes of the force layout with the mouse, the map also gets dragged along with it.
Although my eventual aim is to get the force layout nodes to stay at their respective lat-long positions and scale according to how the map is panned and zoomed, for now I'd appreciate some help on being able to individually drag the nodes and map.

The entire code I've tried (simply putting it into an html file will be enough to make it work):

<!DOCTYPE html>
<html>
<style>
 
.link {
fill: none;
stroke: #666;
stroke-width: 1.5px;
}
 
#licensing {
fill: green;
}
 
.link.licensing {
stroke: green;
}
 
.link.resolved {
stroke-dasharray: 0,2 1;
}
 
circle {
fill: #ccc;
stroke: #333;
stroke-width: 1.5px;
}
 
text {
font: 10px sans-serif;
pointer-events: none;
text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
}
 
</style>
<head>
<!-- http://bl.ocks.org/d3noob/9267535 -->

    <title>d3.js with leaflet.js</title>

    <link
        rel="stylesheet"
        href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css"
    />
    <script src="http://d3js.org/d3.v3.min.js"></script>
    <script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet.js"></script>
   
</head>
<body>

    <div id="map" style="width: 600px; height: 400px"></div>

    <script type="text/javascript">
   
        var map = L.map('map').setView([-41.2858, 174.7868], 13);
        mapLink =
            '<a href="http://openstreetmap.org">OpenStreetMap</a>';
        L.tileLayer(
            'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            attribution: '&copy; ' + mapLink + ' Contributors',
            maxZoom: 18,
            }).addTo(map);
               
    /* Initialize the SVG layer */
    map._initPathRoot()   

    /* We simply pick up the SVG from the map object */
    var svg = d3.select("#map").select("svg"),
    g = svg.append("g");
   
    d3.json("circles_circlesOnMap.json", function(collection)
    {
        /* Add a LatLng object to each item in the dataset */
        collection.objects.forEach(function(d)
        {
            d.LatLng = new L.LatLng(d.circle.coordinates[0], d.circle.coordinates[1])   
        });
       
        var feature = g.selectAll("circle")
            .data(collection.objects)
            .enter().append("circle")
            .style("stroke", "black") 
            .style("opacity", .6)
            .style("fill", "red")
            .attr("r", 20); 
       
        map.on("viewreset", update);
        update();

        function update()
        {
            feature.attr("transform",
            function(d)
                {
                    return "translate("+
                    map.latLngToLayerPoint(d.LatLng).x +","+
                    map.latLngToLayerPoint(d.LatLng).y +")";
                }
            )
        }
    });

        //----------------------------------------------------------------------------------------------------------------------------
        //------------------------------------- topology -----------------------------------------------------------------------------
        //----------------------------------------------------------------------------------------------------------------------------
        var links = [
        {source: "Microsoft", target: "Amazon", type: "licensing"},
        {source: "Microsoft", target: "HTC", type: "licensing"},
        {source: "Samsung", target: "Apple", type: "suit"},
        {source: "Motorola", target: "Apple", type: "suit"},
        {source: "Nokia", target: "Apple", type: "resolved"},
        {source: "HTC", target: "Apple", type: "suit"},
        {source: "Kodak", target: "Apple", type: "suit"},
        {source: "Microsoft", target: "Barnes & Noble", type: "suit"},
        {source: "Microsoft", target: "Foxconn", type: "suit"},
        {source: "Oracle", target: "Google", type: "suit"},
        {source: "Apple", target: "HTC", type: "suit"},
        {source: "Microsoft", target: "Inventec", type: "suit"},
        {source: "Samsung", target: "Kodak", type: "resolved"},
        {source: "LG", target: "Kodak", type: "resolved"},
        {source: "RIM", target: "Kodak", type: "suit"},
        {source: "Sony", target: "LG", type: "suit"},
        {source: "Kodak", target: "LG", type: "resolved"},
        {source: "Apple", target: "Nokia", type: "resolved"},
        {source: "Qualcomm", target: "Nokia", type: "resolved"},
        {source: "Apple", target: "Motorola", type: "suit"},
        {source: "Microsoft", target: "Motorola", type: "suit"},
        {source: "Motorola", target: "Microsoft", type: "suit"},
        {source: "Huawei", target: "ZTE", type: "suit"},
        {source: "Ericsson", target: "ZTE", type: "suit"},
        {source: "Kodak", target: "Samsung", type: "resolved"},
        {source: "Apple", target: "Samsung", type: "suit"},
        {source: "Kodak", target: "RIM", type: "suit"},
        {source: "Nokia", target: "Qualcomm", type: "suit"}
        ];
 
        var nodes = {};
 
        // Compute the distinct nodes from the links.
        links.forEach(function(link) {
        link.source = nodes[link.source] || (nodes[link.source] = {name: link.source});
        link.target = nodes[link.target] || (nodes[link.target] = {name: link.target});
        });
 
        var width = 960,
        height = 500;
 
        var force = d3.layout.force()
        .nodes(d3.values(nodes))
        .links(links)
        .size([width, height])
        .linkDistance(60)
        .charge(-300)
        .on("tick", tick)
        .start();
 
        //var svg = d3.select("body").append("svg").attr("width", width).attr("height", height);
 
        // Per-type markers, as they don't inherit styles.
        svg.append("defs").selectAll("marker")
        .data(["suit", "licensing", "resolved"])
        .enter().append("marker")
        .attr("id", function(d) { return d; })
        .attr("viewBox", "0 -5 10 10")
        .attr("refX", 15)
        .attr("refY", -1.5)
        .attr("markerWidth", 6)
        .attr("markerHeight", 6)
        .attr("orient", "auto")
        .append("path")
        .attr("d", "M0,-5L10,0L0,5");
 
        var path = svg.append("g").selectAll("path")
        .data(force.links())
        .enter().append("path")
        .attr("class", function(d) { return "link " + d.type; })
        .attr("marker-end", function(d) { return "url(#" + d.type + ")"; });
 
        var circle = svg.append("g").selectAll("circle")
        .data(force.nodes())
        .enter().append("circle")
        .attr("r", 6)
        .call(force.drag);
 
        var text = svg.append("g").selectAll("text")
        .data(force.nodes())
        .enter().append("text")
        .attr("x", 8)
        .attr("y", ".31em")
        .text(function(d) { return d.name; });
 
        // Use elliptical arc path segments to doubly-encode directionality.
        function tick() {
        path.attr("d", linkArc);
        circle.attr("transform", transform);
        text.attr("transform", transform);
        }
   
        function linkArc(d) {
        var dx = d.target.x - d.source.x,
        dy = d.target.y - d.source.y,
        dr = Math.sqrt(dx * dx + dy * dy);
        return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
        }
 
        function transform(d) {
        return "translate(" + d.x + "," + d.y + ")";
        }   
</script>
</body>
</html>

Muhammad Sadiq

unread,
Jun 12, 2014, 6:54:41 AM6/12/14
to leafl...@googlegroups.com
to separate the d3.js drag from the leaflet map drag you just need to add mouse events to your circle so when mouse enters the circle area we need to disable the map dragging and when mouse exits the circle area we need to enable the map dragging like this:
 
 

var circle = svg.append("g").selectAll("circle")
        .data(force.nodes())
        .enter().append("circle")
        .attr("r", 6)
.on("mouseenter", function(){ map.dragging.disable(); })
.on("mouseout", function(){map.dragging.enable();})
.style("cursor", "pointer")
        .call(force.drag); 

  so simple isn't it ? 

 

Jov Jab

unread,
Jul 31, 2017, 3:18:29 AM7/31/17
to Leaflet
nice work. can you please share the json file.
Thanks.
Reply all
Reply to author
Forward
0 new messages