【问题标题】:Access topojson object to change map country properties in d3.js访问 topojson 对象以更改 d3.js 中的地图国家属性
【发布时间】:2019-03-08 06:20:27
【问题描述】:

我正在构建一个 Web 应用程序来显示世界各国之间的不同趋势和统计数据。使用 d3,我可以加载 topojson 文件并投影世界地图。

  var countryStatistics = [];
  var pathList = [];

function visualize(){

var margin = {top: 100, left: 100, right: 100, bottom:100},
    height = 800 - margin.top - margin.bottom, 
    width = 1200 - margin.left - margin.right;

 //create svg
var svg = d3.select("#map")
      .append("svg")
      .attr("height", height + margin.top + margin.bottom)
      .attr("width", width + margin.left + margin.right)
      .append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  //load topojson file
d3.queue()
  .defer(d3.json, "world110m.json")
  .await(ready)  

var projection = d3.geoMercator()
  .translate([ width / 2, height / 2 ])
  .scale(180)

  //pass path lines to projection
var path = d3.geoPath()
.projection(projection);

function ready (error, data){
  console.log(data);

    //we pull the countries data out of the loaded json object
  countries = topojson.feature(data, data.objects.countries).features

    //select the country data, draw the lines, call mouseover event to change fill color
  svg.selectAll(".country")
    .data(countries)
    .enter().append("path")
    .attr("class", "country")
    .attr("d", path)
    .on('mouseover', function(d) {
      d3.select(this).classed("hovered", true)
        //this function matches the id property in topojson country, to an id in (see below)
      let country = matchPath(this.__data__.id);
      console.log(country)
    })
    .on('mouseout', function(d) {
      d3.select(this).classed("hovered", false)
    })
      //here I push the country data into a global array just to have access and experimentalism. 
    for (var i = 0; i < countries.length; i++) {
      pathList.push(countries[i]);
    } 
  }
};

matchPath() 函数用于允许我将路径数据匹配到 countryStatistics 以在某个国家/地区被鼠标悬停时显示。

function matchPath(pathId){
    //to see id property of country currently being hovered over
  console.log("pathID:" + pathId)
    //loop through all countryStatistics and return the country with matching id number
  for(var i = 0; i < countryStatistics.length; i++){
    if(pathId == countryStatistics[i].idTopo){
      return countryStatistics[i];
    }
  }
}

问题:这是可行的,但仅限于一个方向。我可以从每个 topojson 路径获取我的统计数据……但我无法根据数据获取和操作各个路径。

我想要做的是有一个按钮,可以从 countryStatistics 中选择某个属性,并根据数据值构建域/范围比例并设置颜色渐变。我坚持的步骤是获取统计数据和路径数据接口。

我看到了两种可能的解决方案,

1:有一种方法可以在渲染过程中将拓扑路径数据连接到统计数据,我可以调用一个函数来重绘sgv...

2:我构建了一个包含所有路径数据和统计数据的新对象。在这种情况下,我可以只提取 topojson.objects.countries 数据而忽略其余数据吗?

我应该如何做到这一点?任何指针,下一步将不胜感激。

(我在这个项目中所处的位置...http://conspiracytime.com/globeApp

【问题讨论】:

    标签: javascript d3.js topojson


    【解决方案1】:

    TopoJSON 是一个非常强大的工具。它有自己的 CLI(命令行界面)来生成您自己的 TopoJSON 文件。

    该 CLI 允许您创建一个包含拓扑和要合并的数据的唯一文件。

    即使它在 v3.0.2 中,first versión 对我来说也很清晰。这是一个示例,说明如何通过公共id 属性将csv 文件与json 合并。

    # Simplified versión from https://bl.ocks.org/luissevillano/c7690adccf39bafe583f72b044e407e8
    # note is using TopoJSON CLI v1
    topojson \
      -e data.csv \
      --id-property CUSEC,cod \
      -p population=+t1_1,area=+Shape_area \ 
      -o cv.json \
      -- cv/shapes/census.json
    

    有一个带有cod 列的data.csv 文件和一个带有名为CUSEC 的属性的census.json 文件。 - 使用--id-property,您可以指定将在合并中使用的属性。 - 使用属性-p,您可以动态创建新属性

    这是一个可靠的解决方案,您可以使用一个唯一的文件(带有一个唯一的请求)来处理整个数据。这种最佳方案并不总是可行的,因此下一个解决方案可能是另一种解决方案。

    回到JavaScript,您可以通过以下方式创建一个可通过通用属性访问的新变量。让你的数据格式:

    // countryStatistics
    {
      "idTopo": "004",
      "country": "Afghanistan",
      "countryCode": "afg",
      // ..
    },
    

    还有你的 TopoJSON 文件结构:

    {"type":"Polygon","arcs":[[0,1,2,3,4,5]],"id":"004"},
    {"type":"MultiPolygon","arcs":[[[6,7,8,9]],[[10,11,12]]],"id":"024"} // ...
    
    
    
    

    针对这种情况的一个常见解决方案是创建一个 Array 变量,该变量可由 idTopo 访问:

    var dataById = [];
    countryStatistics.forEach(function(d) {
        dataById[d.idTopo] = d;
    });
    

    然后,该变量将具有下一个结构:

    [
        004:{
          "idTopo": "004",
          "country": "Afghanistan",
          "countryCode": "afg",
          //...
      },
        008: {
          //...
        }
    ]
    

    从这里,您可以通过其idTopo 属性访问属性,如下所示:

    dataById['004'] // {"idTopo":"004","country":"Afghanistan","countryCode":"afg" ...}
    

    您可以决定遍历 Topo 数据并将这些属性添加到每个特征:

    var countries = topojson
      .feature(data, data.objects.countries)
      .features.map(function(d) {
        d.properties = dataById[d.id];
        return d
      });
    

    或者在你需要的时候使用这个数组

    // ...
    .on("mouseover", function(d) {
      d3.select(this).classed("hovered", true);
      console.log(dataById[d.id]);
    });
    

    【讨论】:

    • 这似乎是朝着正确方向迈出的一大步。我现在在我的 topojson 对象中绑定了 countryStatistics,并且可以在渲染期间调用它们。 dataById 被键入到它自己的 idTopo 属性,然后在初始 topojson 加载期间合并为属性对象。杰出的!!现在以可视方式绑定数据。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多