2016-04-27 29 views
3

我是编程新手,我一直在研究O'Reilly Interactive Data Visualization书中的例子。D3 - 在美国地图上渲染多年的数据状态数据

现在,我正在努力将州电动汽车的5年数据可视化。每个州的颜色都会根据特定年份内注册的电动车辆的数量而变化。

问题

每次我点击下拉菜单中year,它会创建它下面另一个地图SVG。我怎样才能得到它重新呈现在同一(现有)地图SVG的数据?

背景:

我有一个定义了SVG的尺寸全局变量。我也有两个功能:

  • drawMap有一年的论证,并建立了SVG,从CSV到us_map.json加载数据,并创建所有的国界线。
  • clickEventMap将事件侦听器附加到每个年份按钮,然后调用drawMap函数通过与按钮上年份匹配的年份。最后,我拨打drawMap(2013),以便页面默认为2013年的数据,然后致电clickEventMap()

我迄今为止尝试:

console.log(year)的D3 color.domain方法和正确的一年之后被存储为在该点的一年变量。

另外,如果手动更改参数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(); 

感谢您的输入。

+0

你能提供一个工作小提琴/ plnk/jsbin? – iulian

回答

0

您的主要问题是您在drawMap函数中做得太多。很多内容只需要做一次,而不是每次切换多年。这就是为什么你得到多个地图。此代码每次创建一个新的SVG你打电话drawMap

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"; 
    } 
}); 

迈克Bostocks文章Thinking with JoinsThree Little Circles是在D3了解更新都是伟大的资源。基本上,只要想一下你只需要做一次什么事情,每次点击新的一年就需要改变什么(更新你的色阶并为每个状态设置填充样式)。尝试将发生的所有事情都放到init函数中,并保持drawMap函数的小。

+0

谢谢你的回应,詹姆斯。 – arsm800

+0

我移动了drawMap()函数上面的一些变量并创建了一个路径变量。虽然每次都没有创建新的svg,但初始映射svg没有改变。 我最终做的是将地图svg放在一个ID为“usmap”的div中,然后做'''var svg = d3.select(“#usmap”)'''。另外,在声明svg之前,我在drawMap()函数的开头添加了'''$(“#usmap”)。empty()'''。清空div然后创建一个新的svg,虽然可能不是最有效或最优雅的方式,似乎工作。 – arsm800

+0

嗯,我想清空div是让它工作的最简单方法。但我认为你错过了没有利用d3的更新模式。特别是加载地图json会使重绘速度变慢,因此您至少应该尝试仅请求一次该请求。 –