DataWrangler1980
DataWrangler1980

Reputation: 565

Vega Graph Network with Labels on the lines / edges

I have this sample Vega specification, however the labels that are supposed to be plotted alongside the lines (Edges) are instead printed on the top left corner of the canvas?

How can I change the specification to resolve this issue? You will see on the image below there are some labels printed on the top left of the canvas.

Instead it should print those labels near the actual lines / edges on the report.

enter image description here

Here is the specification:

{
  "$schema": "https://vega.github.io/schema/vega/v5.json",
  "description": "A node-link diagram with force-directed layout, depicting character co-occurrence in the novel Les Misérables.",
  "width": 700,
  "height": 500,
  "padding": 0,
  "autosize": "none",

  "signals": [
    { "name": "cx", "update": "width / 6" },
    { "name": "cy", "update": "height / 6" },
    { "name": "nodeRadius", "value": 8,
      "bind": {"input": "range", "min": 1, "max": 50, "step": 1} },
    { "name": "nodeCharge", "value": -30,
      "bind": {"input": "range", "min":-100, "max": 10, "step": 1} },
    { "name": "linkDistance", "value": 30,
      "bind": {"input": "range", "min": 5, "max": 100, "step": 1} },
    { "name": "static", "value": true,
      "bind": {"input": "checkbox"} },
    {
      "description": "State variable for active node fix status.",
      "name": "fix", "value": false,
      "on": [
        {
          "events": "symbol:mouseout[!event.buttons], window:mouseup",
          "update": "false"
        },
        {
          "events": "symbol:mouseover",
          "update": "fix || true"
        },
        {
          "events": "[symbol:mousedown, window:mouseup] > window:mousemove!",
          "update": "xy()",
          "force": true
        }
      ]
    },
    {
      "description": "Graph node most recently interacted with.",
      "name": "node", "value": null,
      "on": [
        {
          "events": "symbol:mouseover",
          "update": "fix === true ? item() : node"
        }
      ]
    },
    {
      "description": "Flag to restart Force simulation upon data changes.",
      "name": "restart", "value": false,
      "on": [
        {"events": {"signal": "fix"}, "update": "fix && fix.length"}
      ]
    }
  ],

  "data": [
    {
      "name": "node-data",
      "values": [
        {"name": "AAA", "group": 1, "index": 0},
        {"name": "BBB", "group": 2, "index": 1},
        {"name": "CCC", "group": 3, "index": 2},
        {"name": "DDD", "group": 4, "index": 3},
        {"name": "EEE", "group": 5, "index": 4}
        
        
      ]
    },
    {
      "name": "link-data",
      "values": [
        {"source": 0, "target": 1, "name": "Contract ABC"},
        {"source": 1, "target": 2, "name": "Contract XYZ"},
        
        {"source": 2, "target": 3, "name": "Contract AAA"},
        
        {"source": 4, "target": 3, "name": "Contract BBB"}          
        
        
      ]
    }
  ],

  "scales": [
    {
      "name": "color",
      "type": "ordinal",
      "domain": {"data": "node-data", "field": "group"},
      "range": {"scheme": "category20c"}
    }
  ],

  "marks": [
    {
      "name": "nodes",
      "type": "symbol",
      "zindex": 1,

      "from": {"data": "node-data"},
      "on": [
        {
          "trigger": "fix",
          "modify": "node",
          "values": "fix === true ? {fx: node.x, fy: node.y} : {fx: fix[0], fy: fix[1]}"
        },
        {
          "trigger": "!fix",
          "modify": "node", "values": "{fx: null, fy: null}"
        }
      ],

      "encode": {
        "enter": {
          "fill": {"scale": "color", "field": "group"},
          "stroke": {"value": "white"}
        },
        "update": {
          "size": {"signal": "2 * nodeRadius * nodeRadius"},
          "cursor": {"value": "pointer"}
        }
      },

      "transform": [
        {
          "type": "force",
          "iterations": 300,
          "restart": {"signal": "restart"},
          "static": {"signal": "static"},
          "signal": "force",
          "forces": [
            {"force": "center", "x": {"signal": "cx"}, "y": {"signal": "cy"}},
            {"force": "collide", "radius": {"signal": "nodeRadius"}},
            {"force": "nbody", "strength": {"signal": "nodeCharge"}},
            {"force": "link", "links": "link-data", "distance": {"signal": "linkDistance"}}
          ]
        }
      ]
    },
    {
      "type": "path",
      "from": {"data": "link-data"},
      "interactive": false,
      "encode": {
        "update": {
          "stroke": {"value": "#ccc"},
          "strokeWidth": {"value": 0.5}
        }
      },
      "transform": [
        {
          "type": "linkpath",
          "require": {"signal": "force"},
          "shape": "line",
          "sourceX": "datum.source.x", "sourceY": "datum.source.y",
          "targetX": "datum.target.x", "targetY": "datum.target.y"
        }
      ]
    },
    
    {
      "type": "text",      "from": {"data": "link-data"},
      "interactive": false,
      "encode": {
        "enter": {
          "x": {"signal": "(datum.source.x + datum.target.x) / 2"},
          "y": {"signal": "(datum.source.y + datum.target.y) / 2"},
          "text": {"field": "name"},
          "fontSize": {"value": 14},
          "align": {"value": "center"},
          "baseline": {"value": "middle"},
          "fill": {"value": "black"}
        }
      }
    }
  ]
}

