簡體   English   中英

水平顯示 D3 網絡圖而不折疊

[英]Display D3 Network Graph Horizontally without folding

我正在創建一個將節點與路徑連接起來的網絡圖。

我的要求很簡單——網絡圖應該是垂直的或水平的而不折疊。

到目前為止,我創建了一個以水平格式顯示圖表的圖表。

但是,如果節點集非常有限(我嘗試了forceManyBody().strength()forceLink(links).distance()的多次試錯以使其正常工作,則該圖僅以單行顯示(不折疊) )

在此處輸入圖像描述

但對於更大的沒有。 的節點,圖形像這樣折疊自己——

在此處輸入圖像描述

d3.forceManyBody().strength(-600)的一些變體給我一行,但鏈接順序相反,就像這樣——

在此處輸入圖像描述

在這里,5050 圈應該是第一個圈,但它在最后。

所以,我的問題是——

  1. 如何根據節點正確找到forceManyBody().strength()forceLink(links).distance()所以我只有一行
  2. 為什么第一個圈子最后來了?

我不介意是否必須滾動查看所有節點(可能 d3.zoom 可以提供幫助?)

尋找指點。 請在下面找到代碼和數據:

 const width = 1413; const height = 480; // data const nodes = [{ "_time": 1666891307118, "CUSTOMER_NAME": " Customer_1", "CUSTOMER": "CID_123", "SOURCE": "QUEUE_PROCESSOR", "SUPPLIER_ANID": "SUPP_ID", "TRACKING_STATUS": "FAILED", "CHECKPOINT": "5050", "DOCUMENT_NUMBER": "DOC_NO_123", "PAYLOAD_ID": "PID_123" }, { "_time": 1666891307241, "CUSTOMER_NAME": " Customer_1", "CUSTOMER": "CID_123", "SOURCE": "PROPAGATION_DISPATCHER", "SUPPLIER_ANID": "SUPP_ID", "TRACKING_STATUS": "FAILED", "CHECKPOINT": "1110", "DOCUMENT_NUMBER": "DOC_NO_123", "PAYLOAD_ID": "PID_123" }, { "_time": 1666891307580, "CUSTOMER_NAME": " Customer_1", "CUSTOMER": "CID_123", "SOURCE": "PROPAGATION_PROCESSOR", "SUPPLIER_ANID": "SUPP_ID", "TRACKING_STATUS": "FAILED", "CHECKPOINT": "1150", "DOCUMENT_NUMBER": "DOC_NO_123", "PAYLOAD_ID": "PID_123" }, { "_time": 1666891307937, "CUSTOMER_NAME": " Customer_1", "CUSTOMER": "CID_123", "SOURCE": "QUEUE_PROCESSOR", "SUPPLIER_ANID": "SUPP_ID", "TRACKING_STATUS": "FAILED", "CHECKPOINT": "5000", "DOCUMENT_NUMBER": "DOC_NO_123", "PAYLOAD_ID": "PID_123" }, { "_time": 1666891308121, "CUSTOMER_NAME": " Customer_1", "CUSTOMER": "CID_123", "SOURCE": "QUEUE_PROCESSOR", "SUPPLIER_ANID": "SUPP_ID", "TRACKING_STATUS": "FAILED", "CHECKPOINT": "5010", "DOCUMENT_NUMBER": "DOC_NO_123", "PAYLOAD_ID": "PID_123" }, { "_time": 1666891308278, "CUSTOMER_NAME": " Customer_1", "CUSTOMER": "CID_123", "SOURCE": "CXML_OUT_DISPATCHER", "SUPPLIER_ANID": "SUPP_ID", "TRACKING_STATUS": "FAILED", "CHECKPOINT": "1250", "DOCUMENT_NUMBER": "DOC_NO_123", "PAYLOAD_ID": "PID_123" }, { "_time": 1666891308605, "CUSTOMER_NAME": " Customer_1", "CUSTOMER": "CID_123", "SOURCE": "PROPAGATION_PROCESSOR", "SUPPLIER_ANID": "SUPP_ID", "TRACKING_STATUS": "FAILED", "CHECKPOINT": "1145", "DOCUMENT_NUMBER": "DOC_NO_123", "PAYLOAD_ID": "PID_123" }, { "_time": 1666891309471, "CUSTOMER_NAME": " Customer_1", "CUSTOMER": "CID_123", "SOURCE": "CXML_OUT_DISPATCHER", "SUPPLIER_ANID": "SUPP_ID", "TRACKING_STATUS": "FAILED", "CHECKPOINT": "1300", "DOCUMENT_NUMBER": "DOC_NO_123", "PAYLOAD_ID": "PID_123" }, { "_time": 1666891309485, "CUSTOMER_NAME": " Customer_1", "CUSTOMER": "CID_123", "SOURCE": "CXML_OUT_DISPATCHER", "SUPPLIER_ANID": "SUPP_ID", "TRACKING_STATUS": "FAILED", "CHECKPOINT": "1450", "DOCUMENT_NUMBER": "DOC_NO_123", "PAYLOAD_ID": "PID_123" }, { "_time": 1666891313018, "CUSTOMER_NAME": " Customer_1", "CUSTOMER": "CID_123", "SOURCE": "QUEUE_PROCESSOR", "SUPPLIER_ANID": "SUPP_ID", "TRACKING_STATUS": "FAILED", "CHECKPOINT": "5050", "DOCUMENT_NUMBER": "DOC_NO_123", "PAYLOAD_ID": "PID_123" }, { "_time": 1666902123954, "CUSTOMER_NAME": " Customer_1", "CUSTOMER": "CID_123", "SOURCE": "EXTERNAL_GATEWAY", "SUPPLIER_ANID": "SUPP_ID", "TRACKING_STATUS": "FAILED", "CHECKPOINT": "1440", "DOCUMENT_NUMBER": "DOC_NO_123", "PAYLOAD_ID": "PID_123" } ]; const links = [{ "source": 0, "target": 1, "time": 123 }, { "source": 1, "target": 2, "time": 339 }, { "source": 2, "target": 3, "time": 357 }, { "source": 3, "target": 4, "time": 184 }, { "source": 4, "target": 5, "time": 157 }, { "source": 5, "target": 6, "time": 327 }, { "source": 6, "target": 7, "time": 866 }, { "source": 7, "target": 8, "time": 14 }, { "source": 8, "target": 9, "time": 3533 }, { "source": 9, "target": 10, "time": 10810936 } ]; const circleRadius = 25; const linkColor = '#999'; //#FFFF00 const dangerColor = '#FF5286'; const dangerTimeInSec = 2; const WAITING_FOR_CONFIRMATION_COLOR = '#F8D06B'; const IN_PROCESS_COLOR = '#6E9FFF'; const COMPLETED_COLOR = '#6CCF8E'; const ERROR_COLOR = '#FF5286'; function getStatusColor(data) { if (data.TRACKING_STATUS === 'WAITING_FOR_CONFIRMATION') { return WAITING_FOR_CONFIRMATION_COLOR; } if (data.TRACKING_STATUS === 'IN_PROCESS') { return IN_PROCESS_COLOR; } if (data.TRACKING_STATUS === 'COMPLETED') { return COMPLETED_COLOR; } if (data.TRACKING_STATUS === 'FAILED') { return ERROR_COLOR; } return 'gray'; } function getTimeTextColor(data) { if (data.time > (dangerTimeInSec * 1000)) { return dangerColor; } return linkColor } function getTimeBetweenNodes(data) { const timeInSecs = data.time / 1000; return `${timeInSecs}s` } function createChart() { const svgId = "svgId"; const node = document.getElementById(svgId); // svg.append('g';) while (node && node.firstChild) { node && node.firstChild.remove(); } const svg = d3.select(`#${CSS.escape(svgId)}`); // const centerX = width /2; const centerY = height / 2; const simulation = d3.forceSimulation(nodes).force("charge", d3.forceManyBody().strength(-600)).force( "collision", d3.forceCollide().radius(function(d) { return d.radius * 2; }) ).force("link", d3.forceLink(links).distance(50)).force("y", d3.forceY(0).strength(0.55)).force("center", d3.forceCenter(width / 2, centerY)).stop(); for (let i = 0; i < 300; ++i) { simulation.tick(); } const arrowId = `arrow-${svgId}`; svg.append("svg:defs").append("svg:marker").attr("id", arrowId).attr("viewBox", "0 -5 10 10").attr('refX', 0).attr("markerWidth", 5).attr("markerHeight", 5).attr("orient", "auto").append("svg:path").style("stroke", linkColor).attr("fill", linkColor).attr("d", "M0,-5L10,0L0,5"); const lines = svg.selectAll("line").data(links).enter().append("path").attr("class", "link").style("stroke", linkColor).attr('marker-end', (d) => `url(#${arrowId})`).style("stroke-width", 1); const circles = svg.selectAll('circle').data(nodes).enter().append('circle').attr('fill', 'none').attr('stroke', (d) => { return getStatusColor(d) }).style("pointer-events", "visible").attr('stroke-width', 2).attr('r', circleRadius) //.call(drag) //.call(zoom) //.on('click', handleClick); // svg.call(zoom); const texts = svg.selectAll('text').data(nodes).enter().append('text').attr('text-anchor', 'middle').attr('text-baseline', 'middle').attr('font-size', '.8rem').attr('fill', '#FFF').style('pointer-events', 'none').text((node) => `${node.CHECKPOINT}`); const timeTexts = svg.selectAll("timeText").data(links).enter().append("text").attr("text-anchor", "middle").attr("text-baseline", "middle").attr("font-size", ".8rem").style("pointer-events", "none").attr('fill', (d) => getTimeTextColor(d)).style('pointer-events', 'none').text((node) => getTimeBetweenNodes(node)); const sourceTexts = svg.selectAll('sourceTexts').data(nodes).enter().append('foreignObject').attr("width", 80).attr("height", 80); sourceTexts.append("xhtml:div").append('p').attr('class', 'source-text').html((d) => { return d.SOURCE.split("_").join(" ") }); circles.attr('cx', (d) => dx).attr('cy', (d) => dy); texts.attr('x', (d) => dx).attr('y', (d) => dy + (circleRadius / 8)); sourceTexts.attr('x', (d) => { return dx - (circleRadius * 1.5); }).attr('y', (d) => dy + (circleRadius)); timeTexts.attr("x", (d) => { return d.source.x + (d.target.x - d.source.x) / 2; }).attr("y", (d) => { return d.source.y + (d.target.y - d.source.y) / 2 - 10; }); lines.attr("d", (d) => "M" + (d.source.x + circleRadius) + "," + (d.source.y) + ", " + (d.target.x - (circleRadius + 10)) + "," + (d.target.y)) } setTimeout(() => { createChart() }, 1000);
 <script src="https://d3js.org/d3.v7.min.js"></script> <svg id="svgId" width="1413px" height="100vh"></svg>

正如評論中提到的。 您的用例非常簡單,可以使用比例和形狀重新創建。 我使用了線性比例尺,並使用節點的索引作為比例尺的域,因為節點之間的時間間隔相差多個數量級。

每個節點都包含在一個組中,以簡化圓、文本和線的相對定位。

由於邊數的節點長度為 - 1,我使用每個 function 分別遍歷節點組並僅附加一條邊,如果當前索引不是最后一個的話。

 // data const nodes = [{ "_time": 1666891307118, "CUSTOMER_NAME": " Customer_1", "CUSTOMER": "CID_123", "SOURCE": "QUEUE_PROCESSOR", "SUPPLIER_ANID": "SUPP_ID", "TRACKING_STATUS": "FAILED", "CHECKPOINT": "5050", "DOCUMENT_NUMBER": "DOC_NO_123", "PAYLOAD_ID": "PID_123" }, { "_time": 1666891307241, "CUSTOMER_NAME": " Customer_1", "CUSTOMER": "CID_123", "SOURCE": "PROPAGATION_DISPATCHER", "SUPPLIER_ANID": "SUPP_ID", "TRACKING_STATUS": "FAILED", "CHECKPOINT": "1110", "DOCUMENT_NUMBER": "DOC_NO_123", "PAYLOAD_ID": "PID_123" }, { "_time": 1666891307580, "CUSTOMER_NAME": " Customer_1", "CUSTOMER": "CID_123", "SOURCE": "PROPAGATION_PROCESSOR", "SUPPLIER_ANID": "SUPP_ID", "TRACKING_STATUS": "FAILED", "CHECKPOINT": "1150", "DOCUMENT_NUMBER": "DOC_NO_123", "PAYLOAD_ID": "PID_123" }, { "_time": 1666891307937, "CUSTOMER_NAME": " Customer_1", "CUSTOMER": "CID_123", "SOURCE": "QUEUE_PROCESSOR", "SUPPLIER_ANID": "SUPP_ID", "TRACKING_STATUS": "FAILED", "CHECKPOINT": "5000", "DOCUMENT_NUMBER": "DOC_NO_123", "PAYLOAD_ID": "PID_123" }, { "_time": 1666891308121, "CUSTOMER_NAME": " Customer_1", "CUSTOMER": "CID_123", "SOURCE": "QUEUE_PROCESSOR", "SUPPLIER_ANID": "SUPP_ID", "TRACKING_STATUS": "FAILED", "CHECKPOINT": "5010", "DOCUMENT_NUMBER": "DOC_NO_123", "PAYLOAD_ID": "PID_123" }, { "_time": 1666891308278, "CUSTOMER_NAME": " Customer_1", "CUSTOMER": "CID_123", "SOURCE": "CXML_OUT_DISPATCHER", "SUPPLIER_ANID": "SUPP_ID", "TRACKING_STATUS": "FAILED", "CHECKPOINT": "1250", "DOCUMENT_NUMBER": "DOC_NO_123", "PAYLOAD_ID": "PID_123" }, { "_time": 1666891308605, "CUSTOMER_NAME": " Customer_1", "CUSTOMER": "CID_123", "SOURCE": "PROPAGATION_PROCESSOR", "SUPPLIER_ANID": "SUPP_ID", "TRACKING_STATUS": "FAILED", "CHECKPOINT": "1145", "DOCUMENT_NUMBER": "DOC_NO_123", "PAYLOAD_ID": "PID_123" }, { "_time": 1666891309471, "CUSTOMER_NAME": " Customer_1", "CUSTOMER": "CID_123", "SOURCE": "CXML_OUT_DISPATCHER", "SUPPLIER_ANID": "SUPP_ID", "TRACKING_STATUS": "FAILED", "CHECKPOINT": "1300", "DOCUMENT_NUMBER": "DOC_NO_123", "PAYLOAD_ID": "PID_123" }, { "_time": 1666891309485, "CUSTOMER_NAME": " Customer_1", "CUSTOMER": "CID_123", "SOURCE": "CXML_OUT_DISPATCHER", "SUPPLIER_ANID": "SUPP_ID", "TRACKING_STATUS": "FAILED", "CHECKPOINT": "1450", "DOCUMENT_NUMBER": "DOC_NO_123", "PAYLOAD_ID": "PID_123" }, { "_time": 1666891313018, "CUSTOMER_NAME": " Customer_1", "CUSTOMER": "CID_123", "SOURCE": "QUEUE_PROCESSOR", "SUPPLIER_ANID": "SUPP_ID", "TRACKING_STATUS": "FAILED", "CHECKPOINT": "5050", "DOCUMENT_NUMBER": "DOC_NO_123", "PAYLOAD_ID": "PID_123" }, { "_time": 1666902123954, "CUSTOMER_NAME": " Customer_1", "CUSTOMER": "CID_123", "SOURCE": "EXTERNAL_GATEWAY", "SUPPLIER_ANID": "SUPP_ID", "TRACKING_STATUS": "FAILED", "CHECKPOINT": "1440", "DOCUMENT_NUMBER": "DOC_NO_123", "PAYLOAD_ID": "PID_123" } ]; const width = 1600; const height = 400; const margin = 100; const data = nodes.map((d) => { d.id = d._time - nodes[0]._time; return d; }); console.log({data}) const svg = d3.select('svg'); const container = svg.append('g').style('transform', `translate(${margin}px, ${height / 2}px)`); const innerWidth = width - (margin * 2); const scale = d3.scaleLinear().range([0, innerWidth]).domain(d3.extent(data, (d, i) => i)); const groups = container.selectAll('g').data(data).enter().append('g').style('transform', (d, i) => `translate(${scale(i)}px, 0`); groups.each(function(d, i) { const e = d3.select(this); if (i < data.length - 1) { e.append('line').attr('x1', 0).attr('y1', 0).attr('x2', scale(1)).attr('y2', 0); e.append('text').attr('x', scale(1) / 2).attr('y', -20).attr('text-anchor', 'middle').text(data[i+1].id) } }); groups.append('circle').attr('r', 30); groups.append('text').attr('x', 0).attr('y', 5).attr('text-anchor', 'middle').text((d) => d.id); groups.append('text').attr('x', 0).attr('y', 50).attr('text-anchor', 'middle').attr('font-size', 10).text((d) => d.SOURCE);
 circle { fill: white; stroke: red; } line { stroke:black; }
 <script src="https://d3js.org/d3.v7.min.js"></script> <svg width=1600 height=480></svg>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM