datahub 中血缘图的实现分析,在react中使用airbnb的visx可视化库来画有向无环图

news2024/9/23 3:26:48

背景

做大数据的项目,必不可少的是要接触到数据血缘图,它在大数据项目中有着很重要的作用。
之前在公司也做过一些案例,也看过很多友商的产品,阿里的DataWork,领英的Datahub,
datawork的血缘图使用的是 G6,自家的产品
Datahub使用的是 爱彼邻的 可视化库 visx
本篇文章就来谈谈datahub中的血缘图。

查看源码

点击此处链接你将看到 datahub中的血缘图,
在这里插入图片描述
由于是demo环境,数据有可能会被删掉,读者可以自行寻找。

该血缘图的特性如下

  • 上下游
  • 自定义节点
  • 节点可点击,操作
  • 线的样式有多种
  • 鼠标放置线上有辅助信息
  • 可以展开上下游
  • 最基本的放大,缩小视图

F12 节点的源码,发现使用的是SVG 实现的
在这里插入图片描述
标签的类前缀都是vx,但直接搜没有搜到,于是去项目的package.json中寻找使用的库。

查看package.json

在项目中 找到了答案
https://github.com/datahub-project/datahub/blob/master/datahub-web-react/package.json
在这里插入图片描述

使用的是
https://airbnb.io/visx
在这里插入图片描述
github 地址 https://github.com/airbnb/visx
visx 是一个为 React 应用程序提供可视化功能的库。它提供了一系列低级可视化元素或组件,被称为 expressive, low-level visualization primitives,这些元素或组件可以用于创建各种可视化效果,例如饼图等。使用 VISX 可以方便地将设计元素添加到 React 应用程序中。它是由 Airbnb 构建的。

提前关键词,该库具有的特征

  • 为react
  • 低级元素
  • 可视化

低级元素是说它不直接提供一个个完整的图表,而且要使用多个元素组装实现,这也意味着 要使用它,还是有一点门槛的,但人家的审美确实在线。visx的gallery 都很美观。让人看了就像用,但一用就头大,提供的api太底层了。

大家看几个官网的示例
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

查看组件源码

上面介绍了一下 visx库,我们回到datahub这个项目
血缘图 都放在https://github.com/datahub-project/datahub/blob/master/datahub-web-react/src/app/lineage这个目录

节点组件
https://github.com/datahub-project/datahub/blob/master/datahub-web-react/src/app/lineage/LineageEntityNode.tsx

visx库文档

因为这个库并不是一个专业的Graph库,所有在图的布局算法,自定义接的,自定义线,或者图的交互 都不如g6做的丰富。
选型还需慎重,依赖大量svg的api,标签。

案例

最后提供一个 使用visx 画的一个 Graph案例
在这里插入图片描述

import React, { useState } from 'react';
import { Group } from '@visx/group';
import { hierarchy, Tree } from '@visx/hierarchy';
import { LinearGradient } from '@visx/gradient';
import { pointRadial } from 'd3-shape';
import useForceUpdate from './useForceUpdate';
import LinkControls from './LinkControls';
import getLinkComponent from './getLinkComponent';

interface TreeNode {
  name: string;
  isExpanded?: boolean;
  children?: TreeNode[];
}

const data: TreeNode = {
  name: 'T',
  children: [
    {
      name: 'A',
      children: [
        { name: 'A1' },
        { name: 'A2' },
        { name: 'A3' },
        {
          name: 'C',
          children: [
            {
              name: 'C1',
            },
            {
              name: 'D',
              children: [
                {
                  name: 'D1',
                },
                {
                  name: 'D2',
                },
                {
                  name: 'D3',
                },
              ],
            },
          ],
        },
      ],
    },
    { name: 'Z' },
    {
      name: 'B',
      children: [{ name: 'B1' }, { name: 'B2' }, { name: 'B3' }],
    },
  ],
};

const defaultMargin = { top: 30, left: 30, right: 30, bottom: 70 };

export type LinkTypesProps = {
  width: number;
  height: number;
  margin?: { top: number; right: number; bottom: number; left: number };
};

