如何嵌套GeoJSON / TopoJSON几何图形或将生成的路径嵌套到D3?

时间:2015-06-14 17:22:08

标签: javascript svg d3.js geojson topojson

问题:

我正在尝试创建美国的交互式地图,其中显示州,县和国家边界。县根据数据着色,悬停在州上应该突出显示州内的所有县,州应该是可点击的。我希望通过在状态形状内部具有县形状的SVG来实现这一点,在美国形状内部。

我可以基于CENSUS县形状文件生成县地图,我可以通过使用TopoJSON命令行准备文件并使用D3中的以下代码,根据外部CSV中的数据来设置状态:

<!DOCTYPE html>
<meta charset="utf-8">
<style>

path {
  fill: none;
  stroke-linejoin: round;
  stroke-linecap: round;
}

</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script>

var width = 960,
    height = 600;

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

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

d3.json("counties_pa.json", function(error, us) {
  if (error) return console.error(error);

var color = d3.scale.threshold()
    .domain([1, 10, 50, 100, 500, 1000, 2000, 5000])
    .range(["#fff7ec", "#fee8c8", "#fdd49e", "#fdbb84", "#fc8d59", "#ef6548", "#d7301f", "#b30000", "#7f0000"]);



svg.append('g').attr('class','counties').selectAll("path").data(topojson.feature(us, us.objects.cb_2014_us_county_20m).features).enter().append('path').attr('d',path).attr('style',function(d){return 'fill:'+color(d.properties.population / d.properties.area * 2.58999e6);});

});



</script>

这在视觉上是可接受的(除了它没有离散的州/国界) - 但在功能上是不合适的。为了将CSS应用于状态悬停的县,县需要处于状态形状或以某种方式分组。

我尝试了什么:

  • 在命令行中使用topojson-merge将各州合并为状态形状,然后分别渲染状态形状 - 这有助于具有离散的状态边界 - 但我还没有找到一种方法将县嵌套到相应的状态形状。

我现在在做什么:

  • 以某种方式组合状态TopoJSON文件和县TopoJSON文件并将状态嵌套在县中,然后使用D3进行渲染。

  • 以某种方式使用d3获取非嵌套状态和县数据,并将其嵌套在客户端级别的客户端上。

最后,我想了解最有效,最快速的渲染过程,以实现我想要的功能。

提前感谢您的帮助。

1 个答案:

答案 0 :(得分:2)

我对你的数据源进行了试探,这就是你想要实现的目标:http://bl.ocks.org/benlyall/55bc9474e6d531a1c1fe

基本上,我使用以下命令行生成了TopoJSON文件:

topojson -o counties_pa.json --id-property=+GEOID -p -e POP01.txt --id-property=+STCOU -p population=+POP010210D,area=ALAND,state=+STATEFP,county=+COUNTYFP cb_2014_us_county_20m.shp cb_2014_us_state_20m.shp

对此的一些解释:

  • -o counties_pa.json设置输出文件的名称
  • --id-property=+GEOID将输入文件中的该属性用作每个输出几何的id
  • -p表示包含输入文件中的所有属性
  • -e POP01.txt将从文件POP01.txt中提取外部数据。此文件是根据http://www.census.gov/support/USACdataDownloads.html#POP
  • 提供的POP01.xls电子表格生成的csv文件
  • --id-property=+STCOU表示外部文件(POP01.txt)中的id属性位于STCOU列中。这用于匹配输入文件中的匹配id(如上所述,位于GEOID属性中)
  • -p population=+POP010210D,area=ALAND,state=+STATEFP,county=+COUNTYFP在输出文件中明确列出了我想要的属性,因此不包含任何额外的内容。 POP010210D是2010年人口普查时人口的列名,因此我只是将其用于演示目的。
  • cb_2014_us_county_20m.shp cb_2014_us_state_20m.shp是两个输入文件。一个用于县形状,一个用于状态形状。它们将以文件名命名的单独属性添加到输出文件中。

我是这样做的,因为您似乎根据人口密度为您的县区着色,因此人口和面积都需要在输出文件中。人口从POP01电子表格中提取,并根据GEOID(只是与县号连接的州号)链接到每个县。

我只是在寻找一种快速简便的方法来重新创建数据集,然后将状态边界添加到其中,以便我可以发布答案。不确定这与原始数据的匹配程度如何,但似乎可以用于演示目的。

从那以后,我把你的代码上面的内容更新为:

<!DOCTYPE html>
<meta charset="utf-8">
<style>

path {
  fill: none;
  stroke-linejoin: round;
  stroke-linecap: round;
}

path.state {
    fill: none;
    stroke: black;
    stroke-width: .5px;
}

</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script>

var width = 960,
    height = 600;

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

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

d3.json("counties_pa.json", function(error, us) {
  if (error) return console.error(error);

var color = d3.scale.threshold()
    .domain([1, 10, 50, 100, 500, 1000, 2000, 5000])
    .range(["#fff7ec", "#fee8c8", "#fdd49e", "#fdbb84", "#fc8d59", "#ef6548", "#d7301f", "#b30000", "#7f0000"]);

    svg.append('g')
        .attr('class','counties')
        .selectAll("path")
      .data(topojson.feature(us, us.objects.cb_2014_us_county_20m).features).enter()
        .append('path')
        .attr('d', path)
        .attr("id", function(d) { return "county-" + d.id; })
        .attr("data-state", function(d) { return d.properties.state; })
        .attr('style',function(d) { 
            return 'fill:'+color(d.properties.population / d.properties.area * 2.58999e6);
        })
        .on("mouseover", hoverCounty)
        .on("mouseout", outCounty);

    svg.append('g')
        .attr('class', 'states')
        .selectAll("path")
      .data(topojson.feature(us, us.objects.cb_2014_us_state_20m).features).enter()
        .append("path")
        .attr("class", "state")
        .attr("id", function(d) { return "state-" + d.id; })
        .attr("d", path);
    });

function hoverCounty(county) {
    d3.selectAll("path[data-state='" + county.properties.state + "']").style("opacity", .5);
}

function outCounty(county) {
    d3.select(".counties").selectAll("path").style("opacity", null);
}

</script>

新的和有趣的代码是:

  1. 为每个县添加data-state属性以确定它属于哪个州:

    .attr("data-state", function(d) { return d.properties.state; })
    
  2. 添加状态边界(我将状态组合到topojson命令行中的TopoJSON文件中)

    svg.append('g')
        .attr('class', 'states')
        .selectAll("path")
      .data(topojson.feature(us, us.objects.cb_2014_us_state_20m).features).enter()
        .append("path")
        .attr("class", "state")
        .attr("id", function(d) { return "state-" + d.id; })
        .attr("d", path);
    });
    
  3. 添加了悬停处理程序,以便您了解我如何确定将县分组为州:

    function hoverCounty(county) {
        d3.selectAll("path[data-state='" + county.properties.state + "']").style("opacity", .5);
    }
    
    function outCounty(county) {
        d3.select(".counties").selectAll("path").style("opacity", null);
    }
    
  4. 将这些悬停处理程序绑定到每个县,以便在适当的时间执行:

    .on("mouseover", hoverCounty)
    .on("mouseout", outCounty);