问题
I am using vis.js to create network graphs on a web page.
My need is to store manipulated graphs to a database in JSON format, exporting the json from the network graph.
I did not find any documentation about it, is it feasible to export vis.js network with manipulated data for storage (in either JSON or in a form convertable into JSON)?
回答1:
As for data, here's my hacky way to extract it for storage (I'll try to cut off code bits irrelevant to the question):
// get nodes and edges
var nodes = network.body.data.nodes._data; // contains id, label, x,y, custom per-node options and doesn't contain options from options.nodes; presumably contains option values set when network was created, not current ones (it is so for x,y)
// network.body.nodes[id].nodeOptions shows options from options.nodes but not custom per-node options (same for edges and network.body.edges[id].edgeOptions)
// network.body.nodes contain much more stuff (x,y, default stuff)
//# look for a suitable getter
var edges = network.body.data.edges._data; // map; for edges to/from? certain node use network.getConnectedNodes(id)
// network.body.data.edges._data is a hash of { id: , from: , to: }
// get node positions
var positions = network.getPositions(),
nodeIds = Object.keys(nodes);
// get data describing nodes, edges and options for storage
var storedEdges = [], storedEdge, storedNodes = [], storedNode;
var indexIds = {}, idIndex = 1, end;
for(var nodeId in nodes) {
// nodes[nodeId].x is the initial value, positions[nodeId].x is the current one
if(positions[nodeId]) { // undefined for hidden
nodes[nodeId].x = positions[nodeId].x;
nodes[nodeId].y = positions[nodeId].y;
}
storedNode = copyObjectProperties(nodes[nodeId]);
// don't store id unless that breaks connected edges
if(!network.getConnectedEdges(nodeId).length)
storedNode.id = undefined;
// substitute generated ids with no semantics with simple indices
if(/\w{8}-\w{4}-\w{4}-\w{4}-\w{12}/.exec(storedNode.id)) {
while(nodes[idIndex])
idIndex++;
indexIds[storedNode.id] = idIndex; // remember the given index
storedNode.id = idIndex; // substitute with an index
idIndex++;
}
storedNodes.push(storedNode);
}
for(var edgeId in edges) {
storedEdge = copyObjectProperties(edges[edgeId]);
storedEdge.id = undefined; // then strip id
// change from/to in accord to the substitution above (for nodes' ids)
for(end of ["from","to"])
storedEdge[end] = indexIds[storedEdge[end]] || storedEdge[end];
storedEdges.push(storedEdge);
}
dataAndOptions = {
data: { nodes: storedNodes, edges: storedEdges },
options: storedOptions
};
var dataAndOptionsText = JSON.stringify(dataAndOptions,"",4)
.replace(/ {4}/gm,"\t").replace(/},\n\t\t\t{/gm,"},{");
and helper definition:
// helper for storing options
var copyObjectProperties = function(obj) {
return JSON.parse(JSON.stringify(obj));
};
For more context, see my plugin for TiddlyWiki Classic (saveDataAndOptions
method). It's not the latest version, but I'll update it at some point.
As for network options (if they were changed), I haven't figured a nice way yet.
回答2:
According to Visjs documents, the method you might want is storePositions()
this is the the function description:
When using the vis.DataSet to load your nodes into the network, this method will put the X and Y positions of all nodes into that dataset. If you're loading your nodes from a database and have this dynamically coupled with the DataSet, you can use this to stablize your network once, then save the positions in that database through the DataSet so the next time you load the nodes, stabilization will be near instantaneous.
If the nodes are still moving and you're using dynamic smooth edges (which is on by default), you can use the option stabilization.onlyDynamicEdges in the physics module to improve initialization time.
This method does not support clustering. At the moment it is not possible to cache positions when using clusters since they cannot be correctly initialized from just the positions.
VisJs docs
回答3:
tl;dr
Use storePositions() to load the X and Y coordinates to your dataset. You can serialize them, then just expand the nodes with the serialized coordinates when you initialize the network later.
Explanation
The vis.js Network docs on storePositions() says:
When using the vis.DataSet to load your nodes into the network, this method will put the X and Y positions of all nodes into that dataset. If you're loading your nodes from a database and have this dynamically coupled with the DataSet, you can use this to stabilize your network once, then save the positions in that database through the DataSet so the next time you load the nodes, stabilization will be near instantaneous.
Saving
You have to use vis.js's DataSet for your network.data
. To "save", just call network.storePositions()
so it can load the X and Y coordinates to network.data.nodes
then you serialize it however you want.
Loading
You just forEach() your network.data.nodes
and add in the serialized X and Y coordinates to its nodes via update().
Example
In this demo, the positions are serialized into a textarea
. You can generate randomly positioned graphs (that is the default behavior), move the nodes, serialize them, optionally edit it in the textarea
, then load it back.
const nodes = [
{ id: 1, label: 1 },
{ id: 2, label: 2 },
{ id: 3, label: 3 },
{ id: 4, label: 4 },
]
const edges = [
{ id: '1-2', from: 1, to: 2 },
{ id: '1-3', from: 1, to: 3 },
{ id: '2-3', from: 2, to: 3 },
{ id: '1-4', from: 1, to: 4 },
]
const positionsElement = document.getElementById('positions')
const container = document.getElementById('graph')
const data = {
nodes: new vis.DataSet(nodes),
edges: new vis.DataSet(edges),
}
const options = {
layout: {
improvedLayout: false,
},
edges: {
smooth: false,
},
physics: false,
}
let network = null
function initGraph(generateRandomPosition = true) {
if (generateRandomPosition) {
data.nodes.forEach(node => {
data.nodes.update({ id: node.id, x: undefined, x: undefined })
})
}
network = new vis.Network(container, data, options)
}
document.getElementById('generate-graph').addEventListener('click', initGraph)
document.getElementById('extract-positions').addEventListener('click', e => {
network.storePositions()
const nodePositions = data.nodes.map(({ id, x, y }) => ({ id, x, y }))
positionsElement.value = JSON.stringify(nodePositions)
})
document.getElementById('load-positions').addEventListener('click', e => {
const nodePositions = JSON.parse(positionsElement.value)
nodePositions.forEach(nodePosition => data.nodes.update(nodePosition))
initGraph(false)
})
#graph {
width: 100%;
height: 300px;
border: 1px solid lightgray;
}
#positions {
width: 100%;
min-height: 60px;
}
<link href="https://visjs.github.io/vis-network/dist/vis-network.min.css" rel="stylesheet" />
<script src="https://visjs.github.io/vis-network/dist/vis-network.min.js"></script>
<div id="graph"></div>
<button id="generate-graph">Generate graph</button>
<button id="extract-positions">Extract positions</button>
<button id="load-positions">Load positions</button>
<textarea id="positions"></textarea>
来源:https://stackoverflow.com/questions/40489700/visjs-save-manipulated-data-to-json