export default function Example({
  width: totalWidth,
  height: totalHeight,
  margin = defaultMargin,
}: LinkTypesProps) {
  const [layout, setLayout] = useState<string>('cartesian');
  const [orientation, setOrientation] = useState<string>('horizontal');
  const [linkType, setLinkType] = useState<string>('diagonal');
  const [stepPercent, setStepPercent] = useState<number>(0.5);
  const forceUpdate = useForceUpdate();

  const innerWidth = totalWidth - margin.left - margin.right;
  const innerHeight = totalHeight - margin.top - margin.bottom;

  let origin: { x: number; y: number };
  let sizeWidth: number;
  let sizeHeight: number;

  if (layout === 'polar') {
    origin = {
      x: innerWidth / 2,
      y: innerHeight / 2,
    };
    sizeWidth = 2 * Math.PI;
    sizeHeight = Math.min(innerWidth, innerHeight) / 2;
  } else {
    origin = { x: 0, y: 0 };
    if (orientation === 'vertical') {
      sizeWidth = innerWidth;
      sizeHeight = innerHeight;
    } else {
      sizeWidth = innerHeight;
      sizeHeight = innerWidth;
    }
  }

  const LinkComponent = getLinkComponent({ layout, linkType, orientation });

  return totalWidth < 10 ? null : (
    <div>
      <LinkControls
        layout={layout}
        orientation={orientation}
        linkType={linkType}
        stepPercent={stepPercent}
        setLayout={setLayout}
        setOrientation={setOrientation}
        setLinkType={setLinkType}
        setStepPercent={setStepPercent}
      />
      <svg width={totalWidth} height={totalHeight}>
        <LinearGradient id="links-gradient" from="#fd9b93" to="#fe6e9e" />
        <rect width={totalWidth} height={totalHeight} rx={14} fill="#272b4d" />
        <Group top={margin.top} left={margin.left}>
          <Tree
            root={hierarchy(data, (d) => (d.isExpanded ? null : d.children))}
            size={[sizeWidth, sizeHeight]}
            separation={(a, b) => (a.parent === b.parent ? 1 : 0.5) / a.depth}
          >
            {(tree) => (
              <Group top={origin.y} left={origin.x}>
                {tree.links().map((link, i) => (
                  <LinkComponent
                    key={i}
                    data={link}
                    percent={stepPercent}
                    stroke="rgb(254,110,158,0.6)"
                    strokeWidth="1"
                    fill="none"
                  />
                ))}

                {tree.descendants().map((node, key) => {
                  const width = 40;
                  const height = 20;

                  let top: number;
                  let left: number;
                  if (layout === 'polar') {
                    const [radialX, radialY] = pointRadial(node.x, node.y);
                    top = radialY;
                    left = radialX;
                  } else if (orientation === 'vertical') {
                    top = node.y;
                    left = node.x;
                  } else {
                    top = node.x;
                    left = node.y;
                  }

                  return (
                    <Group top={top} left={left} key={key}>
                      {node.depth === 0 && (
                        <circle
                          r={12}
                          fill="url('#links-gradient')"
                          onClick={() => {
                            node.data.isExpanded = !node.data.isExpanded;
                            console.log(node);
                            forceUpdate();
                          }}
                        />
                      )}
                      {node.depth !== 0 && (
                        <rect
                          height={height}
                          width={width}
                          y={-height / 2}
                          x={-width / 2}
                          fill="#272b4d"
                          stroke={node.data.children ? '#03c0dc' : '#26deb0'}
                          strokeWidth={1}
                          strokeDasharray={node.data.children ? '0' : '2,2'}
                          strokeOpacity={node.data.children ? 1 : 0.6}
                          rx={node.data.children ? 0 : 10}
                          onClick={() => {
                            node.data.isExpanded = !node.data.isExpanded;
                            console.log(node);
                            forceUpdate();
                          }}
                        />
                      )}
                      <text
                        dy=".33em"
                        fontSize={9}
                        fontFamily="Arial"
                        textAnchor="middle"
                        style={{ pointerEvents: 'none' }}
                        fill={node.depth === 0 ? '#71248e' : node.children ? 'white' : '#26deb0'}
                      >
                        {node.data.name}
                      </text>
                    </Group>
                  );
                })}
              </Group>
            )}
          </Tree>
        </Group>
      </svg>
    </div>
  );
}

题外话

开源项目 openmatedata
血缘图使用的react-flow,节点,线都是使用div画的。大数据量时,可能堪忧
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1131929.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电子学会C/C++编程等级考试2023年05月(三级)真题解析

C/C等级考试&#xff08;1~8级&#xff09;全部真题・点这里 第1题&#xff1a;找和为K的两个元素 在一个长度为n(n < 1000)的整数序列中&#xff0c;判断是否存在某两个元素之和为k。 输入 第一行输入序列的长度n和k&#xff0c;用空格分开。 第二行输入序列中的n个整数&am…

【021】整理力学拉伸实验数据(复制、黏贴、计算)_#VBA

整理力学拉伸实验数据 1. 需求2. 实现流程2.1 流程图2.2 运行方法2.3 完整代码 1. 需求 2. 实现流程 2.1 流程图 流程如上&#xff0c;因测试得到多个数据表格&#xff0c;先将表格数据合并&#xff0c;并以文件名作为每个数据的代号。然后更换坐标轴&#xff0c;通过对文件名…

单片机设计_RTC时钟(ACM32F403)

一、电路设计 ACM32F403开发板 实现结果&#xff1a;通过串口发送每秒的时间 二、运行结果 三、部分代码 #include "APP.h"UART_HandleTypeDef Uart_Handle;/********************************************************************************* * Function …

【Django 05】Django-DRF(ModelViewSet)、路由组件、自定义函数

1. Django-DRF&#xff08;ModelViewSet&#xff09; 1.1 DRF是什么&#xff1f; ModelViewSet 是 Django REST framework 提供的一个视图集类&#xff0c;它封装了常见的模型操作方法。 模型类提供了默认的增删改查功能。 它继承自 GenericViewSet、ListModelMixin、Retri…

(四)Apache log4net™ 手册 - AOP

0、引言 如果你已经开发了一个中型或者大型的 .NET / .NET Framework 项目但还没有为其添加日志系统。那么&#xff0c;你可能需要重新回顾大量的业务逻辑代码&#xff0c;并在其中找到合适的位置&#xff0c;编写合适的日志输出语句进行插入&#x1f641;。 显然&#xff0c…

mybatisPlus逻辑删除注解@TableLogic

当我做了一个实体类&#xff0c;字段为del_flag的逻辑删除字段&#xff0c;要通过这个字段控制数据库中的数据逻辑删除。 重写mapper中的deleteById&#xff0c; 先按id查出数据&#xff0c;在更新此数据中的del_flag字段为1&#xff0c;调用update方法更新数据。 这种方式我…

adb: error: 46-byte write failed: Invalid argument

使用adb pull可以&#xff0c;但是adb push fail。 貌似是adb的问题&#xff0c;将adb.exe 重命名为pdb&#xff0c;使用pdb push就行了。

三季报开启消费电子增长新纪元?看蓝思科技如何落子

10月18日晚间&#xff0c;蓝思科技公布了2023年第三季度报告。根据报告&#xff0c;蓝思科技第三季度营业收入136.31亿元&#xff0c;同比增长9.98%&#xff0c;环比增长31.85%&#xff1b;归母净利润10.95亿元&#xff0c;同比增长2.93%&#xff0c;环比119.88%。 作为消费电…

OceanBase 全局索引与局部索引探索

OceanBase 全局索引与局部索引探索导致的本区域查找和跨区域查找。 作者&#xff1a;网名大数据模型&#xff0c;对制造业、银行业、通讯业了解多一点&#xff0c;关心专注国产数据库技术布道以及数据资产建设的应用实践。 爱可生开源社区出品&#xff0c;原创内容未经授权不得…

day02:DML DQL DCL

