D3 График не будет отображать динамически созданный JSON - PullRequest
0 голосов
/ 11 декабря 2018

У меня есть функция, которая при загрузке страницы, getVisualizer (), которая создает JSON-файл текущего пользователя и его друзей.Целью здесь является визуализация сети пользователей.Таким образом, конечным результатом должен быть текущий пользовательский узел, связанный с узлами, помеченными именами их друзей.

D3 здесь настроен на визуализацию графа, называемого «ссылками».У меня есть пример графика (с названием previous_links), который я смог отобразить, но в тот момент, когда я пытаюсь создать свой собственный JSON-файл под названием «links», он больше не рисует.Я в недоумении, так как проверил, что в файл объекта JSON добавляются правильные данные, поэтому я считаю, что это может быть проблема с форматированием.

Соответствующий код выглядит следующим образом.

 <!doctype html>
    <html lang="en">
      <head>
        <style>....

        </style>
      </head>

    <body>     
      <div id = "trythis" class="container main-section">
      //...basic layout items here...//


    <script>

    var numRequests = 0;

    var links = [];

    //PROBLEM SOMEWHERE IN HERE.--------

    function getVisualizer() {
        $.getJSON('/visualizer', function(data) {
          var n = Object.keys(data).length;
          if (n !== numRequests) {
            for (var key in data) {
              var value = data[key];
              var myObj = {
             "source" : "MyName",    //Test Username variable
             "target" : value       //friends name variable
            };
            //push the object to JSON array
            console.log("The object added is" + JSON.stringify(myObj));
            links.push( myObj );
            }
            numRequests = n;
          }
          console.log("Reached end of function");
        });
      };

      function refresh() {
        getVisualizer();

        //test that item 1 is correct
        console.log(JSON.stringify(links[0]) + "is links 0");
      };


        refresh();


    //PARTIAL ISSUE HERE. THIS GRAPH DRAWS SUCCESSFULLY WHEN RENAMED "links"
    //As you can see, I tried to create the same data structure 
    //above when loading friends and calling "push" for each object.

    //---------SAMPLE GRAPH THAT DRAWS--------------------

    var previous_links = [{
        "source": "Analytics",
        "target": "Science"
    }, {
        "source": "Analytics",
        "target": "Software"
    }, {
        "source": "Analytics",
        "target": "Story"
    }, {
        "source": "Science",
        "target": "Math"
    }, {
        "source": "Science",
        "target": "Statistics"
    }, {
        "source": "Software",
        "target": "R"
    }, {
        "source": "Software",
        "target": "SAS"
    }, {
        "source": "Software",
        "target": "Other"
    }, {
        "source": "Story",
        "target": "Business Communication"
    }, {
        "source": "Story",
        "target": "Visualization"
    }];

    //----------------------------------  

    //Here, I set up the graph for JSON named "links". Question is, how come
    //my newly created version will not draw?

    var nodes = {}

    // Compute the distinct nodes from the links.
    links.forEach(function (link) {
        link.source = nodes[link.source] || (nodes[link.source] = {
            name: link.source
        });
        link.target = nodes[link.target] || (nodes[link.target] = {
            name: link.target
        });
        link.value = +link.value;
    });

    var width = 900
    height = 300;

    var force = d3.layout.force()
        .nodes(d3.values(nodes))
        .links(links)
        .size([width, height])
        .linkDistance(50)
        .charge(-200)
        .on("tick", tick)
        .start();

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

    var link = svg.selectAll(".link")
        .data(force.links())
        .enter().append("line")
        .attr("class", "link");

    var node = svg.selectAll(".node")
        .data(force.nodes())
        .enter().append("g")
        .attr("class", "node")
        .on("mouseover", mouseover)
        .on("mouseout", mouseout)
        .on("click", click)
        .on("dblclick", dblclick)
        .call(force.drag);

    node.append("circle")
        .attr("r", 12)
        .style("fill", "#C71585");

    node.append("text")
        .attr("x", 14)
        .attr("dy", ".35em")
        .style("fill", "#333")
        .text(function (d) {
        return d.name;
    });

    function tick() {
        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("transform", function (d) {
            return "translate(" + d.x + "," + d.y + ")";
        });
    }

    function mouseover() {
        d3.select(this).select("circle").transition()
            .duration(750)
            .attr("r", 16);
    }

    function mouseout() {
        d3.select(this).select("circle").transition()
            .duration(750)
            .attr("r", 12);
    }
    // action to take on mouse click
    function click() {
        d3.select(this).select("text").transition()
            .duration(750)
            .attr("x", 22)
            .style("stroke-width", ".5px")
            .style("opacity", 1)
            .style("fill", "#E34A33")
            .style("font", "17.5px serif");
        d3.select(this).select("circle").transition()
            .duration(750)
            .style("fill", "#E34A33")
            .attr("r", 16)
    }

    // action to take on mouse double click
    function dblclick() {
        d3.select(this).select("circle").transition()
            .duration(750)
            .attr("r", 12)
            .style("fill", "#E34A33");
        d3.select(this).select("text").transition()
            .duration(750)
            .attr("x", 14)
            .style("stroke", "none")
            .style("fill", "#E34A33")
            .style("stroke", "none")
            .style("opacity", 0.6)
            .style("font", "14px serif");
    }





    </script>
    </div>
    </div>
    </div>
    </div>
    </div>

    </body>
    </html>

