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

How can I nest GeoJSON / TopoJSON geometries OR nest the generated paths with D3?

问题:

我正在尝试创建一个交互式美国地图,其中显示了州、县和国界。县根据数据加阴影,将鼠标悬停在一个州上应该突出显示该州的所有县,并且该州应该是可点击的。我想通过一个 SVG 来实现这一点,该 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 应用于状态悬停上的县,这些县需要位于一个州形状内,或以某种方式分组。

我尝试过的:

我现在在做什么:

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

提前感谢您的帮助。

我对你的数据源下了赌注,这就是你想要实现的目标: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 列中。这用于匹配输入文件中的匹配 ids(如上所述,它们在 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 链接到每个县(这只是州编号与县编号相结合)。

我只是在寻找一种快速简便的方法来重新创建您的数据集,然后向其中添加州边界,这样我就可以 post 找到答案。不确定这与您的原始数据有多接近,但它似乎可以用于演示目的。

据此,我将上面的代码更新为:

<!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);