目录 一:DML 二:DCL 三:DCL 一:DML 1:概念:数据操作原因&#xff0c;对数据进行增删改。 2:三个操作 (1):增加:insert id name age gender 1 令狐冲 23 男 2(添加的数据)风清扬25男 1--->给指定字段添加数据:insert into 表名(字段1&#xff0c;字段2--)values…

基础课9——机器学习

1.概念 机器学习是一种数据分析技术&#xff0c;它使计算机能够像人类一样从经验中学习。机器学习算法使用计算方法直接从数据中获取信息&#xff0c;而不依赖于预定方程模型。当可用于学习的样本数量增加时&#xff0c;这些算法可以自适应提高性能。 机器学习是人工智能的一…

方法递归详解

什么是方法递归 方法直接调用自己或者间接调用自己的形式称为方法递归&#xff08; recursion&#xff09;。 递归做为一种算法在程序设计语言中广泛应用。 递归的形式 直接递归&#xff1a;方法自己调用自己。 间接递归&#xff1a;方法调用其他方法&#xff0c;其他方法又…

Postman中几个body请求格式区别及使用说明

参阅&#xff1a;https://blog.csdn.net/qq_41063141/article/details/101505956&#xff0c;在此基础上添加代码使用说明 一、Params与Body 二者区别在于请求参数在http协议中位置不一样。 Params 它会将参数放入url中以&#xff1f;区分以&拼接 Body则是将请求参数放在请…

39.B树,B+树(王道第7章查找补充知识)

目录 一. B树 &#xff08;1&#xff09;B树的定义 &#xff08;2&#xff09;B树的高度 &#xff08;3&#xff09;B树的插入 &#xff08;4&#xff09;B树的删除 二. B树 &#xff08;1&#xff09;B树的定义 &#xff08;2&#xff09;B树与B树的区别 一. B树 &am…

什么是自动证书管理环境(ACME)

组织的网站需要 24x7 全天候可用&#xff0c;以建立信任并提供信息&#xff0c;如果网站因证书过期而停机&#xff0c;那么很难恢复失去的客户信任、收入和品牌声誉&#xff0c;手动管理证书基础结构会使组织面临中断、中间人 &#xff08;MITM&#xff09; 攻击等的严重风险。…

新上线游戏产品需不需要防御?

游戏运营免不了遭受恶意DDoS和CC攻击&#xff0c;且攻击常达百G以上&#xff0c;攻击流量过大&#xff0c;超过一般服务器的基础防护能力&#xff0c;不少企业面对大流量攻击显得束手无策&#xff0c;只能选择被迫停机&#xff0c;其次游戏行业利润高&#xff0c;很容易被黑客盯…

智慧矿山:如何快速识别带式运输机空载状态!

带式运输机作为一种常见的物料输送设备&#xff0c;广泛应用于矿山、建筑、化工等行业。但在使用过程中&#xff0c;经常会出现空载运行的情况&#xff0c;即带式运输机无物料传送时仍不停工作&#xff0c;导致能源和设备的浪费。因此&#xff0c;对带式运输机进行空载识别并采…

【期刊】IEEE系列指定期刊模版下载(LaTeX或者Word)全网最细教程

IEEE旗下有很多期刊&#xff0c;例如&#xff1a; IEEE Transactions on Pattern Analysis and Machine Intelligence IEEE Transactions on Cybernetics IEEE Transactions on Neural Networks and Learning Systems IEEE Transactions on Industrial Informatics IEEE Tra…

代理服务器可能有问题,或地址不正确的解决办法

问题&#xff1a; 解决办法&#xff1a; 1.找到代理服务器设置 2.代理服务器&#xff0c;设置关闭

基于springboot实现在线考试平台管理系统【项目源码+论文说明】计算机毕业设计

基于Springboot实现在线考试平台管理系统演示 摘要 网络的广泛应用给生活带来了十分的便利。所以把在线考试管理与现在网络相结合&#xff0c;利用java技术建设在线考试系统&#xff0c;实现在线考试的信息化管理。则对于进一步提高在线考试管理发展&#xff0c;丰富在线考试管…