1 Ответ

0 голосов
/ 11 декабря 2018

Мне трудно проверить это без примера проекта, но я думаю, что вот что произошло:

К моменту запуска links.forEach(function (link) { значение links все еще имеет значение [],потому что function в $.getJSON('/visualizer', function(data) { выполняется асинхронно и, скорее всего, произойдет после выполнения остальной части вашего кода.

Попробуйте модульный код инициализации и вызов его после асинхронного кода.Примерно так:

<script>
  let numRequests = 0
  let links = []
  let nodes = {}
  let link
  let node

  let testData = [{
    "source": "Analytics",
    "target": "Science"
  }, {
    "source": "Analytics",
    "target": "Software"
  }, {
    "source": "Analytics",
    "target": "Story"
  }, {
    "source": "Science",
    "target": "Math"
  }, {
    "source": "Science",
    "target": "Statistics"
  }, {
    "source": "Software",
    "target": "R"
  }, {
    "source": "Software",
    "target": "SAS"
  }, {
    "source": "Software",
    "target": "Other"
  }, {
    "source": "Story",
    "target": "Business Communication"
  }, {
    "source": "Story",
    "target": "Visualization"
  }]


  const addIfUnique = (list, item) => list.indexOf(item) === -1 ? list.concat(item) : list
  const byName = (links, {source, target}) => addIfUnique(addIfUnique(links, source), target)
  const toNode = (name, i) => ({name, index: i})

  function addLinks(data) {
    let names = data.reduce(byName, [])
    const toLink = ({source, target}) => ({
      source: names.indexOf(source),
      target: names.indexOf(target)
    })
    nodes = names.map(toNode)
    links = data.map(toLink)
  }

  function tick() {
    link.attr("x1", d => d.source.x)
      .attr("y1", d => d.source.y)
      .attr("x2", d => d.target.x)
      .attr("y2", d => d.target.y)

    node.attr("transform", d => `translate(${d.x},${d.y})`)
  }

  function mouseover() {
    d3.select(this).select("circle").transition()
      .duration(750)
      .attr("r", 16)
  }

  function mouseout() {
    d3.select(this).select("circle").transition()
      .duration(750)
      .attr("r", 12)
  }

  function click() {
    d3.select(this).select("text").transition()
      .duration(750)
      .attr("x", 22)
      .style("stroke-width", ".5px")
      .style("opacity", 1)
      .style("fill", "#E34A33")
      .style("font", "17.5px serif")

    d3.select(this).select("circle").transition()
      .duration(750)
      .style("fill", "#E34A33")
      .attr("r", 16)
  }

  function dblclick() {
    d3.select(this).select("circle").transition()
      .duration(750)
      .attr("r", 12)
      .style("fill", "#E34A33")

    d3.select(this).select("text").transition()
      .duration(750)
      .attr("x", 14)
      .style("stroke", "none")
      .style("fill", "#E34A33")
      .style("stroke", "none")
      .style("opacity", 0.6)
      .style("font", "14px serif")
  }

  function drawGraph() {
    const width = 900
    const height = 300

    const force = d3.layout.force()
      .nodes(d3.values(nodes))
      .links(links)
      .size([width, height])
      .linkDistance(50)
      .charge(-200)
      .on("tick", tick)
      .start()

    const svg = d3.select("#trythis").append("svg")
      .attr("width", width)
      .attr("height", height)

    link = svg.selectAll(".link")
      .data(force.links())
      .enter().append("line")
      .attr("class", "link")

    node = svg.selectAll(".node")
      .data(force.nodes())
      .enter().append("g")
      .attr("class", "node")
      .on("mouseover", mouseover)
      .on("mouseout", mouseout)
      .on("click", click)
      .on("dblclick", dblclick)
      .call(force.drag)

    node.append("circle")
      .attr("r", 12)
      .style("fill", "#C71585")

    node.append("text")
      .attr("x", 14)
      .attr("dy", ".35em")
      .style("fill", "#333")
      .text(d => d.name)
  }

  function verifyItem1IsCorrect() {
    const item1 = JSON.stringify(links[0])
    console.log(`${item1} is links 0`)
  }

  function setUpGraph(data) {
    addLinks(data)
    drawGraph()
    verifyItem1IsCorrect()
  }

  // $.getJSON('/visualizer', setUpGraph)
  setUpGraph(testData)
</script>

РЕДАКТИРОВАТЬ : API D3.js v3 имеет очень специфическую разметку, необходимую для ссылок и узлов .

Массив узлов содержит объекты с простым целочисленным индексом и именем строки, а массив ссылок содержит объекты с атрибутами источника и назначения, но источник и цели должны быть либо:

  • целые числа, относящиеся к индексам узлов;или
  • ссылки на объекты на фактические узлы в массиве узлов.
...