Доступ к объекту topojson для изменения свойств страны карты в d3.js - PullRequest
0 голосов
/ 08 марта 2019

Я создаю веб-приложение для отображения различных тенденций и статистики между странами мира. С 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)

1 Ответ

1 голос
/ 08 марта 2019

TopoJSON - действительно мощный инструмент. Он имеет свой собственный CLI ( интерфейс командной строки ) для генерации ваших собственных файлов TopoJSON.

Этот CLI позволяет вам создать уникальный файл с топологией и данными, которые вы хотите объединить.

Несмотря на то, что версия v3.0.2 , первая версия выглядит для меня ясной. Это пример того, как вы можете объединить файл csv с json через общий атрибут id.

# 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

Существует файл data.csv со столбцом cod и файл census.json со свойством с именем CUSEC. - Используя --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]);
});
...