代码之家  ›  专栏  ›  技术社区  ›  MetalCat

在d3中,如何在地图上的鼠标上方显示多个值?

  •  2
  • MetalCat  · 技术社区  · 8 年前

    我想在地图上的国家上方悬停时显示多个值(价格、点数)。例如,当我在英国上空盘旋时,我想看到平均价格和点数。但我只能展示其中一个 d.properties.name 。 代码取自 here.

    我对代码的更改:

    <!DOCTYPE html>
    <meta charset="utf-8">
    <style>
    
    body {
      font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
      width: 960px;
      height: 500px;
      position: relative;
    }
    
    #canvas {
    }
    
    #canvas-svg {
    }
    
    
    .land {
      fill: #222;
    }
    
    .boundary {
      fill: none;
      stroke: #fff;
      stroke-width: 1px;
    }
    
    #tooltip-container {
      position: absolute;
      background-color: #fff;
      color: #000;
      padding: 10px;
      border: 1px solid;
      display: none;
    }
    
    .tooltip_key {
      font-weight: bold;
    }
    
    .tooltip_value {
      margin-left: 20px;
      float: right;
    }
    
    </style>
    
    <div id="tooltip-container"></div>
    
    <div id="canvas-svg"></div>
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/topojson/1.1.0/topojson.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
    
    <script>
    
        
    d3.csv("MyData130.csv", function(err, data) {
    
      var config = {"data0":"country","data1":"price", "data2": "points",
                  "label0":"label 0","label1":"label 1","label2":"label 2", "color0":"#99ccff","color1":"#0050A1", "color3":"#4398ef",
                  "width":900,"height":900}
      
      var width = config.width,
          height = config.height;
      
      var COLOR_COUNTS = 9;
      
      function Interpolate(start, end, steps, count) {
          var s = start,
              e = end,
              final = s + (((e - s) / steps) * count);
          return Math.floor(final);
      }
      
      function Color(_r, _g, _b) {
          var r, g, b;
          var setColors = function(_r, _g, _b) {
              r = _r;
              g = _g;
              b = _b;
          };
      
          setColors(_r, _g, _b);
          this.getColors = function() {
              var colors = {
                  r: r,
                  g: g,
                  b: b
              };
              return colors;
          };
      }
      
      function hexToRgb(hex) {
          var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
          return result ? {
              r: parseInt(result[1], 16),
              g: parseInt(result[2], 16),
              b: parseInt(result[3], 16)
          } : null;
      }
      
      function valueFormat(d) {
        if (d > 1000000000) {
          return Math.round(d / 1000000000 * 10) / 10 + "B";
        } else if (d > 1000000) {
          return Math.round(d / 1000000 * 10) / 10 + "M";
        } else if (d > 1000) {
          return Math.round(d / 1000 * 10) / 10 + "K";
        } else {
          return d;
        }
      }
      
      var COLOR_FIRST = config.color0, COLOR_LAST = config.color1;
      
      var rgb = hexToRgb(COLOR_FIRST);
      
      var COLOR_START = new Color(rgb.r, rgb.g, rgb.b);
      
      rgb = hexToRgb(COLOR_LAST);
      var COLOR_END = new Color(rgb.r, rgb.g, rgb.b);
      
      var startColors = COLOR_START.getColors(),
          endColors = COLOR_END.getColors();
      
      var colors = [];
      
      for (var i = 0; i < COLOR_COUNTS; i++) {
        var r = Interpolate(startColors.r, endColors.r, COLOR_COUNTS, i);
        var g = Interpolate(startColors.g, endColors.g, COLOR_COUNTS, i);
        var b = Interpolate(startColors.b, endColors.b, COLOR_COUNTS, i);
        colors.push(new Color(r, g, b));
      }
      
      var MAP_KEY = config.data0;
      var MAP_VALUE = config.data1;
        
        var MAP_KEY2 = config.data0;
        var MAP_VALUE2 = config.data2;
      
      var projection = d3.geo.mercator()
          .scale((width + 1) / 2 / Math.PI)
          .translate([width / 2, height / 2])
          .precision(.1);
      
      var path = d3.geo.path()
          .projection(projection);
      
      var graticule = d3.geo.graticule();
      
      var svg = d3.select("#canvas-svg").append("svg")
          .attr("width", width)
          .attr("height", height);
      
      svg.append("path")
          .datum(graticule)
          .attr("class", "graticule")
          .attr("d", path);
      
      var valueHash = {};
      
      function log10(val) {
        return Math.log(val);
      }
      
      data.forEach(function(d) {
        valueHash[d[MAP_KEY]] = +d[MAP_VALUE];
        valueHash[d[MAP_KEY2]] = +d[MAP_VALUE2];
      });
      
        
      var quantize = d3.scale.quantize()
          .domain([0, 1.0])
          .range(d3.range(COLOR_COUNTS).map(function(i) { return i }));
      
      quantize.domain([d3.min(data, function(d){
          return (+d[MAP_VALUE], +d[MAP_VALUE2]) }),
        d3.max(data, function(d){
          return (+d[MAP_VALUE], +d[MAP_VALUE2])})]);
      
      d3.json("https://s3-us-west-2.amazonaws.com/vida-public/geo/world-topo-min.json", function(error, world) {
        var countries = topojson.feature(world, world.objects.countries).features;
      
        svg.append("path")
           .datum(graticule)
           .attr("class", "choropleth")
           .attr("d", path);
      
        var g = svg.append("g");
      
        g.append("path")
         .datum({type: "LineString", coordinates: [[-180, 0], [-90, 0], [0, 0], [90, 0], [180, 0]]})
         .attr("class", "equator")
         .attr("d", path);
      
        var country = g.selectAll(".country").data(countries);
      
        country.enter().insert("path")
            .attr("class", "country")
            .attr("d", path)
            .attr("id", function(d,i) { return d.id; })
            .attr("title", function(d) { return d.properties.name; })
            .style("fill", function(d) {
              if ((valueHash[d.properties.name])) {
                  valueHash[d.properties.value] = valueHash[d[MAP_KEY2]];
                var c = quantize((valueHash[d.properties.name]) );
                var color = colors[c].getColors();
                return "rgb(" + color.r + "," + color.g +
                    "," + color.b + ")";
              } else {
                return "#ccc";
              }
            })
            .on("mousemove", function(d) {
                var html = "";
      
                html += "<div class=\"tooltip_kv\">";
                html += "<span class=\"tooltip_key\">";
                html += d.properties.name + "<br/>";
    //            html += d.properties.value;   
                html += "</span>";
                html += "<span class=\"tooltip_value\">";
                html += (valueHash[d.properties.name] ? "Average Price: " + valueFormat(valueHash[d.properties.name]) +" $"+ 
                         "<br/>" + "Average Points: " + valueFormat(valueHash[d.properties.value]) : "");
                html += "";
                html += "</span>";
                html += "</div>";
                
                $("#tooltip-container").html(html);
                $(this).attr("fill-opacity", "0.8");
                $("#tooltip-container").show();
                
                var coordinates = d3.mouse(this);
                
                var map_width = $('.choropleth')[0].getBoundingClientRect().width;
                
                if (d3.event.pageX < map_width / 2) {
                  d3.select("#tooltip-container")
                    .style("top", (d3.event.layerY + 15) + "px")
                    .style("left", (d3.event.layerX + 15) + "px");
                } else {
                  var tooltip_width = $("#tooltip-container").width();
                  d3.select("#tooltip-container")
                    .style("top", (d3.event.layerY + 15) + "px")
                    .style("left", (d3.event.layerX - tooltip_width - 30) + "px");
                }
            })
            .on("mouseout", function() {
                    $(this).attr("fill-opacity", "1.0");
                    $("#tooltip-container").hide();
                });
        
        g.append("path")
            .datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }))
            .attr("class", "boundary")
            .attr("d", path);
        
        svg.attr("height", config.height * 2.2 / 3);
      });
      
      d3.select(self.frameElement).style("height", (height * 2.3 / 3) + "px");
    });
        
        
    
    
    </script>

    CSV文件:

    	country	price	points
    1	Azerbaijan	24.59322034	88.63492063
    2	Argentina	24.51011715	86.71026316
    3	Armenia	14.5	87.5
    4	Australia	35.43766347	88.58050666
    5	Austria	30.76277242	90.10134529
    6	Bosnia and Herzegovina	12.5	86.5
    7	Brazil	23.76595745	84.67307692
    8	Bulgaria	14.64539007	87.93617021
    9	Canada	35.71259843	89.36964981
    10	Chile	20.78645833	86.49351521

    我真的很愿意自己去实现它,但我不知道该怎么做。我在谷歌上搜索了很多次,但都找不到任何有用的东西。也许有人有一个想法或给我一些提示,这样我就可以做得对。

    1 回复  |  直到 8 年前
        1
  •  1
  •   Chirag Kothari    8 年前

    几个问题:

    • tsv正在解析为csv
    • 您正在覆盖价格上的点(MAP\U KEY和MAP\U KEY2都是“国家”):
    valueHash[d[MAP_KEY]] = +d[MAP_VALUE];
    valueHash[d[MAP_KEY2]] = +d[MAP_VALUE2];
    

    我对价格和积分使用了不同的散列,但我建议您使用更好的数据结构来保存这两种数据。以下是更正的代码:

    <!DOCTYPE html>
    <meta charset="utf-8">
    <style>
    
        body {
            font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
            width: 960px;
            height: 500px;
            position: relative;
        }
    
        #canvas {
        }
    
        #canvas-svg {
        }
    
    
        .land {
            fill: #222;
        }
    
        .boundary {
            fill: none;
            stroke: #fff;
            stroke-width: 1px;
        }
    
        #tooltip-container {
            position: absolute;
            background-color: #fff;
            color: #000;
            padding: 10px;
            border: 1px solid;
            display: none;
        }
    
        .tooltip_key {
            font-weight: bold;
        }
    
        .tooltip_value {
            margin-left: 20px;
            float: right;
        }
    
    </style>
    
    <div id="tooltip-container"></div>
    
    <div id="canvas-svg"></div>
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/topojson/1.1.0/topojson.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
    
    <script>
    
    
        d3.tsv("dat.tsv", function(err, data) {
    
            var config = {"data0":"country","data1":"price", "data2": "points",
                "label0":"label 0","label1":"label 1","label2":"label 2", "color0":"#99ccff","color1":"#0050A1", "color3":"#4398ef",
                "width":900,"height":900}
    
            var width = config.width,
                    height = config.height;
    
            var COLOR_COUNTS = 9;
    
            function Interpolate(start, end, steps, count) {
                var s = start,
                        e = end,
                        final = s + (((e - s) / steps) * count);
                return Math.floor(final);
            }
    
            function Color(_r, _g, _b) {
                var r, g, b;
                var setColors = function(_r, _g, _b) {
                    r = _r;
                    g = _g;
                    b = _b;
                };
    
                setColors(_r, _g, _b);
                this.getColors = function() {
                    var colors = {
                        r: r,
                        g: g,
                        b: b
                    };
                    return colors;
                };
            }
    
            function hexToRgb(hex) {
                var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
                return result ? {
                    r: parseInt(result[1], 16),
                    g: parseInt(result[2], 16),
                    b: parseInt(result[3], 16)
                } : null;
            }
    
            function valueFormat(d) {
                if (d > 1000000000) {
                    return Math.round(d / 1000000000 * 10) / 10 + "B";
                } else if (d > 1000000) {
                    return Math.round(d / 1000000 * 10) / 10 + "M";
                } else if (d > 1000) {
                    return Math.round(d / 1000 * 10) / 10 + "K";
                } else {
                    return d;
                }
            }
    
            var COLOR_FIRST = config.color0, COLOR_LAST = config.color1;
    
            var rgb = hexToRgb(COLOR_FIRST);
    
            var COLOR_START = new Color(rgb.r, rgb.g, rgb.b);
    
            rgb = hexToRgb(COLOR_LAST);
            var COLOR_END = new Color(rgb.r, rgb.g, rgb.b);
    
            var startColors = COLOR_START.getColors(),
                    endColors = COLOR_END.getColors();
    
            var colors = [];
    
            for (var i = 0; i < COLOR_COUNTS; i++) {
                var r = Interpolate(startColors.r, endColors.r, COLOR_COUNTS, i);
                var g = Interpolate(startColors.g, endColors.g, COLOR_COUNTS, i);
                var b = Interpolate(startColors.b, endColors.b, COLOR_COUNTS, i);
                colors.push(new Color(r, g, b));
            }
    
            var MAP_KEY = config.data0;
            var MAP_VALUE = config.data1;
    
            var MAP_KEY2 = config.data0;
            var MAP_VALUE2 = config.data2;
    
            var projection = d3.geo.mercator()
                    .scale((width + 1) / 2 / Math.PI)
                    .translate([width / 2, height / 2])
                    .precision(.1);
    
            var path = d3.geo.path()
                    .projection(projection);
    
            var graticule = d3.geo.graticule();
    
            var svg = d3.select("#canvas-svg").append("svg")
                    .attr("width", width)
                    .attr("height", height);
    
            svg.append("path")
                    .datum(graticule)
                    .attr("class", "graticule")
                    .attr("d", path);
    
            var valueHash = {};
    
            function log10(val) {
                return Math.log(val);
            }
    
            data.forEach(function(d) {
                valueHash[d[MAP_KEY]] = +d[MAP_VALUE];
            });
    
            var pointsHash = {};
    
            data.forEach(function(d) {
                pointsHash[d[MAP_KEY2]] = +d[MAP_VALUE2];
            });
    
            var quantize = d3.scale.quantize()
                    .domain([0, 1.0])
                    .range(d3.range(COLOR_COUNTS).map(function(i) { return i }));
    
            quantize.domain([d3.min(data, function(d){
                return (+d[MAP_VALUE], +d[MAP_VALUE2]) }),
                d3.max(data, function(d){
                    return (+d[MAP_VALUE], +d[MAP_VALUE2])})]);
    
            d3.json("https://s3-us-west-2.amazonaws.com/vida-public/geo/world-topo-min.json", function(error, world) {
                var countries = topojson.feature(world, world.objects.countries).features;
    
                svg.append("path")
                        .datum(graticule)
                        .attr("class", "choropleth")
                        .attr("d", path);
    
                var g = svg.append("g");
    
                g.append("path")
                        .datum({type: "LineString", coordinates: [[-180, 0], [-90, 0], [0, 0], [90, 0], [180, 0]]})
                        .attr("class", "equator")
                        .attr("d", path);
    
                var country = g.selectAll(".country").data(countries);
    
                country.enter().insert("path")
                        .attr("class", "country")
                        .attr("d", path)
                        .attr("id", function(d,i) { return d.id; })
                        .attr("title", function(d) { return d.properties.name; })
                        .style("fill", function(d) {
                            if ((valueHash[d.properties.name])) {
                                valueHash[d.properties.value] = valueHash[d[MAP_KEY2]];
                                var c = quantize((valueHash[d.properties.name]) );
                                var color = colors[c].getColors();
                                return "rgb(" + color.r + "," + color.g +
                                        "," + color.b + ")";
                            } else {
                                return "#ccc";
                            }
                        })
                        .on("mousemove", function(d) {
                            debugger;
                            var html = "";
    
                            html += "<div class=\"tooltip_kv\">";
                            html += "<span class=\"tooltip_key\">";
                            html += d.properties.name + "<br/>";
    //            html += d.properties.value;
                            html += "</span>";
                            html += "<span class=\"tooltip_value\">";
                            html += (valueHash[d.properties.name] ? "Average Price: " + valueFormat(valueHash[d.properties.name]) +" $"+
                            "<br/>" + "Average Points: " + valueFormat(pointsHash[d.properties.name]) : "");
                            html += "";
                            html += "</span>";
                            html += "</div>";
    
                            $("#tooltip-container").html(html);
                            $(this).attr("fill-opacity", "0.8");
                            $("#tooltip-container").show();
    
                            var coordinates = d3.mouse(this);
    
                            var map_width = $('.choropleth')[0].getBoundingClientRect().width;
    
                            if (d3.event.pageX < map_width / 2) {
                                d3.select("#tooltip-container")
                                        .style("top", (d3.event.layerY + 15) + "px")
                                        .style("left", (d3.event.layerX + 15) + "px");
                            } else {
                                var tooltip_width = $("#tooltip-container").width();
                                d3.select("#tooltip-container")
                                        .style("top", (d3.event.layerY + 15) + "px")
                                        .style("left", (d3.event.layerX - tooltip_width - 30) + "px");
                            }
                        })
                        .on("mouseout", function() {
                            $(this).attr("fill-opacity", "1.0");
                            $("#tooltip-container").hide();
                        });
    
                g.append("path")
                        .datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }))
                        .attr("class", "boundary")
                        .attr("d", path);
    
                svg.attr("height", config.height * 2.2 / 3);
            });
    
            d3.select(self.frameElement).style("height", (height * 2.3 / 3) + "px");
        });
    
    
    
    
    </script>

    no	country	price	points
    1	Azerbaijan	24.59322034	88.63492063
    2	Argentina	24.51011715	86.71026316
    3	Armenia	14.5	87.5
    4	Australia	35.43766347	88.58050666
    5	Austria	30.76277242	90.10134529
    6	Bosnia and Herzegovina	12.5	86.5
    7	Brazil	23.76595745	84.67307692
    8	Bulgaria	14.64539007	87.93617021
    9	Canada	35.71259843	89.36964981
    10	Chile	20.78645833	86.49351521