Search code examples
jsonchartsvisualizationvega

Modifying the Width of a Symbol Mark in Vega


I want to label the symbols in my Vega graph. Some of the labels are long, extending beyond the boundaries of my symbols. How do I extend the width of a symbol to handle long labels?

Update: Is there a way of increasing the width of a symbol mark?

This is a link to my code.

N.B: I tried using rect marks, whose widths are easier to modify. But the aesthetics of the rect mark do not work for my use case. My use case is a Force Transform force-directed graph.


Solution

  • Here is a working example using your Vega spec with Force transform and text box using reactive geometry as suggested by David.

    View in Vega online editor

    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": 500,
      "height": 200,
      "autosize": "pad",
      "signals": [
        {
          "name": "nodeRadius",
          "value": 50,
          "bind": {"input": "range", "min": 1, "max": 100, "step": 1}
        },
        {
          "name": "nodeCharge",
          "value": -100,
          "bind": {"input": "range", "min": -100, "max": 10, "step": 1}
        },
        {
          "name": "linkDistance",
          "value": 66,
          "bind": {"input": "range", "min": 5, "max": 100, "step": 1}
        },
        {
          "name": "textPadding", 
          "value": 5,
          "bind": {"input": "range", "min": 0, "max": 20, "step": 1}
          },
        {
          "name": "cornerRadius", 
          "value": 10,
          "bind": {"input": "range", "min": 0, "max": 20, "step": 1}
        },
        { "name": "showSymbol", "value": false, "bind": {"input": "checkbox"} },
    
      ],
      "data": [
        {
          "name": "node_data",
          "values": [
            {"name": "A Name", "type": "node", "1d": 0},
            {"name": "A Very Long Name", "type": "node", "1d": 1}
          ]
        },
        {"name": "edge_data", "values": [{"source": 0, "target": 1}]}
      ],
      "marks": [
       {
          "type": "symbol",
          "name": "nodes",
          "from": {"data": "node_data"},
          "encode": {
            "update": {
              "fill": {"value": "grey"},
              "opacity": {"signal": "showSymbol ? 0.5 : 0"},
              "size": {"signal": "PI * nodeRadius * nodeRadius"},
              "shape": {"value": "circle"}
            }
          },
          "transform": [
            {
              "type": "force",
              "iterations": 300,
              "static": {"signal": "false"},
              "signal": "force",
              "forces": [
                {
                  "force": "center",
                  "x": {"signal": "width/2"},
                  "y": {"signal": "height/2"}
                },
                {"force": "collide", "radius": {"signal": "nodeRadius"}},
                {"force": "nbody", "strength": {"signal": "nodeCharge * 10"}},
                {
                  "force": "link",
                  "links": "edge_data",
                  "distance": {"signal": "linkDistance"}
                }
              ]
            }
          ]
        },
        {
          "type": "path",
          "from": {"data": "edge_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",
          "name": "textmark",
          "from": {"data": "nodes"},
          "interactive": false,
          
          "encode": {
            "enter": {"fill": {"value": "black"}, "fontSize": {"value": 16}},
            "update": {
              "y": {"field": "y", "offset": {"signal": "nodeRadius * -0.1"}},
              "x": {"field": "x"},
              "text": {"field": "datum.name"},
              "align": {"value": "center"},
              "baseline": {"value": "middle"}
            }
          },
           "zindex": 1
        },
        {
          "name": "rectmark",
          "type": "rect",
          "from": {"data": "textmark"},
          "encode": {
            "update": {
              "x": {"field": "bounds.x1", "round": true,  "offset": {"signal": "-textPadding"}},
              "x2": {"field": "bounds.x2", "round": true, "offset": {"signal": "textPadding"}},
              "y": {"field": "bounds.y1", "round": true, "offset": {"signal": "-textPadding"}},
              "y2": {"field": "bounds.y2", "round": true, "offset": {"signal": "textPadding"}},
              "cornerRadius": {"signal": "cornerRadius"},
              "fill": {"value": "aliceblue"},
              "stroke": {"value": "steelblue"}
            }
          }
        }
      ]
    }