【问题标题】:d3 graphs on google maps with link between nodes谷歌地图上的 d3 图表与节点之间的链接
【发布时间】:2015-10-29 05:37:52
【问题描述】:

我的要求是在谷歌地图上显示 d3 图表,其中包含多个节点和它们之间的链接。我尝试了以下sn-p。但它根本不起作用。

<!DOCTYPE html>
<html> 
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js">        </script>
<style type="text/css">
html,
body,
#map {
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
}

.stations,
.stations svg {
    position: absolute;
    margin: 0 auto;
}

.links {
    position: absolute;
}

.stations svg {
    width: 20px;
    height: 20px;
    margin: 0 auto;
    z-index: 4;
}

.stations circle {
    fill: blue;
    stroke: none;
    stroke-width: 1px;
}
</style>
</head>
<body>
<div id="map"></div>
<script type="text/javascript">
         var map = new google.maps.Map(d3.select("#map").node(), {
        zoom: 7,
        center: new google.maps.LatLng(-22.1629209, -47.9922608),
        mapTypeId: google.maps.MapTypeId.MAP
      });

var dataset = JSON.parse('{"directed": true, "graph": [], "nodes": [{"lat": 44.391643516091975, "lng": 23.159677682342053}, {"lat": 44.315988, "lng": 23.818359, "id": "a:a::"},  {"lat": 44.29844994776969, "lng": 24.402314492323608, "id": "b:b"},  {"lat": 44.351118152120485, "lng": 23.341791630955303, "id": "a:c"},  {"lat": 44.889424527442685, "lng": 23.960970697645276, "id": "e:d"},  {"lat": 43.46084400349923, "lng": 23.975774627524885, "id": "d:6104:1"},  {"lat": 44.64680010013528, "lng": 23.20292820976948, "id": "c:6104:2"},  {"lat": 44.40446080879215, "lng": 23.953536570796015, "id": "b:6104:3"},  {"lat": 44.18593375168617, "lng": 23.769879901486856, "id": "af:6104:4"},  {"lat": 44.09051846584001, "lng": 24.14130778735744},  {"lat": 44.66376251969314, "lng": 23.77379490100736},  {"lat": 44.6240449587762, "lng": 24.08347249542858, "id": "aaaa3bab:3d:6f06"},  {"lat": 45.00138334367271, "lng": 24.092331272179138, "id": "aaaa3bab:3d:1306"},  {"lat": 44.55033831045195, "lng": 24.312914121854526, "id": "aaaa3bab:3c:ef05"},  {"lat": 44.74421652327631, "lng": 24.728457702115804, "id": "aaaa3bab:3c:ea03"},  {"lat": 43.79401723931746, "lng": 23.77846416630604, "id": "aaaa3bab:3d:7200"},  {"lat": 43.67351687345779, "lng": 23.00140978137842, "id": "aaaa3bab:3d:5d07"},  {"lat": 43.87692500855015, "lng": 24.28543591328852, "id": "aaaa3bab:3d:550b"},  {"lat": 44.28189405244278, "lng": 23.972410391551893, "id": "aaaa3bab:3d:2706"},  {"lat": 43.94916218711252, "lng": 23.9733463072956},  {"lat": 44.61479884874806, "lng": 24.27581898293906},  {"lat": 44.92223011339065, "lng": 23.505887513934034},  {"lat": 44.20117807597118, "lng": 23.70555450810448, "id": "aaaa3bab:3d:2603"},  {"lat": 43.547714841247966, "lng": 24.56985383484244, "id": "aaaa3bab:3d:2601"},  {"lat": 43.92116991202797, "lng": 22.82805535024416, "id": "aaaa3bab:3d:5803"},  {"lat": 44.56587414638437, "lng": 22.970799697228976, "id": "aaaa3bab:3d:7406"},  {"lat": 44.10230727065641, "lng": 23.701204095342597, "id": "aaaa3bab:3d:7407"},  {"lat": 45.25416535851712, "lng": 24.434312172789625, "id": "aaaa3bab:3d:7404"},  {"lat": 44.91647619491961, "lng": 23.678252259828515, "id": "aaaa3bab:3d:7405"},  {"lat": 45.03473433359779, "lng": 24.07596179597473},  {"lat": 45.16855171992733, "lng": 23.435986773864467},  {"lat": 44.553669079256146, "lng": 23.05123326220677},  {"lat": 43.32871087231798, "lng": 23.325707869122013, "id": "aaaa3bab:3d:5308"},  {"lat": 43.40444516345915, "lng": 23.485798521785892, "id": "aaaa3bab:3c:f107"},  {"lat": 43.9435337313432, "lng": 22.968285824722354},  {"lat": 44.74549949495889, "lng": 22.832034225254052},  {"lat": 44.34901730307382, "lng": 24.33506529636527},  {"lat": 43.53125851464172, "lng": 24.763229039168245, "id": "aaaa3bab:3d:6602"},  {"lat": 44.155575603194634, "lng": 23.250881840942217, "id": "aaaa3bab:3c:e300"}], "links": [ {"source": 1, "target": 25},  {"source": 1, "target": 26},  {"source": 1, "target": 27},  {"source": 1, "target": 28},  {"source": 1, "target": 29},  {"source": 1, "target": 30},  {"source": 1, "target": 31},  {"source": 1, "target": 34},  {"source": 1, "target": 35},  {"source": 1, "target": 36},  {"source": 3, "target": 5},  {"source": 3, "target": 6},  {"source": 4, "target": 15},  {"source": 4, "target": 9},  {"source": 5, "target": 19},  {"source": 5, "target": 23},  {"source": 6, "target": 18},  {"source": 6, "target": 20},  {"source": 7, "target": 22},  {"source": 8, "target": 37},  {"source": 8, "target": 3},  {"source": 10, "target": 11},  {"source": 17, "target": 21}, {"source": 18, "target": 13}, {"source": 18, "target": 14}, {"source": 19, "target": 33}, {"source": 19, "target": 38}, {"source": 23, "target": 2}, {"source": 25, "target": 10}, {"source": 28, "target": 4}, {"source": 28, "target": 17}, {"source": 29, "target": 32}, {"source": 32, "target": 25}, {"source": 34, "target": 24}, {"source": 35, "target": 8}, {"source": 35, "target": 16}, {"source": 37, "target": 7}, {"source": 37, "target": 12}], "multigraph": false}');

 d3.json(dataset, function (json) {
    var overlay = new google.maps.OverlayView();        

    // Add the container when the overlay is added to the map.
    overlay.onAdd = function () {

        var layer = d3.select(this.getPanes().overlayLayer)
            .append("div")
            .attr("height", "100%")
            .attr("width", "100%")
            .attr("class", "stations");
        overlay.draw = function () {
            var radius = 5;
            var projection = this.getProjection(),
                padding = 10;


            var node_coord = {};

            var marker = layer.selectAll("svg")
                .data(d3.entries(dataset))
                .each(transform) // update existing markers
                .enter().append("svg:svg")
                .each(transform)
                .attr("class", "marker");
            marker.append("svg:circle")
                .attr("r", radius)
                .attr("cx", padding)
                .attr("cy", padding);


            var markerLink = layer.selectAll(".links")
                .data(d3.entries(dataset))
                .each(pathTransform) // update existing markers       
                .enter().append("svg:svg")
                .attr("class", "links")
                .each(pathTransform);

            function pathTransform(d) {
                var t, b, l, r, w, h, currentSvg;
                var d1 = new Object();
                var d2 = new Object();
                $(this).empty();
                d1.x = node_coord[d.source + "," + 0]
                d1.y = node_coord[d.source + "," + 1]
                d2.x = node_coord[d.target + "," + 0]
                d2.y = node_coord[d.target + "," + 1]

                if (d1.y < d2.y) {
                    t = d1.y;
                    b = d2.y;
                } else {
                    t = d2.y;
                    b = d1.y;
                }
                if (d1.x < d2.x) {
                    l = d1.x;
                    r = d2.x;
                } else {
                    l = d2.x;
                    r = d1.x;
                }
                currentSvg = d3.select(this)
        .style("z-index", "1")
        .style("left", (l + 2 * radius) + "px")
                    .style("top", (t + 2 * radius) + "px")
                    .style("width", (r - l + 2 * radius) + "px")
                    .style("height", (b - t + 2 * radius) + "px");


                var x1 = 0, y1 = 0, x2 = 0, y2 = 0;
                if ((d1.y < d2.y) && (d1.x < d2.x)) {
                    x2 = r - l;
                    y2 = b - t;
                } else if ((d1.x > d2.x) && (d1.y > d2.y)) {
                    x2 = r - l;
                    y2 = b - t;
                } else if ((d1.y < d2.y) && (d1.x > d2.x)) {
                    x1 = r - l;
                    y2 = b - t;
                } else if ((d1.x < d2.x) && (d1.y > d2.y)) {
                    x1 = r - l;
                    y2 = b - t;
                }
                currentSvg.append("svg:line")
                        .style("stroke-width", 2)
                        .style("stroke", "black")
                        .attr("x1", x1)
                        .attr("y1", y1)
                        .attr("x2", x2)
                        .attr("y2", y2);

                return currentSvg;
            }

            function transform(d, i) {
                d = new google.maps.LatLng(d.lat, d.lng);
                d = projection.fromLatLngToDivPixel(d);

                node_coord[i + "," + 0] = d.x;
                node_coord[i + "," + 1] = d.y;

                return d3.select(this)
                    .style("left", (d.x) + "px")
                    .style("top", (d.y) + "px");
            }
            layer.append("div")
                .attr("class", "stations.line");

        };

    };

    // Bind our overlay to the map…
    overlay.setMap(map);
});

