d3 - "не удается создать свойство 'vx' на номере '65'"


Поэтому я пытаюсь использовать этот замечательный пример силового Графа для некоторых очень простых json: https://raw.githubusercontent.com/DealPete/forceDirected/master/countries.json

Моя работа здесь: codepen

Я получаю бесконечный поток ошибок от d3 без ошибки в начале, чтобы предположить, что что-то не так с моим кодом. Она начинается так:

XHR finished loading: GET "https://raw.githubusercontent.com/DealPete/forceDirected/master/countries.json".
[...]
d3.min.js:2 Uncaught Error: missing: 0
    at ar (d3.min.js:2)
    at r (d3.min.js:5)
    at Function.e.links (d3.min.js:5)
    at pen.js:46
    at Object.<anonymous> (d3.min.js:7)
    at d.call (d3.min.js:4)
    at XMLHttpRequest.e (d3.min.js:7)
ar @ d3.min.js:2
r @ d3.min.js:5
e.links @ d3.min.js:5
(anonymous) @ pen.js:46
(anonymous) @ d3.min.js:7
call @ d3.min.js:4
e @ d3.min.js:7
d3.min.js:5 Uncaught TypeError: Cannot create property 'vx' on number '66'
    at e (d3.min.js:5)
    at d3.min.js:5
    at Fe.each (d3.min.js:5)
    at e (d3.min.js:5)
    at n (d3.min.js:5)
    at yn (d3.min.js:2)
    at gn (d3.min.js:2)
e @ d3.min.js:5
(anonymous) @ d3.min.js:5
each @ d3.min.js:5
e @ d3.min.js:5
n @ d3.min.js:5
yn @ d3.min.js:2
gn @ d3.min.js:2
d3.min.js:5 Uncaught TypeError: Cannot create property 'vx' on number '66'
    at e (d3.min.js:5)
    at d3.min.js:5
    at Fe.each (d3.min.js:5)
    at e (d3.min.js:5)
    at n (d3.min.js:5)
    at yn (d3.min.js:2)
    at gn (d3.min.js:2)
e @ d3.min.js:5
(anonymous) @ d3.min.js:5
each @ d3.min.js:5
e @ d3.min.js:5
n @ d3.min.js:5
yn @ d3.min.js:2
gn @ d3.min.js:2

На самом деле я не могу найти хороший вводный ресурс по графам силы в d3 v4+, поэтому я надо просто взломать его.

Html

<main>
  <section class="d3">
  </section>
</main>

Код

const api = 'https://raw.githubusercontent.com/DealPete/forceDirected/master/countries.json' 

let root = d3.select(".d3"),
    width = +root.attr("width"),
    height = +root.attr("height")

let svg = root.append('svg')
    .attr("width", width)
    .attr("height", height)

let color = d3.scaleOrdinal(d3.schemeCategory20);

let simulation = d3.forceSimulation()
    .force("link", d3.forceLink().id((d) => d.country))
    .force("charge", d3.forceManyBody())
    .force("center", d3.forceCenter(width / 2, height / 2));

d3.json(api, function(error, graph) {
  if (error) 
    throw error

  let link = svg.append("g")
    .attr("class", "links")
    .selectAll("line")
    .data(graph.links)
    .enter().append("line")
    .attr("stroke-width",  () => 4);

  let node = svg.append("g")
    .attr("class", "nodes")
    .selectAll("circle")
    .data(graph.nodes)
    .enter().append("circle")
    .attr("r", 5)
    .attr("fill", d => color(1))
    .call(d3.drag()
      .on("start", dragstarted)
      .on("drag", dragged)
      .on("end", dragended))

  simulation
    .nodes(graph.nodes)
    .on("tick", ticked)

  simulation.force("link")
    .links(graph.links)

  function ticked() {
    link
        .attr("x1", function(d) { return d.source.x; })
        .attr("y1", function(d) { return d.source.y; })
        .attr("x2", function(d) { return d.target.x; })
        .attr("y2", function(d) { return d.target.y; });

    node
        .attr("cx", function(d) { return d.x; })
        .attr("cy", function(d) { return d.y; });
  }
})

function dragstarted(d) {
  if (!d3.event.active) simulation.alphaTarget(0.3).restart();
  d.fx = d.x;
  d.fy = d.y;
}

function dragged(d) {
  d.fx = d3.event.x;
  d.fy = d3.event.y;
}

function dragended(d) {
  if (!d3.event.active) simulation.alphaTarget(0);
  d.fx = null;
  d.fy = null;
}
1 3

1 ответ:

Взгляните на свой массив links:

[
    { "target": 66, "source": 0 },
    { "target": 3, "source": 1 },
    { "target": 100, "source": 2 },
    ...
]
Теперь взгляните на вашу функцию id:
.id((d) => d.country)

Как вы можете видеть, в вашем массиве links нет country.

Поэтому, поскольку вы используете для ссылок числовой индекс , Просто отбросьте функцию id(). Согласно API:

Если указан id, устанавливает метод доступа node id в указанную функцию и возвращает эту силу. Если id не указан, возвращает текущий идентификатор узла доступа, по умолчанию используется числовой узел.индекс

Вот ваш рабочий код:

const api = 'https://raw.githubusercontent.com/DealPete/forceDirected/master/countries.json'

var width = 500,
  height = 500;

let svg = d3.select("body").append('svg')
  .attr("width", width)
  .attr("height", height)

let color = d3.scaleOrdinal(d3.schemeCategory20);

let simulation = d3.forceSimulation()
  .force("link", d3.forceLink())
  .force("charge", d3.forceManyBody())
  .force("center", d3.forceCenter(width / 2, height / 2));

d3.json(api, function(error, graph) {
  if (error)
    throw error

  let link = svg.append("g")
    .attr("class", "links")
    .selectAll("line")
    .data(graph.links)
    .enter().append("line")
    .attr("stroke", "black")
    .attr("stroke-width", 4);

  let node = svg.append("g")
    .attr("class", "nodes")
    .selectAll("circle")
    .data(graph.nodes)
    .enter().append("circle")
    .attr("r", 5)
    .attr("fill", d => color(1))
    .call(d3.drag()
      .on("start", dragstarted)
      .on("drag", dragged)
      .on("end", dragended))

  simulation
    .nodes(graph.nodes)
    .on("tick", ticked)

  simulation.force("link")
    .links(graph.links)

  function ticked() {
    link
      .attr("x1", function(d) {
        return d.source.x;
      })
      .attr("y1", function(d) {
        return d.source.y;
      })
      .attr("x2", function(d) {
        return d.target.x;
      })
      .attr("y2", function(d) {
        return d.target.y;
      });

    node
      .attr("cx", function(d) {
        return d.x;
      })
      .attr("cy", function(d) {
        return d.y;
      });
  }
})

function dragstarted(d) {
  if (!d3.event.active) simulation.alphaTarget(0.3).restart();
  d.fx = d.x;
  d.fy = d.y;
}

function dragged(d) {
  d.fx = d3.event.x;
  d.fy = d3.event.y;
}

function dragended(d) {
  if (!d3.event.active) simulation.alphaTarget(0);
  d.fx = null;
  d.fy = null;
}
<script src="https://d3js.org/d3.v4.min.js"></script>