【问题标题】:D3 - Rendering Multiple Years' Worth of State Data on US MapD3 - 在美国地图上呈现多年的州数据价值
【发布时间】:2016-08-22 04:53:47
【问题描述】:

我是编程新手,我一直在研究 O'Reilly 交互式数据可视化一书中的示例。

目前,我正致力于按州可视化 5 年的电动汽车数据。每个州的颜色会根据该特定年份注册的电动汽车数量而变化。

问题

每次我点击下拉菜单中的year 时,它都会在其下方创建另一个地图 svg。我怎样才能让它在同一个(现有的)地图 svg 上重新渲染数据?

背景:

我有定义 svg 尺寸的全局变量。我还有两个功能:

  • drawMap 有一个 year 参数并构建 svg,将数据从 csv 加载到 us_map.json,并创建所有状态边界。
  • clickEventMap 将事件侦听器附加到每个年份按钮,然后调用 drawMap 函数传递与按钮上的年份匹配的年份。最后,我调用drawMap(2013),因此页面默认为2013年数据,然后调用clickEventMap()

到目前为止我所尝试的:

我在 d3 color.domain 方法之后 console.log(year) 并且正确的年份存储为此时的年份变量。

另外,如果我在调用drawMap() 时手动更改参数,我可以成功更改现有地图上呈现的数据。

我的代码:

var w = 1200;
var h = 800;
var barpadding = 1;

var drawMap = function(year) {

  var svg = d3.select("body")
  .append("svg")
  .attr("height", h)
  .attr("width", w);

  var projection = d3.geo.albersUsa()
                         .translate([w/2, h/2]) 
                         .scale([1600]);   

  var path = d3.geo.path()
                   .projection(projection);

  var color = d3.scale.quantize()   
                    .range(["rgb(237, 248, 233)", "rgb(186, 228, 179)", "rgb(116,196,118)", "rgb(49, 163, 84)", "rgb(0, 109, 44)"]);


  color.domain([
    d3.min(data, function(d) {return d["electric_vehicles_" + year];}),
    d3.max(data, function(d) { return d["electric_vehicles_" + year];})

  ]);

  console.log(year);

  d3.json("us-states.json", function(json) {  
    for (var i = 0; i < data.length; i ++) {  
      var dataState = data[i].state;         
      var dataValue = parseFloat(data[i]["electric_vehicles_" + year]);  
      for (var j = 0; j < json.features.length; j++) {   
        var jsonState = json.features[j].properties.name;
        if (dataState === jsonState) {                    
          json.features[j].properties.value = dataValue;
          break;
        }
      }
    }

    var stateView = function() {
    console.log("State clicked.");
    location.href="/states/1";  

    svg.selectAll("path")
      .data(json.features)
      .enter()
      .append("path")
      .attr("d", path)
      .style("fill", function(d) {
        var value = d.properties.value;
        if (value) {
          return color(value);
        }
        else {
          return "#ccc";
        }
      })
      .on("mouseover", function(d, i) {
        d3.select(this)
        .style("fill-opacity", 0.5);
      })
      .on("mouseout", function(d, i) {
        d3.selectAll("path")
        .style("fill-opacity", 1);
      })
      .on("click", function() {
        stateView();
      });
  });
});
};

var clickEventMap = function() {
  $("#map2013").on("click", function() {
    // var year = 2013;
    drawMap(2013);
  });

  $("#map2012").on("click", function() {
    // var year = 2012;
    drawMap(2012);
  });

  $("#map2011").on("click", function() {
    // var year = 2011;
    drawMap(2011);
  });

  $("#map2010").on("click", function() {
    // var year = 2010;
    drawMap(2010);
  });

  $("#map2009").on("click", function() {
    // var year = 2009;
    drawMap(2009);
  });
};

drawMap(2013);
clickEventMap();

感谢您的意见。

【问题讨论】:

  • 你能提供一个工作小提琴/plnk/jsbin吗?

标签: javascript jquery json d3.js svg


【解决方案1】:

您的主要问题是您在 drawMap 函数中做了太多事情。里面的很多东西只需要做一次,而不是每次换年。这就是为什么您要获得多张地图的原因。每次调用drawMap时,这段代码都会创建一个新的svg

var svg = d3.select("body")
  .append("svg")
  .attr("height", h)
  .attr("width", w);

如果你只是把它移出drawMap 函数,你只会得到一个svg。

每次绘制地图时,您都会获得“us-states.json”文件。这可能在技术上可行,但会导致许多您确实不需要的慢速网络请求。只需在页面加载时发出一次请求。这是对代码的相当大的重组更改,因为基本上之后的所有内容都需要从回调中调用。

最后,在进行这些更改后,您需要更改绘制状态的代码,以使它们正确更新。现在你在“输入”选项上做所有事情,它只会在新路径上运行。由于您将编辑现有路径,因此您的代码需要看起来更像这样:

var paths = svg.selectAll("path")
    .data(json.features)
    .enter()
    .append("path")
    .attr("d", path)
    .on("mouseover", function(d, i) {
        d3.select(this)
            .style("fill-opacity", 0.5);
    })
    .on("mouseout", function(d, i) {
        d3.selectAll("path")
            .style("fill-opacity", 1);
    })
    .on("click", function() {
        stateView();
    });

paths.style("fill", function(d) {
    var value = d.properties.value;
    if (value) {
        return color(value);
    } else {
        return "#ccc";
    }
});

Mike Bostocks 的文章 Thinking with JoinsThree Little Circles 都是了解 d3 更新的绝佳资源。

基本上,只需考虑一下您只需要做一次,以及每次点击新的一年时需要更改哪些内容(更新您的色阶并为每个州设置填充样式)。尝试将曾经发生的所有事情都拉到一个 init 函数中,并保持你的 drawMap 函数很小。

【讨论】:

  • 感谢您的回复,詹姆斯。
  • 我在 drawMap() 函数上方移动了一些变量并创建了一个路径变量。虽然每次都没有创建新的 svg,但初始地图 svg 并没有改变。我最终做的是将地图 svg 放入 id 为“usmap”的 div 中,然后执行var svg = d3.select("#usmap")。此外,在声明 svg 之前,我在 drawMap() 函数的开头添加了$("#usmap").empty()。清空 div 然后创建一个新的 svg,虽然可能不是最有效或最优雅的方式,但似乎可行。
  • 好吧,我想清空 div 是让它工作的最简单方法。但我认为你错过了不利用 d3 的更新模式。尤其是加载地图 json 会使重绘速度变慢,因此您至少应该尝试只发出一次请求。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-23
  • 1970-01-01
  • 1970-01-01
  • 2022-08-18
  • 2016-03-21
相关资源
最近更新 更多