当我尝试实现仅绘制节点的example 时,它运行良好。但我需要使用链接对象中的源和目标详细信息在这些节点之间创建链接。

需要在谷歌地图上显示图表(附加)的种类。其中每个节点代表位置。

【问题讨论】:

    标签: javascript jquery google-maps d3.js


    【解决方案1】:

    您的代码中有错误。绑定到链接和节点的数据集不正确。您必须将节点数组设置为节点元素,将链接数组设置为链接元素。

    而不是下面的代码,

     var marker = layer.selectAll("svg")
       .data(d3.entries(dataset)) //Replace this line
       .each(transform) 
       .enter().append("svg:svg")
       .each(transform)
       .attr("class", "marker");
    
     marker.append("svg:circle")
       .attr("r", radius)
       .attr("cx", padding)
       .attr("cy", padding);   
    
     var markerLink = layer.selectAll(".links")
       .data(d3.entries(dataset)) //Replace this line
       .each(pathTransform)
       .enter().append("svg:svg")
       .attr("class", "links")
       .each(pathTransform);
    

    使用以下代码。

    var marker = layer.selectAll("svg")
       .data(dataset.nodes) 
       .each(transform) // update existing markers
       .enter().append("svg:svg")
       .each(transform)
       .attr("class", "marker");
     marker.append("svg:circle")
       .attr("r", radius)
       .attr("cx", padding)
       .attr("cy", padding);
    
     var markerLink = layer.selectAll(".links")
       .data(dataset.links)
       .each(pathTransform) // update existing markers       
       .enter().append("svg:svg")
       .attr("class", "links")
       .each(pathTransform);
    

    工作代码sn-p:

    var map = new google.maps.Map(d3.select("#map").node(), {
        zoom: 7,
        center: new google.maps.LatLng(-22.1629209, -47.9922608),
        mapTypeId: google.maps.MapTypeId.MAP
      });
    
      var dataset = JSON.parse('{"directed": true, "graph": [], "nodes": [{"lat": 44.391643516091975, "lng": 23.159677682342053}, {"lat": 44.315988, "lng": 23.818359, "id": "a:a::"},  {"lat": 44.29844994776969, "lng": 24.402314492323608, "id": "b:b"},  {"lat": 44.351118152120485, "lng": 23.341791630955303, "id": "a:c"},  {"lat": 44.889424527442685, "lng": 23.960970697645276, "id": "e:d"},  {"lat": 43.46084400349923, "lng": 23.975774627524885, "id": "d:6104:1"},  {"lat": 44.64680010013528, "lng": 23.20292820976948, "id": "c:6104:2"},  {"lat": 44.40446080879215, "lng": 23.953536570796015, "id": "b:6104:3"},  {"lat": 44.18593375168617, "lng": 23.769879901486856, "id": "af:6104:4"},  {"lat": 44.09051846584001, "lng": 24.14130778735744},  {"lat": 44.66376251969314, "lng": 23.77379490100736},  {"lat": 44.6240449587762, "lng": 24.08347249542858, "id": "aaaa3bab:3d:6f06"},  {"lat": 45.00138334367271, "lng": 24.092331272179138, "id": "aaaa3bab:3d:1306"},  {"lat": 44.55033831045195, "lng": 24.312914121854526, "id": "aaaa3bab:3c:ef05"},  {"lat": 44.74421652327631, "lng": 24.728457702115804, "id": "aaaa3bab:3c:ea03"},  {"lat": 43.79401723931746, "lng": 23.77846416630604, "id": "aaaa3bab:3d:7200"},  {"lat": 43.67351687345779, "lng": 23.00140978137842, "id": "aaaa3bab:3d:5d07"},  {"lat": 43.87692500855015, "lng": 24.28543591328852, "id": "aaaa3bab:3d:550b"},  {"lat": 44.28189405244278, "lng": 23.972410391551893, "id": "aaaa3bab:3d:2706"},  {"lat": 43.94916218711252, "lng": 23.9733463072956},  {"lat": 44.61479884874806, "lng": 24.27581898293906},  {"lat": 44.92223011339065, "lng": 23.505887513934034},  {"lat": 44.20117807597118, "lng": 23.70555450810448, "id": "aaaa3bab:3d:2603"},  {"lat": 43.547714841247966, "lng": 24.56985383484244, "id": "aaaa3bab:3d:2601"},  {"lat": 43.92116991202797, "lng": 22.82805535024416, "id": "aaaa3bab:3d:5803"},  {"lat": 44.56587414638437, "lng": 22.970799697228976, "id": "aaaa3bab:3d:7406"},  {"lat": 44.10230727065641, "lng": 23.701204095342597, "id": "aaaa3bab:3d:7407"},  {"lat": 45.25416535851712, "lng": 24.434312172789625, "id": "aaaa3bab:3d:7404"},  {"lat": 44.91647619491961, "lng": 23.678252259828515, "id": "aaaa3bab:3d:7405"},  {"lat": 45.03473433359779, "lng": 24.07596179597473},  {"lat": 45.16855171992733, "lng": 23.435986773864467},  {"lat": 44.553669079256146, "lng": 23.05123326220677},  {"lat": 43.32871087231798, "lng": 23.325707869122013, "id": "aaaa3bab:3d:5308"},  {"lat": 43.40444516345915, "lng": 23.485798521785892, "id": "aaaa3bab:3c:f107"},  {"lat": 43.9435337313432, "lng": 22.968285824722354},  {"lat": 44.74549949495889, "lng": 22.832034225254052},  {"lat": 44.34901730307382, "lng": 24.33506529636527},  {"lat": 43.53125851464172, "lng": 24.763229039168245, "id": "aaaa3bab:3d:6602"},  {"lat": 44.155575603194634, "lng": 23.250881840942217, "id": "aaaa3bab:3c:e300"}], "links": [ {"source": 1, "target": 25},  {"source": 1, "target": 26},  {"source": 1, "target": 27},  {"source": 1, "target": 28},  {"source": 1, "target": 29},  {"source": 1, "target": 30},  {"source": 1, "target": 31},  {"source": 1, "target": 34},  {"source": 1, "target": 35},  {"source": 1, "target": 36},  {"source": 3, "target": 5},  {"source": 3, "target": 6},  {"source": 4, "target": 15},  {"source": 4, "target": 9},  {"source": 5, "target": 19},  {"source": 5, "target": 23},  {"source": 6, "target": 18},  {"source": 6, "target": 20},  {"source": 7, "target": 22},  {"source": 8, "target": 37},  {"source": 8, "target": 3},  {"source": 10, "target": 11},  {"source": 17, "target": 21}, {"source": 18, "target": 13}, {"source": 18, "target": 14}, {"source": 19, "target": 33}, {"source": 19, "target": 38}, {"source": 23, "target": 2}, {"source": 25, "target": 10}, {"source": 28, "target": 4}, {"source": 28, "target": 17}, {"source": 29, "target": 32}, {"source": 32, "target": 25}, {"source": 34, "target": 24}, {"source": 35, "target": 8}, {"source": 35, "target": 16}, {"source": 37, "target": 7}, {"source": 37, "target": 12}], "multigraph": false}');
    
      d3.json(dataset, function(json) {
        var overlay = new google.maps.OverlayView();
    
        // Add the container when the overlay is added to the map.
        overlay.onAdd = function() {
    
          var layer = d3.select(this.getPanes().overlayLayer)
            .append("div")
            .attr("height", "100%")
            .attr("width", "100%")
            .attr("class", "stations");
          overlay.draw = function() {
            var radius = 5;
            var projection = this.getProjection(),
              padding = 10;
    
    
            var node_coord = {};
    
            var marker = layer.selectAll("svg")
              .data(dataset.nodes)
              .each(transform) // update existing markers
              .enter().append("svg:svg")
              .each(transform)
              .attr("class", "marker");
            marker.append("svg:circle")
              .attr("r", radius)
              .attr("cx", padding)
              .attr("cy", padding);
    
    
            var markerLink = layer.selectAll(".links")
              .data(dataset.links)
              .each(pathTransform) // update existing markers       
              .enter().append("svg:svg")
              .attr("class", "links")
              .each(pathTransform);
    
            function pathTransform(d) {
              var t, b, l, r, w, h, currentSvg;
              var d1 = new Object();
              var d2 = new Object();
              $(this).empty();
              d1.x = node_coord[d.source + "," + 0]
              d1.y = node_coord[d.source + "," + 1]
              d2.x = node_coord[d.target + "," + 0]
              d2.y = node_coord[d.target + "," + 1]
    
              if (d1.y < d2.y) {
                t = d1.y;
                b = d2.y;
              } else {
                t = d2.y;
                b = d1.y;
              }
              if (d1.x < d2.x) {
                l = d1.x;
                r = d2.x;
              } else {
                l = d2.x;
                r = d1.x;
              }
              currentSvg = d3.select(this)
                .style("z-index", "1")
                .style("left", (l + 2 * radius) + "px")
                .style("top", (t + 2 * radius) + "px")
                .style("width", (r - l + 2 * radius) + "px")
                .style("height", (b - t + 2 * radius) + "px");
    
    
              var x1 = 0,
                y1 = 0,
                x2 = 0,
                y2 = 0;
              if ((d1.y < d2.y) && (d1.x < d2.x)) {
                x2 = r - l;
                y2 = b - t;
              } else if ((d1.x > d2.x) && (d1.y > d2.y)) {
                x2 = r - l;
                y2 = b - t;
              } else if ((d1.y < d2.y) && (d1.x > d2.x)) {
                x1 = r - l;
                y2 = b - t;
              } else if ((d1.x < d2.x) && (d1.y > d2.y)) {
                x1 = r - l;
                y2 = b - t;
              }
              currentSvg.append("svg:line")
                .style("stroke-width", 2)
                .style("stroke", "black")
                .attr("x1", x1)
                .attr("y1", y1)
                .attr("x2", x2)
                .attr("y2", y2);
    
              return currentSvg;
            }
    
            function transform(d, i) {
              console.log("here");
              d = new google.maps.LatLng(d.lat, d.lng);
              d = projection.fromLatLngToDivPixel(d);
    
              node_coord[i + "," + 0] = d.x;
              node_coord[i + "," + 1] = d.y;
    
              return d3.select(this)
                .style("left", (d.x) + "px")
                .style("top", (d.y) + "px");
            }
            layer.append("div")
              .attr("class", "stations.line");
    
          };
    
        };
    
        // Bind our overlay to the map…
        overlay.setMap(map);
      });
    html,
    body,
    #map {
      width: 100%;
      height: 100%;
      margin: 0;
      padding: 0;
    }
    .stations,
    .stations svg {
      position: absolute;
      margin: 0 auto;
    }
    .links {
      position: absolute;
    }
    .stations svg {
      width: 20px;
      height: 20px;
      margin: 0 auto;
      z-index: 4;
    }
    .stations circle {
      fill: blue;
      stroke: none;
      stroke-width: 1px;
    }
    <head>
      <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
      <script src="https://maps.googleapis.com/maps/api/js?v=3.exp"></script>
      <script src="http://d3js.org/topojson.v1.min.js"></script>
      <script src="//code.jquery.com/jquery-1.10.2.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js">
      </script>
    </head>
    
    <body>
      <div id="map"></div>

    【讨论】:

    • 完美运行..非常感谢
    • 还有一个问题要问你。我需要在此处更改什么以显示从源节点到目标节点的箭头,就像在我附加的示例中一样?我试图在我定义链接的地方添加以下内容。但它不起作用。 .style("marker-end", "url(#suit)") .attr('marker-start', function (d) { return d.birectional ? 'url(#start-arrow)' : null; }) ;
    • 你是否在 defs 中添加了西装和开始箭头标记?确保您已使用相应的 id 定义它们。
    • 我已经添加了这样的.. layer.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", 12) 。 attr("refY", 0) .attr("markerWidth", 6) .attr("markerHeight", 6) .attr("orient", "auto") .append("path") .attr("d" , "M0,-5L10,0L0,5 L10,0 L0, -5") .style("stroke", "#4679BD") .style("opacity", "1");
    • NithyaShree,我认为这行不通,因为layour 是 div 而不是 svg 元素。您必须将 def 附加到链接的 svg 容器中。
    【解决方案2】:

    不确定您是否可以单独使用 d3 来绘制 svg 线。

    我正在使用 google google.maps.Polyline 生成线条

    类似这样的:

    var flightPlanCoordinates = [
        {lat: 36.98, lng: -120.12},//start point of line
        {lat: 37.37, lng: -121.92},//end point of line1
        {lat: 37.28, lng: -120.50},//end point of line2
        {lat:37.70, lng:-121.82}//end point of line3
      ];
      var flightPath = new google.maps.Polyline({
        path: flightPlanCoordinates,
        geodesic: true,
        strokeColor: '#FF0000',
        strokeOpacity: 1.0,
        strokeWeight: 2
      });
    
      flightPath.setMap(map);
    

    工作代码here

    希望这会有所帮助!

    【讨论】:

    • 是的。有用。它允许我在两个节点之间画线。但它需要一个用于该折线的 foreach 循环。
    • 如果您需要一条线连接所有点,那么您需要相应地制作数组 flightPlanCoordinates。您可以阅读此文档developers.google.com/maps/documentation/javascript/examples/… ...它涵盖了您可以使用谷歌地图绘制的所有可能方式在地图上绘制
    • 是的。但我可以在那里做一条连续的线。不像我在问题中附上的图片。
    猜你喜欢
    • 1970-01-01
    • 2011-07-16
    • 1970-01-01
    • 1970-01-01
    • 2012-03-23
    • 2023-03-20
    • 1970-01-01
    • 1970-01-01
    • 2012-09-26
    相关资源
    最近更新 更多