Coder Social home page Coder Social logo

hololink-graph-overview's People

Contributors

eiffelfly avatar

Stargazers

 avatar  avatar

Watchers

 avatar

hololink-graph-overview's Issues

force diagram - how to overcome NER generate same basestone and stellar keyword on separated output

if key_title not in nodevalidator:
 if key_group == "basestone":
   d3node.append({"id":f"{key_title}", "level":"basestone", "connection":1})
   nodevalidator.append({f"{key_title}":"basestone"})
 elif key_group == "stellar":
    d3node.append({"id":f"{key_title}", "level":"stellar", "connection":1})
    nodevalidator.append({f"{key_title}":"stellar"})
else:
 for node in d3node:
    if node['id'] == key_title:
      if node['level'] == key_group:
        node['connection'] += 1

There will be some errors on force diagram

bug1

the reason is when we compute NER, the machine may recognize some keywords as basestone and stellar at separated output, so we need to adjust that afterward.

and then I noticed that the code have another fault!

if key_title not in nodevalidator: #because nodevalidator is now a list of dict so it will make some unexpected issue
 if key_group == "basestone":
   d3node.append({"id":f"{key_title}", "level":"basestone", "connection":1})
   nodevalidator.append({f"{key_title}":"basestone"})
 elif key_group == "stellar":
    d3node.append({"id":f"{key_title}", "level":"stellar", "connection":1})
    nodevalidator.append({f"{key_title}":"stellar"})
else:
 for node in d3node:
    if node['id'] == key_title:
      if node['level'] == key_group:
        node['connection'] += 1

so we now make nodevalidator to list of string and use other method to detect whether a keystone had been generated as stellar and basestone at same time.

if key_title not in nodevalidator: 
 if key_group == "basestone":
   d3node.append({"id":f"{key_title}", "level":"basestone", "connection":1})
   nodevalidator.append(f"{key_title}")
 elif key_group == "stellar":
    d3node.append({"id":f"{key_title}", "level":"stellar", "connection":1})
    nodevalidator.append(f"{key_title}")
else:
 for node in d3node:
    if node['id'] == key_title:
      if node['level'] == key_group:
        node['connection'] += 1
      else: #只要 basestone 和 stellar 重複出現,則都將其 level 改為 basestone
        node['level'] = 'basestone'
        node['connection'] += 1

now the diagram work as what we want

bug-solved

How to wrap long text in force diagram?

First of all, there is only a method provide by mbostock on internet, all solution surround that.
https://bl.ocks.org/mbostock/7555321

function wrap(text, width) {
  text.each(function() {
    var text = d3.select(this),
        words = text.text().split(/\s+/).reverse(), 
        word,
        line = [], 
        lineNumber = 0, 
        lineHeight = 1.1, // ems
        y = text.attr("y"),
        dy = parseFloat(text.attr("dy")),
        tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em"); 
    while (word = words.pop()) { 
      line.push(word);
      tspan.text(line.join(" ")); 
      if (tspan.node().getComputedTextLength() > width) { 
        line.pop(); 
        tspan.text(line.join(" "));
        line = [word];
        tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
      }
    }
  });
}

To speed the process of coding I try this solution too, and discover various problems.

  1. Only support English.
  2. Base on every English letter, which is not accurate.
  3. Need to be adjust when use force diagram

But still, thanks mbostock for providing this elegant code.


When I first implement this function this is what I got, Which definitely have some problem. And I discovered when you want to wrap chinese letter you have to customize it.

螢幕快照 2020-08-25 下午5 05 10

and I begin working on chinese version. First, I replace this line words = text.text().split(/\s+/).reverse(), with text.text().split('').reverse(), to fit chinese characteristic. Then the diagram become:

wrong-diagram_2

In the console I discovered that I didn't pass x and y variable correctly. And after dig inside the code I recognized that I misunderstood the whole logic of d3 force. For regular diagram such as bar chart, I believe the x and y parameter of every nodes are computed after you use data().enter() binding the data. But force diagram is different, the computing process begin when you call simulation on and ticking which is totally logical.

If you want to access the computed x, y and store them inside every nodes as a attribute, you need to initiate simulation first then selectAll and modify it.

From then on, I want to make sure I get the computed x, y parameter.

var nodeElements = g.append("g")
        .attr("class", "nodes")         
    .selectAll("circle")
        .data(graph.nodes)
        .enter()
        .append("circle")
        .attr("r", getNodeSize)
        .attr("fill", getNodeColor)
        .style("stroke", getLinkColor)
        .attr("x", function(d){ return d.x; })
        .attr("y", function(d){ return d.y; })
    .on('mouseover.fade', fade(0.2))
    .on('mouseout.fade', fade(1));

var textElements = g.append("g")
    .attr("class", "text")
        .selectAll("text")
            .data(graph.nodes)                   
        .enter()
        .filter(function(d){
            return d.connection >= 2 || d.level == 'article'
        })
            .append('text')
            .text(node => node.id)
            .attr('text-anchor', 'middle')
            .attr('font-family', "'Noto Sans TC', sans-serif")
            .attr('font-weight', getTextFontWeight)
            .attr('font-size', getTextFontSize)
            .attr("x", function(d){ return d.x; })
            .attr("y", function(d){ return d.y; })
            .style('fill', 'black')
            .each(wrap);

function wrap(d) {
    if (d.connection >= 2 || d.level == 'article'){
        var text = d3.select(this),
            width = getNodeSize(d),
            words = text.text().split("").reverse(),
            word,
            x = d.x
            y = d.y
            line = [],
            lineNumber = 0,
            lineHeight = 1.1,
            tspan = text.text(null).append("tspan").attr("x", x).attr("y", y);
            console.log(d, d.x, text.attr('x')) // <= this output interesting data

        while (word = words.pop()) {
            line.push(word);
            tspan.text(line.join(""));
            //console.log(word, tspan.node().getComputedTextLength(), x,y)
            
            if (tspan.node().getComputedTextLength() > width) {
                line.pop();
                tspan.text(line.join(""));
                line = [word];
                tspan = text.append("tspan").attr("x", x).attr("y", y).attr("dy", ++lineNumber * lineHeight + "em").text(word);
            }
        };                    
    }
    
};

螢幕快照 2020-08-26 下午3 28 24

d is node's array and is including the computed x, y for sure, but when you try to access d.x it become another data which is node attribute we put into it previously. What exactly is that and how we get the actual computed x,y.

What I had tried:

  1. Learned the data format you can load into function when use selectAll() & data()
function (d, i, nodes){
    d = node
    nodes = arrays contain all nodes
    i = index
    nodes[i] = d
}

螢幕快照 2020-08-27 下午4 04 30

  1. The process of force diagram

a. simulation: if node is empty then create an empty array.
b. simulation.node(): this method will arrange the position
c. so in theory I should get the x after sinulation.

  1. I should put it before selectAll to get x is wrong. The reason is simple, at that moment there is no force, just some nodes. So the nodes will arrange on a line, that is the reason I get a singular x in node array.

  2. some data format thought different process

var nodeElements = g.append("g")
        .attr("class", "nodes")         
    .selectAll("circle")
        .data(graph.nodes)
        .enter()
        .append("circle")
        .attr('test', testing)


function testing(d, i, nodes){
    node = d3.select(this)
    node_data = d3.select(this).datum()
    console.log(this, node, node_data ,d)
}

=> 1. ...with node data
=> 2. d3 selection format of node data
=> 3. node array
=> 4. node array

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.