【问题标题】:How to display multiple values on mouseover on map in d3?如何在 d3 地图上的鼠标悬停上显示多个值?
【发布时间】:2018-10-22 03:14:38
【问题描述】:

我想在地图上的国家/地区上悬停时显示多个值(价格、积分)。例如,当我将鼠标悬停在英国时,我想同时查看平均价格和积分。但我只能用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

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

【问题讨论】:

    标签: javascript dictionary d3.js mouseover


    【解决方案1】:

    几个问题:

    • tsv 被解析为 csv
    • 您正在覆盖价格点(MAP_KEY 和 MAP_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

    【讨论】:

    • 非常感谢!现在我明白了这个问题。它真的很酷!
    猜你喜欢
    • 2018-01-23
    • 2018-09-06
    • 2016-12-14
    • 2015-02-20
    • 1970-01-01
    • 1970-01-01
    • 2017-03-06
    • 2023-03-27
    • 1970-01-01
    相关资源
    最近更新 更多