使用 d3 或 cytoscape 渲染家谱

2024-02-02

我在用 Javascript 生成漂亮的家谱时遇到问题。

要求:

  • 每个孩子应该连接到树中的两个父母,而不是像某些图中的一个
  • 我希望配偶在树上彼此相邻(相同的垂直位置)
  • 我想把节点按世代垂直组织起来,这样你就能一目了然地看到同一年代出生的人。
  • 随着时间的推移,一个人可以有多个配偶,并且每个配偶都有孩子
  • 父母和孩子可以在树中自由添加,因此不仅仅是“从一个人向上追踪血统”

我试过的最接近这个:

  1. Cytoscape JS https://js.cytoscape.org/ with Dagre https://github.com/dagrejs/dagre/ as layout engine, and curve-style: taxi https://js.cytoscape.org/#style/taxi-edges edges enabled. Family tree

    (随机数据图表。实线为亲子关系,虚线为配偶关系)

    问题在于夫妻之间的关系不一致。 Dagre 历史上支持“等级”作为节点的参数,这意味着您可以强制某些节点处于特定高度(如果愿意,可以将其视为“一代”)。不幸的是,它不再起作用了 https://github.com/dagrejs/dagre/issues/130,以及负责任的开发商不再参与该项目 https://github.com/dagrejs/dagre/issues/159。这可以很好地解决我的问题。

我尝试过但失败的其他事情:

  1. 将 dagre 降级到支持排名的旧版本?

    还没有获得使用任何版本的 dagre 的等级。

  2. D3 https://d3js.org/ with dagre-d3 https://github.com/dagrejs/dagre-d3

    与上面的问题相同,因为dagre-d3是dagre的修改版本,这意味着它不支持分代排名。

  3. yFiles 家谱 https://live.yworks.com/demos/layout/familytree/演示看起来很棒,但是是商业的。出于我的目的(希望任何人都建立自己的家谱),单个开发人员许可证的成本为 26.000 美元(!?!)。显然不能接受。

我的问题

是否可以像我上面描述的那样垂直对齐 cytoscape/dagre 图中的节点?

如果没有,我愿意尝试其他库和其他布局算法。

我正在寻找一个看起来类似于 yFiles 解决方案的工作示例,但使用开源工具。


在你深入了解我的答案之前:) 你可能想看看WebCola https://github.com/tgdwyer/WebCola,我在研究约束力定向图时遇到的:

基于 JavaScript 约束的高质量图形布局 使用 D3.js 和其他基于 Web 的图形进行可视化和探索 图书馆。

它可以让你指定 x 和 y 维度约束 https://ialab.it.monash.edu/webcola/examples/alignment.html正如我在下面的示例中对 y 维度所做的那样。我自己没有使用过它,但看起来非常适合您的要求。它可以与 CytoScape 配合使用,因此您可以在已经完成的工作的基础上进行构建...

将尺寸约束应用于力导向图:

由于您没有处理严格的层次结构(例如,您不是从一个后代开始并逐步向上),一种方法是使用D3 力定向图 https://github.com/d3/d3-force用一个节点来代表每个家庭成员。与线性层次结构相比,这将提供更大的灵活性。

然后,可以通过将节点限制在 y 轴上的固定点来实现您正在寻找的分代布局。

这是概念证明 https://stackblitz.com/edit/js-jbn7hl:

  • 家族三代人
  • 多个配偶由 Alice 和 Bob / Bob 和 Carol 代表
  • 大卫是爱丽丝和鲍勃的孩子
  • 詹姆斯是鲍勃和卡罗尔的孩子
  • 节点生成(或 y 坐标)计算如下assignGeneration基于链接的子节点、伙伴节点和父节点
  • 节点 X 坐标由 d3 处理,我认为这比尝试手动为每个节点分配 x 轴上的位置更稳健
  • Basic styling:
    • 合作伙伴链接是珊瑚色的
    • 子链接是浅蓝色的
    • 兄弟链接是浅绿色的

希望这里有足够的信息供您决定这是否是可行的方法。在父母和孩子之间建立展示性的垂直/水平联系应该相当简单,但可能需要一些实验。

可能需要进行调整(取决于数据量和节点关系等)simulation- 同样,需要进行一些实验才能生成最佳布局。有关可用的不同力量的更多信息here https://github.com/d3/d3-force#simulation_nodes.

