Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save positions for reproducibility? #157

Open
tsp-kucbd opened this issue Jun 30, 2022 · 6 comments
Open

Save positions for reproducibility? #157

tsp-kucbd opened this issue Jun 30, 2022 · 6 comments
Labels
enhancement New feature or request

Comments

@tsp-kucbd
Copy link

Is it possible to save or dump the node positions once they have been drawn and physics is turned off?
It would be great to be able to store each nodes coordinates and then reuse it for subsequent drawings of the same graph.

@SPA20
Copy link

SPA20 commented Jul 4, 2022

I have been looking for this information for a year. So far no luck!
I wish somebody answer it and if this feature not available, whether there is any roadmap for new features.

@tsp-kucbd
Copy link
Author

I assume that one could access them with network.storePositions() as mentioned here
But I have no idea if that should be added before or after network = new vis.Network(container, data, options); in the resulting graph file or somewhere else ....

@jhunpingco jhunpingco added the enhancement New feature or request label Aug 30, 2022
@tsp-kucbd
Copy link
Author

tsp-kucbd commented Oct 24, 2022

My current solution is a bit clumsy, but it works for me to get the positions after I moved the nodes around.
After displaying the html file and turning off the physics, open a javascript console I the browser and paste following javascript code

function download(content, fileName, contentType) {
    var a = document.createElement("a");
    var file = new Blob([content], {type: contentType});
    a.href = URL.createObjectURL(file);
    a.download = fileName;
    a.click();
}
network.storePositions();
download(JSON.stringify(data.nodes.get()), 'positions.txt', 'text/plain');

That will download the information for all nodes, including their positions (x,y) which then can be added to the graph in the python code:

import json
file = os.path.expanduser('~/Downloads/positions.txt')

d = json.load(open(file))
X = {x['node_name']:x['x'] for x in d}
Y = {x['node_name']:x['y'] for x in d}

nx.set_node_attributes(G, X, 'x')
nx.set_node_attributes(G, Y, 'y')

@SPA20
Copy link

SPA20 commented Oct 27, 2022

Do you have a similar code in Python ? I am not familiar with much Java scripting.
I am trying to accomplish the same using the module =
"from pyvis.network import Network"

Thank you very much.

@ConorJurewicz
Copy link

My current solution is a bit clumsy, but it works for me to get the positions after I moved the nodes around. After displaying the html file and turning off the physics, open a javascript console I the browser and paste following javascript code

function download(content, fileName, contentType) {
    var a = document.createElement("a");
    var file = new Blob([content], {type: contentType});
    a.href = URL.createObjectURL(file);
    a.download = fileName;
    a.click();
}
network.storePositions();
download(JSON.stringify(data.nodes.get()), 'positions.txt', 'text/plain');

That will download the information for all nodes, including their positions (x,y) which then can be added to the graph in the python code:

import json
file = os.path.expanduser('~/Downloads/positions.txt')

d = json.load(open(file))
X = {x['node_name']:x['x'] for x in d}
Y = {x['node_name']:x['y'] for x in d}

nx.set_node_attributes(G, X, 'x')
nx.set_node_attributes(G, Y, 'y')

This is so helpful. Thanks!!

@adamryczkowski
Copy link

adamryczkowski commented Jan 31, 2024

Here's a fully non-interactive follow-up of the walkaround that @tsp-kucbd proposed.

from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromiumService
from webdriver_manager.chrome import ChromeDriverManager
from webdriver_manager.core.os_manager import ChromeType


def get_nx_coordinates(file_path) -> dict[str, tuple[float, float]]:
    drivermanager = ChromeDriverManager(chrome_type=ChromeType.CHROMIUM).install()
    service = ChromiumService(drivermanager)
    driver = webdriver.Chrome(service=service)

    driver.get(file_path)
    driver.implicitly_wait(0.1)

    # Execute a command `data.nodes.get()` in the browser console and return the result
    ans = driver.execute_script("network.storePositions();return data.nodes.get();")
    driver.quit()

    mean_x = sum(e["x"] for e in ans) / len(ans)
    mean_y = sum(e["y"] for e in ans) / len(ans)

    min_x = min(e["x"] for e in ans)
    min_y = min(e["y"] for e in ans)
    max_x = max(e["x"] for e in ans)
    max_y = max(e["y"] for e in ans)

    span_x = max_x - min_x
    span_y = max_y - min_y

    coords = {e["id"]: ((e["x"] - mean_x) / span_x, (e["y"] - mean_y) / span_y) for e in ans}

    return coords

It is also inspired by the pyvis unit tests.

Please note, that this solution requires selenium (pip install selenium) and for my convenience also the webdriver-manager (pip install webdriver-manager).

For Ubuntu users: Selenium would not work with Chromium, if Chromium was installed via snap (which is the default). You may want to remove it, and then install it again, this time using sudo add-apt-repository ppa:savoury1/chromium and also possibly sudo add-apt-repository ppa:savoury1/ffmpeg.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

5 participants