Upvotes: 0

Views: 361

Answers (1)

davidebacci
davidebacci

Reputation: 30324

Here you go.

enter image description here

{
  "$schema": "https://vega.github.io/schema/vega/v5.json",
  "description": "A node-link diagram with force-directed layout, depicting character co-occurrence in the novel Les Misérables.",
  "width": 700,
  "height": 500,
  "padding": 0,
  "autosize": "none",
  "signals": [
    {"name": "cx", "update": "width / 6"},
    {"name": "cy", "update": "height / 6"},
    {
      "name": "nodeRadius",
      "value": 8,
      "bind": {"input": "range", "min": 1, "max": 50, "step": 1}
    },
    {
      "name": "nodeCharge",
      "value": -30,
      "bind": {"input": "range", "min": -100, "max": 10, "step": 1}
    },
    {
      "name": "linkDistance",
      "value": 30,
      "bind": {"input": "range", "min": 5, "max": 100, "step": 1}
    },
    {"name": "static", "value": true, "bind": {"input": "checkbox"}},
    {
      "description": "State variable for active node fix status.",
      "name": "fix",
      "value": false,
      "on": [
        {
          "events": "symbol:mouseout[!event.buttons], window:mouseup",
          "update": "false"
        },
        {"events": "symbol:mouseover", "update": "fix || true"},
        {
          "events": "[symbol:mousedown, window:mouseup] > window:mousemove!",
          "update": "xy()",
          "force": true
        }
      ]
    },
    {
      "description": "Graph node most recently interacted with.",
      "name": "node",
      "value": null,
      "on": [
        {"events": "symbol:mouseover", "update": "fix === true ? item() : node"}
      ]
    },
    {
      "description": "Flag to restart Force simulation upon data changes.",
      "name": "restart",
      "value": false,
      "on": [{"events": {"signal": "fix"}, "update": "fix && fix.length"}]
    }
  ],
  "data": [
    {
      "name": "node-data",
      "values": [
        {"name": "AAA", "group": 1, "index": 0},
        {"name": "BBB", "group": 2, "index": 1},
        {"name": "CCC", "group": 3, "index": 2},
        {"name": "DDD", "group": 4, "index": 3},
        {"name": "EEE", "group": 5, "index": 4}
      ]
    },
    {
      "name": "link-data",
      "values": [
        {"source": 0, "target": 1, "name": "Contract ABC"},
        {"source": 1, "target": 2, "name": "Contract XYZ"},
        {"source": 2, "target": 3, "name": "Contract AAA"},
        {"source": 4, "target": 3, "name": "Contract BBB"}
      ]
    }
  ],
  "scales": [
    {
      "name": "color",
      "type": "ordinal",
      "domain": {"data": "node-data", "field": "group"},
      "range": {"scheme": "category20c"}
    }
  ],
  "marks": [
    {
      "name": "nodes",
      "type": "symbol",
      "zindex": 1,
      "from": {"data": "node-data"},
      "on": [
        {
          "trigger": "fix",
          "modify": "node",
          "values": "fix === true ? {fx: node.x, fy: node.y} : {fx: fix[0], fy: fix[1]}"
        },
        {"trigger": "!fix", "modify": "node", "values": "{fx: null, fy: null}"}
      ],
      "encode": {
        "enter": {
          "fill": {"scale": "color", "field": "group"},
          "stroke": {"value": "white"}
        },
        "update": {
          "size": {"signal": "2 * nodeRadius * nodeRadius"},
          "cursor": {"value": "pointer"},
          "tooltip": {"signal": "item"}
        }
      },
      "transform": [
        {
          "type": "force",
          "iterations": 300,
          "restart": {"signal": "restart"},
          "static": {"signal": "static"},
          "signal": "force",
          "forces": [
            {"force": "center", "x": {"signal": "cx"}, "y": {"signal": "cy"}},
            {"force": "collide", "radius": {"signal": "nodeRadius"}},
            {"force": "nbody", "strength": {"signal": "nodeCharge"}},
            {
              "force": "link",
              "links": "link-data",
              "distance": {"signal": "linkDistance"}
            }
          ]
        }
      ]
    },
    {
      "type": "path",
      "name": "theLinks",
      "from": {"data": "link-data"},
      "interactive": true,
      "encode": {
        "update": {
          "stroke": {"value": "#ccc"},
          "strokeWidth": {"value": 3},
          "tooltip": {"signal": "item"}
        }
      },
      "transform": [
        {
          "type": "linkpath",
          "require": {"signal": "force"},
          "shape": "line",
          "sourceX": "datum.source.x",
          "sourceY": "datum.source.y",
          "targetX": "datum.target.x",
          "targetY": "datum.target.y"
        }
      ]
    },
    {
      "type": "text",
      "from": {"data": "theLinks"},
      "interactive": false,
      "encode": {
        "update": {
          "x": {
            "signal": "((datum.bounds.x2-datum.bounds.x1)/2)+datum.bounds.x1"
          },
          "y": {
            "signal": "((datum.bounds.y2-datum.bounds.y1)/2)+datum.bounds.y1"
          },
          "text": {"signal": "datum.datum.name"},
          "fontSize": {"value": 14},
          "align": {"value": "center"},
          "baseline": {"value": "middle"},
          "fill": {"value": "black"}
        }
      }
    }
  ]
}

Upvotes: 1

Related Questions