简体   繁体   English

更改d3强制布局使用的数据

[英]Changing the data used by a d3 force layout

I am playing around with an implementation of Christopher Manning's 3D force layout 我正在研究Christopher Manning的3D力布局的实现

Here is the link to the JSFiddle I'm editing. 是我正在编辑的JSFiddle的链接。 My goal is to create a three dimensional layout where the nodes and links are created from outside data. 我的目标是创建一个三维布局,其中节点和链接是从外部数据创建的。 My question here is, where would I start to transition this layout to one that takes data from an external file? 我的问题是,我将从哪里开始将此布局转换为从外部文件获取数据的布局? Currently the nodes are created: 当前已创建节点:

for(x=0;x<200;x++) {
source = nodes[~~(Math.random() * nodes.length)]
target = {id: 'label-'+x, x: source.x + Math.random(), y: source.y + Math.random(), group: Math.random()}
links.push({source: source, target: target})
nodes.push(target)

I have created force directed layouts that are set up this way, but once the third dimension is introduced I get stuck. 我已经创建了以这种方式设置的力导向布局,但是一旦引入了三维,我就会陷入困境。 This is especially difficult because of how many areas I must alter to get the desired result. 这特别困难,因为我必须更改多少个区域才能获得所需的结果。 Everything I have tried thus far has caused the project to break. 到目前为止,我所做的一切都导致该项目失败。 I am still learning with d3 so any input would be helpful. 我仍在学习d3,因此任何输入都会有所帮助。 Thanks! 谢谢!

I'm hesitant to answer this question because you haven't asked anything concrete. 我不愿意回答这个问题,因为您还没有提出任何具体的问题。 But here's a quick refactor of the linked code that reads the data externally from a JSON API. 但这是链接代码的快速重构,该链接代码从JSON API外部读取数据。 I really don't see anything in the code that makes the data anything different then a conventional force layout. 我真的没有在代码中看到任何东西使数据与传统的力布局有所不同。 Something like this: 像这样:

{
  "nodes": [{
    "x": 100,
    "y": 100,
    "group": 0.5
  },{
    "x": 200,
    "y": 200,
    "group": 0.2
  },{
    "x": 300,
    "y": 300,
    "group": 0.6
  },{
    "x": 400,
    "y": 400,
    "group": 0.1
  },{
    "x": 500,
    "y": 500,
    "group": 0.7
  }],
  "links": [{
    "source": 0,
    "target": 1
  },{
    "source": 1,
    "target": 2
  },{
    "source": 2,
    "target": 3
  },{
    "source": 3,
    "target": 4
  }]
}

The other changes are just to fix variable scoping since node and link are created in the d3.json callback: 其他更改只是为了修复变量作用域,因为nodelink是在d3.json回调中创建的:

 <!DOCTYPE html> <html> <head> <title>Spherical Force-Directed Layout</title> <script src="http://d3js.org/d3.v3.min.js"></script> <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5/dat.gui.min.js"></script> <!--<script src="/js/d3.v3.min.js"></script>--> <!--<script src="/js/dat-gui/build/dat.gui.js"></script> --> <style type="text/css"> body { padding: 0; margin: 0; } path.node { stroke-width: 1.5px; } path.link { stroke: #999; fill-opacity: 0 } </style> </head> <body> <script type="text/javascript"> var projections = { "Albers": d3.geo.albers(), "Azimuthal Equal Area": d3.geo.azimuthalEqualArea(), "Azimuthal Eqidistant": d3.geo.azimuthalEquidistant(), "Conic Conformal": d3.geo.conicConformal(), "Conic Equal Area": d3.geo.conicEqualArea(), "Conic Equidistant": d3.geo.conicEquidistant(), "Eqirectangular": d3.geo.equirectangular(), "Gnomonic": d3.geo.gnomonic(), "Mercator": d3.geo.mercator(), "Orthographic": d3.geo.orthographic(), "Stereographic": d3.geo.stereographic(), "Transverse Mercator": d3.geo.transverseMercator(), }; var config = { "projection": "Orthographic", "clip": true, "friction": .9, "linkStrength": 1, "linkDistance": 20, "charge": 30, "gravity": .1, "theta": .8 }; var gui = new dat.GUI(); //var projectionChanger = gui.add(config, "projection", ['equalarea', 'equidistant', 'gnomonic', 'orthographic', 'stereographic', 'rectangular']); var projectionChanger = gui.add(config, "projection", Object.keys(projections)); //http://stackoverflow.com/a/3417242 function wrapIndex(i, i_max) { return ((i % i_max) + i_max) % i_max; } projectionChanger.onChange(function(value) { projection = projections[value] .scale(height / 2) .translate([(width / 2) - 125, height / 2]) .clipAngle(config["clip"] ? 90 : null) path.projection(projections[value]) return if (value == 'rectangular') { path = d3.geo.path().projection(function(coordinates) { console.log(coordinates[0], coordinates[1]) return [ wrapIndex(coordinates[0], width), wrapIndex(coordinates[1], height), ]; }); config['clip'] = false } else { projection.mode(value) path = d3.geo.path().projection(projection) } force.start() }); var clipChanger = gui.add(config, "clip").listen(); clipChanger.onChange(function(value) { projection.clipAngle(value ? 90 : null) force.start() }); var fl = gui.addFolder('Force Layout'); fl.open() var frictionChanger = fl.add(config, "friction", 0, 1); frictionChanger.onChange(function(value) { force.friction(value) force.start() }); var linkDistanceChanger = fl.add(config, "linkDistance", 0, 400); linkDistanceChanger.onChange(function(value) { force.linkDistance(value) force.start() }); var linkStrengthChanger = fl.add(config, "linkStrength", 0, 1); linkStrengthChanger.onChange(function(value) { force.linkStrength(value) force.start() }); var chargeChanger = fl.add(config, "charge", 0, 500); chargeChanger.onChange(function(value) { force.charge(-value) force.start() }); var gravityChanger = fl.add(config, "gravity", 0, 1); gravityChanger.onChange(function(value) { force.gravity(value) force.start() }); var thetaChanger = fl.add(config, "theta", 0, 1); thetaChanger.onChange(function(value) { force.theta(value) force.start() }); var width = window.innerWidth, height = window.innerHeight - 5, fill = d3.scale.category20(), nodes = [{ x: width / 2, y: height / 2 }], links = []; var projection = projections[config["projection"]] .scale(height / 2) .translate([(width / 2) - 125, height / 2]) .clipAngle(config["clip"] ? 90 : null) var path = d3.geo.path() .projection(projection) var force = d3.layout.force() .linkDistance(config["linkDistance"]) .linkStrength(config["linkStrength"]) .gravity(config["gravity"]) .size([width, height]) .charge(-config["charge"]); var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height) .call(d3.behavior.drag() .origin(function() { var r = projection.rotate(); return { x: 2 * r[0], y: -2 * r[1] }; }) .on("drag", function() { force.start(); var r = [d3.event.x / 2, -d3.event.y / 2, projection.rotate()[2]]; t0 = Date.now(); origin = r; projection.rotate(r); })) var node, link; d3.json('https://jsonblob.com/api/547d877a-0e4e-11e7-a0ba-f11a5a82ba29', function(e, data) { if (e) console.warn(e); link = svg.selectAll("path.link") .data(data.links) .enter().append("path").attr("class", "link") node = svg.selectAll("path.node") .data(data.nodes) .enter().append("path").attr("class", "node") .style("fill", function(d) { return fill(d.group); }) .style("stroke", function(d) { return d3.rgb(fill(d.group)).darker(); }) .call(force.drag); force .nodes(data.nodes) .links(data.links) .on("tick", tick) .start(); }); function tick() { node.attr("d", function(d) { var p = path({ "type": "Feature", "geometry": { "type": "Point", "coordinates": [dx, dy] } }); return p ? p : 'M 0 0' }); link.attr("d", function(d) { var p = path({ "type": "Feature", "geometry": { "type": "LineString", "coordinates": [ [d.source.x, d.source.y], [d.target.x, d.target.y] ] } }); return p ? p : 'M 0 0' }); } </script> </body> </html> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM