如何使用 Vega / Vega-lite 渲染 class/org/flow/state chart/diagrams

How to render class/org/flow/state chart/diagrams with Vega / Vega-lite

我找不到 Vega 的 state/class/flow chart/org 图表示例。那里有吗?

感觉 Vega 非常适合这个(如果有点过分),但没有一个例子可以从它开始学习曲线相当陡峭。 "How Vega Works" 页面上有一些示例,但没有指向它们如何构建的链接:

还有 tree layout 示例,但不清楚如何开始将其转换为适合 flow-chart 样式图的块。

下面是一些所需的输出类型示例(加上其他形状,例如 diamonds/triangles),例如mermaid.js

您可以参考这个解决方案 - Working with trees 涵盖了

第 1 步 - 从表格数据中提取节点

步骤 2 - 从分层节点数据中提取链接

第 3 步 - 如何将它们组合在一起

第 4 步 - 添加标签

第 5 步 - 添加颜色

假设您可以如下表示您的图表:

"values": [
        {"id": "1", "parent": null, "title": "Animal"},
        {"id": "2", "parent": "1", "title": "Duck"},
        {"id": "3", "parent": "1", "title": "Fish"},
        {"id": "4", "parent": "1", "title": "Zebra"}
      ]

然后您可以将节点布置成 tree-like 形状(stratify 完成工作):

"transform": [
        {
          "type": "stratify",
          "key": "id",
          "parentKey": "parent"
        },
        {
          "type": "tree",
          "method": "tidy",
          "separation": true,
          "size": [{"signal": "width"}, {"signal": "height"}]
        }
      ]

布置完节点后,您需要生成连接线,treelinks + linkpath 组合正是这样做的:

{
      "name": "links",
      "source": "tree", // take datasource "tree" as input
      "transform": [
        { "type": "treelinks" }, // apply transform 1
        { "type": "linkpath", // follow up with next transform
          "shape": "diagonal"
          }
      ]
    }

现在您已经有了数据源,您想要绘制实际的 objects。在 Vega 中,这些被称为 marks。我想这就是我要偏离你想要的输出的地方,因为我只画了一个矩形,每个数据点都有一个标题和一些要连接的基本线:

"marks": [
    {
      "type": "path",
      "from": {"data": "links"}, // dataset we defined above
      "encode": {
        "enter": {
          "path": {"field": "path"} // linkpath generated a dataset with "path" field in it - we just grab it here
        }
      }
    },
    {
      "type": "rect",
      "from": {"data": "tree"},
      "encode": {
        "enter": {
          "stroke": {"value": "black"},
          "width": {"value": 100},
          "height": {"value": 20},
          "x": {"field": "x"},
          "y": {"field": "y"}
        }
      }
    },
    {
      "type": "text",
      "from": {"data": "tree"}, // use data set we defined earlier
      "encode": {
        "enter": {
          "stroke": {"value": "black"},
          "text": {"field": "title"}, // we can use data fields to display actual values
          "x": {"field": "x"}, // use data fields to draw values from
          "y": {"field": "y"},
          "dx": {"value":50}, // offset the mark to appear in rectangle center
          "dy": {"value":13},
          "align": {"value": "center"}
        }
      }
    }
  ]

总而言之,我 arrived at a very basic approximation of your target state. It's definitely not an exact match: the rectangles there should probably be replaced with groups 和连接路径也需要一些工作。您会注意到我没有使用任何 signals 来提供动态用户输入和 update/exit/hover 指令 - 同样,为了简单起见。

{
  "$schema": "https://vega.github.io/schema/vega/v5.json",
  "width": 800,
  "height": 300,
  "padding": 5,

  "data": [
    {
      "name": "tree",
      "values": [
        {"id": "1", "parent": null, "title": "Animal"},
        {"id": "2", "parent": "1", "title": "Duck"},
        {"id": "3", "parent": "1", "title": "Fish"},
        {"id": "4", "parent": "1", "title": "Zebra"}
      ],
      "transform": [
        {
          "type": "stratify",
          "key": "id",
          "parentKey": "parent"
        },
        {
          "type": "tree",
          "method": "tidy",
          "separation": true,
          "size": [{"signal": "width"}, {"signal": "height"}]
        }
      ]      
    },
    {
      "name": "links",
      "source": "tree",
      "transform": [
        { "type": "treelinks" },
        { "type": "linkpath",
          "shape": "diagonal"
          }
      ]
    }, 
    {
      "name": "tree-boxes",
      "source": "tree",
      "transform": [
          { 
            "type": "filter",
            "expr": "datum.parent == null"
          }
        ]
    },
    {
      "name": "tree-circles",
      "source": "tree",
      "transform": [
        {
          "type": "filter",
          "expr": "datum.parent != null"
        }
      ]
    }
  ],
  "marks": [
    {
      "type": "path",
      "from": {"data": "links"},
      "encode": {
        "enter": {
          "path": {"field": "path"}
        }
      }
    },
    {
      "type": "rect",
      "from": {"data": "tree-boxes"},
      "encode": {
        "enter": {
          "stroke": {"value": "black"},
          "width": {"value": 100},
          "height": {"value": 20},
          "x": {"field": "x"},
          "y": {"field": "y"}
        }
      }
    },
    {
      "type": "symbol",
      "from": {"data": "tree-circles"},
      "encode": {
        "enter": {
          "stroke": {"value": "black"},
          "width": {"value": 100},
          "height": {"value": 20},
          "x": {"field": "x"},
          "y": {"field": "y"}
        }
      }
    },
    {
      "type": "rect",
      "from": {"data": "tree"},
      "encode": {
        "enter": {
          "stroke": {"value": "black"},
          "width": {"value": 100},
          "height": {"value": 20},
          "x": {"field": "x"},
          "y": {"field": "y"}
        }
      }
    },
    {
      "type": "text",
      "from": {"data": "tree"},
      "encode": {
        "enter": {
          "stroke": {"value": "black"},
          "text": {"field": "title"},
          "x": {"field": "x"},
          "y": {"field": "y"},
          "dx": {"value":50},
          "dy": {"value":13},
          "align": {"value": "center"}
        }
      }
    }
  ]
}

UPD:假设您想为图表的根节点和叶节点呈现不同的形状。 实现此目的的一种方法是根据您的 tree 数据集添加两个 filter 转换并相应地过滤它们:

    {
      "name": "tree-boxes",
      "source": "tree", // grab the existing data
      "transform": [
          { 
            "type": "filter",
            "expr": "datum.parent == null" // run it through a filter defined by expression
          }
        ]
    },
    {
      "name": "tree-circles",
      "source": "tree",
      "transform": [
        {
          "type": "filter",
          "expr": "datum.parent != null"
        }
      ]
    }

然后,不是将所有标记呈现为 rect,而是希望为各个转换后的数据集提供两种不同的形状:

{
      "type": "rect",
      "from": {"data": "tree-boxes"},
      "encode": {
        "enter": {
          "stroke": {"value": "black"},
          "width": {"value": 100},
          "height": {"value": 20},
          "x": {"field": "x"},
          "y": {"field": "y"}
        }
      }
    },
    {
      "type": "symbol",
      "from": {"data": "tree-circles"},
      "encode": {
        "enter": {
          "stroke": {"value": "black"},
          "width": {"value": 100},
          "height": {"value": 20},
          "x": {"field": "x"},
          "y": {"field": "y"}
        }
      }
    }

我构建了一个示例,该示例迄今为止最接近该问题中描述的内容。感谢@timur,我的解决方案基于此处接受的答案。

Click here 在 Vega 编辑器中查看它。

它将树节点显示为包含多个文本的组。它支持节点的展开和折叠以及水平和垂直布局之间的切换(您可以通过设置horizontal信号的默认值来控制)。

不过我遇到了一些限制:

  • 在水平和垂直布局之间切换不会正确地重新呈现所有标记(提出了一个问题 here)。仅当您在代码
  • 中手动更改 horizontal 信号的默认值时才有效
  • 如果不手动折叠嵌套节点,我找不到正确折叠树的根节点的方法(发布了相关问题 here

无论如何,它应该对任何正在寻找一种方法来使用 Vega 构建组织结构图可视化的人有用 - 没有更接近的例子,我不得不花很多时间来弄清楚所有的注意事项并解决几乎所有的问题.