<!DOCTYPE html>
<html>

<head>
  <style>
svg {
  border: 1px solid gray;
}

.partner_link {
  stroke: lightcoral;
}

.child_link {
  stroke: lightskyblue;
}

.sibling_link {
  stroke: lightseagreen;
}
  </style>
</head>

<body>
  <script src="https://d3js.org/d3.v5.min.js"></script>
  <script type="text/javascript">

var nodeData = [{
  id: 1,
  name: 'Alice',
  partners: [2],
  children: [4]
}, {
  id: 2,
  name: 'Bob',
  partners: [1, 3],
  children: [4,10]
}, {
  id: 3,
  name: 'Carol',
  partners: [2],
  children: [10]
}, {
  id: 4,
  name: 'David',
  partners: [7],
  children: [8]
}, {
  id: 5,
  name: 'Emily',
  partners: [6],
  children: [7, 9]
}, {
  id: 6,
  name: 'Fred',
  partners: [5],
  children: [7, 9]
}, {
  id: 7,
  name: 'Grace',
  partners: [4],
  children: [8]
}, {
  id: 8,
  name: 'Harry',
  partners: null,
  children: null
}, {
  id: 9,
  name: 'Imogen',
  partners: null,
  children: null
}, {
  id: 10,
  name: 'James',
  partners: null,
  children: null
}];

var linkData = [];

nodeData.forEach((node, index) => {
  if (node.partners) {
    node.partners.forEach(partnerID => {
      linkData.push({ source: node, target: nodeData.find(partnerNode => partnerNode.id === partnerID), relationship: 'Partner' });
    })
  }
  if (node.children) {
    node.children.forEach(childID => {
      const childNode = nodeData.find(childNode => childNode.id === childID);
      if (node.children.length > 1) {
        childNode.siblings = node.children.slice(0, node.children.indexOf(childNode.id)).concat(node.children.slice(node.children.indexOf(childNode.id) + 1, node.children.length));
        childNode.siblings.forEach(siblingID => {
          linkData.push({ source: childNode, target: nodeData.find(siblingNode => siblingNode.id === siblingID), relationship: 'Sibling' });
        })
      }
      linkData.push({ source: node, target: childNode, relationship: 'Child' });
    })
  }
});

linkData.map(d => Object.create(d));

assignGeneration(nodeData, nodeData, 0);

var w = 500,
  h = 500;

var svg = d3.select("body")
  .append("svg")
  .attr("width", w)
  .attr("height", h);

var color = d3.scaleOrdinal(d3.schemeCategory10);

var rowScale = d3.scalePoint()
  .domain(dataRange(nodeData, 'generation'))
  .range([0, h - 50])
  .padding(0.5);

var simulation = d3.forceSimulation(nodeData)
  .force('link', d3.forceLink().links(linkData).distance(50).strength(1))
  .force("y", d3.forceY(function (d) {
    return rowScale(d.generation)
  }))
  .force("charge", d3.forceManyBody().strength(-300).distanceMin(60).distanceMax(120))
  .force("center", d3.forceCenter(w / 2, h / 2));

var links = svg.append("g")
  .attr("stroke", "#999")
  .attr("stroke-opacity", 0.8)
  .selectAll("line")
  .data(linkData)
  .join("line")
  .attr("stroke-width", 1)
  .attr("class", d => {
    return d.relationship.toLowerCase() + '_link';
  });;

var nodes = svg.append("g")
  .attr("class", "nodes")
  .selectAll("g")
  .data(nodeData)
  .enter().append("g")

var circles = nodes.append("circle")
  .attr("r", 5)
  .attr("fill", function (d) {
    return color(d.generation)
  });

var nodeLabels = nodes.append("text")
  .text(function (d) {
    return d.name;
  }).attr('x', 12)
  .attr('y', 20);

var linkLabels = links.append("text")
  .text(function (d) {
    return d.relationship;
  }).attr('x', 12)
  .attr('y', 20);

/*
// Y Axis - useful for testing:
var yAxis = d3.axisLeft(rowScale)(svg.append("g").attr("transform", "translate(30,0)"));
*/

simulation.on("tick", function () {
  links
    .attr("x1", d => {
      return d.source.x;
    })
    .attr("y1", d => {
      return rowScale(d.source.generation);
    })
    .attr("x2", d => {
      return d.target.x;
    })
    .attr("y2", d => {
      return rowScale(d.target.generation);
    });
  nodes.attr("transform", function (d) {
    return "translate(" + d.x + "," + rowScale(d.generation) + ")";
  })
});

function dataRange(records, field) {
  var min = d3.min(records.map(record => parseInt(record[field], 10)));
  var max = d3.max(records.map(record => parseInt(record[field], 10)));
  return d3.range(min, max + 1);
};

function assignGeneration(nodes, generationNodes, generationCount) {
  const childNodes = [];
  generationNodes.forEach(function (node) {
    if (node.children) {
      // Node has children
      node.generation = generationCount + 1;
      node.children.forEach(childID => {
        if (!childNodes.find(childNode => childNode.id === childID)) {
          childNodes.push(generationNodes.find(childNode => childNode.id === childID));
        }
      })
    } else {
      if (node.partners) {
        node.partners.forEach(partnerID => {
          if (generationNodes.find(partnerNode => partnerNode.id === partnerID && partnerNode.children)) {
            // Node has partner with children
            node.generation = generationCount + 1;
          }
        })
      } else {
        // Use generation of parent + 1
        const parent = nodes.find(parentNode => parentNode.children && parentNode.children.indexOf(node.id) !== -1);
        node.generation = parent.generation + 1;
      }
    }
  });
  if (childNodes.length > 0) {
    return assignGeneration(nodes, childNodes, generationCount += 1);
  } else {
    nodes.filter(node => !node.generation).forEach(function (node) {
      node.generation = generationCount + 1;
    });
    return nodes;
  }
}

  </script>
</body>

</html>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 d3 或 cytoscape 渲染家谱 的相关文章

  • 暂停除了已激活的玩家之外的所有其他玩家。

    我有这个插件 它可以将不同的样式应用于 html5
  • 将鼠标悬停时的鼠标光标更改为锚状样式

    如果我将鼠标悬停在div鼠标光标将更改为 HTML 锚点中的光标 我怎样才能做到这一点 假设你的div has an id myDiv 将以下内容添加到您的 CSS 中 这cursor pointer指定光标应与用于锚点 超链接 的手形图标
  • socket.io 的良好初学者教程? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 以编程方式填写reactjs表单

    我正在编写一个用户脚本 但无法填写由reactjs制作的表单 我的代码 document querySelector id username value email protected cdn cgi l email protection
  • IE从哪个版本开始支持Object.create(null)?

    您可以通过多种方式在 JavaScript 中创建对象 creates an object which makes the Object prototype of data var data1 new Object Object liter
  • 使用 JavaScript 禁用第三方 cookie

    我正在努力根据所有在欧盟运营的公司的数据保护规则实施新的 Cookie 政策合规性 根据该规则 用户在使用任何网站时必须能够拒绝 接受除必需的 Cookie 之外的所有内容 在我客户的网站中 我可以看到正在存储以下第三方 cookie ga
  • 摩卡 - Chai Karma“套件未定义”

    我对 jscript tdd 很陌生 遇到了问题 希望有人能告诉我我在做什么 在浏览器中运行测试 通过 HTML 文件 一切正常 通过节点和业力运行它们我得到以下异常 我想在 node js 主机的 karma 中使用 Mocha 和 Ch
  • 引导程序提前输入未填充承诺的响应

    我的引导程序预输入如下
  • 使用 dc.js 按条形值对条形图中的条形进行排序(排序)

    如何通过维度的计算值而不是维度本身的名称对 dc js 示例中的 x 轴 维度 进行排序 例如 请考虑序数条形图的 dc js 示例 https github com dc js dc js blob master web examples
  • 防止 iOS 键盘在 cordova 3.5 中滚动页面

    我正在使用 Cordova 3 5 和 jQuery mobile 构建 iOS 应用程序 我在大部分应用程序中禁用了滚动功能 但是 当我选择输入字段时 iOS 键盘会打开并向上滚动页面 我不想要这个功能 由于输入足够高 键盘不会覆盖它 我
  • 将 GMT 时间转换为当地时间

    我以这种格式从我的服务器获取 GMT 时间 Fri 18 Oct 2013 11 38 23 GMT 我的要求是使用Javascript将此时间转换为本地时间 例如 如果用户来自印度 首先我需要采用时区 5 30并将其添加到我的服务器时间并
  • 调整图像大小并将画布旋转 90 度

    这里有很多关于在 js 上使用画布旋转图像的主题 我阅读了其中的大部分内容 但无法找到解决我的问题的方法 我正在接收任何分辨率的图像 来自上传组件 我将其大小调整为 1024x768 如下所示 var canvas document cre
  • 日期出现奇怪的错误,“未捕获非法访问”

    所以我试图找到最新的DateJavascript 可以处理 我把它减少到 9 月 275760 并增加了我开始捕获未捕获的天数illegal access例外new Date 09 24 275760 to new Date 10 13 2
  • Three.js 各种大小的粒子

    我是 Three js 的新手 正在尝试找出添加 1000 个粒子的最佳方法 每个粒子都有不同的大小和颜色 每个粒子的纹理是通过绘制画布创建的 通过使用粒子系统 所有粒子都具有相同的颜色和大小 为每个粒子创建一个粒子系统是非常低效的 有没有
  • 在 JavaScript 循环之外声明变量可以提高速度和内存?

    C 也有类似的问题 但我们没有看到 JavaScript 的任何问题 在循环内声明变量是否可以接受 假设循环有 200 次迭代 使用样本 2 相对于样本 1 是否有性能要求 内存和速度 我们使用 jQuery 来循环 它提高了我们将 var
  • 在 Javascript 中连接空数组

    我正在浏览一些代码 我想知道这有什么用处 grid push concat row 根据我的理解 它等同于 grid push row 为什么要大惊小怪 连接 你想使用 concat当您需要展平数组并且没有由其他数组组成的数组时 例如 va
  • 带参数的事件监听器

    我想将参数传递给 JavaScript 中的事件侦听器 我已经找到了解决方案 但我无法理解它们为什么或如何工作以及为什么其他解决方案不起作用 我有 C C 背景 但是 Javascript 函数的执行有很大不同 您能否帮助我理解以下示例如何
  • 如何用另一个响应替换窗口的 URL 哈希?

    我正在尝试使用替换方法更改哈希 URL document location hash 但它不起作用 function var anchor document location hash this returns me a string va
  • 在 CKEditor 中设置字体大小和字体系列

    我正在使用 ckeditor 我想问一下这个插件如何设置font family和font size 我尝试过使用 CKEDITOR config font defaultLabel Arial CKEDITOR config fontSiz
  • JavaScript 相对路径

    在第一个 html 文件中 我使用了一个变量类别链接 var categoryLinks Career prospects http localhost Landa DirectManagers 511 HelenaChechik Dim0

随机推荐

  • Ubuntu 终端 - 使用 gnu parallel 读取文件夹中所有文件中的行

    我正在尝试计算 Ubuntu 下一个非常大的文件夹中所有文件的行数 这些文件是 gz 文件 我使用 zcat wc l 计算所有文件中的所有行 而且很慢 我想使用多核计算来完成这项任务并发现this https www gnu org so
  • Kotlin数据类:如果我在编译时不知道属性的名称,如何读取属性的值?

    如果属性名称仅在运行时已知 如何读取 Kotlin 数据类实例中的属性值 这是一个函数 用于从给定属性名称的类实例中读取属性 如果未找到属性 则抛出异常 但您可以更改该行为 import kotlin reflect KProperty1
  • 在 ant 任务中使用 eclipse 类路径

    我想使用 Ant 交付 JAR 文件 如何在 Ant 任务中使用 Eclipse 类路径 问候 托比亚斯 Try ant4eclipse http ant4eclipse sourceforge net
  • 带 IF 条件的 PL/SQL 游标

    我的代码中有以下光标 CURSOR cur1 IS SELECT a b c d FROM EMP BEGIN Stored procedure logic END 该游标正在从 EMP 表获取信息 但我需要改变如下 有一个包含键值对的表
  • 如何跳转到 Visual Studio 源代码管理资源管理器中的文件

    我喜欢 Visual Studio 2010 的 PowerCommands 扩展的 解决方案资源管理器 gt 右键单击 gt 打开包含文件夹 功能 我想要相当于 跳转到源代码管理资源管理器中的位置 功能 如何找出给定打开文件或解决方案资源
  • 突出显示 Lisp 表单的 Emacs 模式

    什么是 Emacs 模式或包 它突出显示 Lisp 表单 更改背景颜色 以便您所在的表单具有一种颜色 外部表单具有另一种颜色 外部表单具有另一种颜色 依此类推 你可能想尝试mwe 彩盒 http www foldr org michaelw
  • 环边太长

    在下面的点图中 之间的边handleClick and onSelect in COLOROPTION太长了 如何将其变成紧密循环 如果也这样就更好了DIV离右边有点远COLOROPTION 循环边是option p4 e gt optio
  • WPF 控件的公共类修饰符

    我正在创建 Windows 应用程序和类库 类库包含名为 InsertForm xaml 的 WPF 控件 InsertForm 包含名为 eUserName 的文本框 我使用以下代码来显示 InsertForm 这样就成功了 但我无法访问
  • 如何安装列出的类型?

    我正在使用带有 Typescript 的库 并收到编译器错误 public components chatlogs ts 25 19 错误 TS2304 找不到名称 Handsontable 似乎有一个打字 typings search h
  • 工厂方法设计模式

    据书中所述 工厂模式的本质是 定义一个接口 创建一个对象 但让子类决定使用哪个类 实例化 Factory 方法让类将实例化推迟到 子类 假设我有一个 Creator 类 class Product this is what the Fact
  • 使用 javascript 获取当前月份的天数

    对于我的网站 我试图获取特定功能当前月份的天数 我在网上看到过获取指定月份的天数的示例 但是我需要获取当前月份的天数并找出该月还剩多少天 这是我设法组合的代码 function myFunction var today new Date v
  • 生产/heroku 失败:WHERE a.attrelid = '"schools"'::regclass

    我在本地环境中添加了一个名为schools并且在开发中运行良好 事实上 它甚至在暂存 heroku 中工作得很好 但在生产中却失败了很多rake db migrate抛出以下错误 我什至无法预编译资产 使用 RAILS ENV 生产 访问我
  • 使用 git 或 gitLab 的持续集成来部署 Angular 应用程序

    我希望通过 Apache 服务器上的 bitbucket 持续集成来部署 Angular 5 应用程序 现在因为我是该领域的新手 所以我不知道执行此操作的可能选择 比如我们是否需要将 webpack 与 Jenkins 集成 或者我们是否需
  • Node.js 中的 JSON 对象是全局的吗?

    我似乎找不到 Node js 的 JSON parse 的文档 我只是看到它散落在各种脚本周围 我想知道 它从哪里来 是否有某些回调使其可用 或者它是一个全局对象 谢谢 马特 穆勒 它内置于 V8 中 这是他们的实现的链接 http cod
  • 数据网格复选框自动化

    我有一个带有复选框的数据网格 当我单击单元格时 我希望在选择包含该复选框的单元格时自动选中该复选框 它现在所做的是我需要选择单元格然后单击复选框 这对我们来说非常烦人
  • 如何在 Javascript 中使用 onPageLoad?

    我尝试使用 onPageLoad function alert hi 但这行不通 我需要它作为 Firefox 扩展 请问有什么建议吗 如果你想在普通 JavaScript 中执行此操作 只需使用window onload事件处理程序 wi
  • 如何制作 TortoiseSVN diff .dot 和 .dotx Word 模板文件

    TortoiseSVN 具有区分 Microsoft Word 文档修订版的惊人能力 这显然是通过以下脚本实现的 C Program Files TortoiseSVN Diff Scripts它调用 MS Word 进行比较 而不是使用
  • Rails 3 - 如何通过 order by 从模型中获取行号

    我正在制作一个小应用程序 其中包含排行榜概念 基本上 该模型只是一个player name 和一个current score 我想做的是获取特定玩家的排名 并且考虑到新的分数一直在出现 它需要是动态的 显然 我可以使用 order by 子
  • 如何使用 VBA 编辑电源查询的源?

    我正在尝试使用 VBA 编辑我的查询之一的源 这是我到目前为止所拥有的 Dim mFormula As String mFormula let Source Excel Workbook File Contents wbname null
  • 使用 d3 或 cytoscape 渲染家谱

    我在用 Javascript 生成漂亮的家谱时遇到问题 要求 每个孩子应该连接到树中的两个父母 而不是像某些图中的一个 我希望配偶在树上彼此相邻 相同的垂直位置 我想把节点按世代垂直组织起来 这样你就能一目了然地看到同一年代出生的人 随着时