Python 中的球形图布局

问题描述 投票:0回答:2

目标

目标:

enter image description here

工作状态

  • 输入给定因子的数据
  • 用于位置计算的NetworkX
  • 切换到 VTK 方法进行 3D 可视化

问题

3年前,我已经实现了如上图所示的可视化。不幸的是,我做了太多的清洁工作,我才意识到,我不再有这些方法了。它在某种程度上是球体表面上的力导向图。可能类似于二维力图集中的“强重力”参数。我还没有找到任何 3D 实现。

我再次尝试了以下算法,但没有一个产生这种布局,也没有对这些算法进行参数调整(或者我错过了一个重要的算法?):

  • NetworkX:Spherical、Spring、Shell、Kamada Kawaii、Fruchterman-Reingold(Gephi 中的 2D fruchterman-reingold 看起来可以接近 3D 版本中的目标,但 gephi 不支持 3D 还是我监督了什么?)
  • ForceAtlas2
  • Gephi(2D fruchterman-reingold 看起来像一个圆圈,但这在 3D 中不可用,3D Force Atlas 也不会生成有效的 Z 坐标(它们在 +1e-4 和 -1e-4 的范围内)

研究“球形图布局”并没有给我带来任何进展(仅对这个看起来非常相似的视图https://observablehq.com/@fil/3d-graph-on-sphere)。

如何使用Python(或提供定位信息的第三方)实现这种球形布局

更新:我取得了一些进展,找到了关键字非欧几里德、双曲和球面力导向算法,但仍然没有取得任何成果。或非欧几里德黎曼嵌入 (https://www2.cs.arizona.edu/~kobourov/riemann_embedders.pdf)

python graph 3d
2个回答
0
投票

您尝试过 GSPBOX 的 python lib 版本吗? 如果是,为什么它对你不起作用?

https://pygsp.readthedocs.io/en/stable/reference/graphs.html


0
投票

我所做的是使用networkx进行绘图,然后将其生成一个KML文件,然后将其加载到google Earth中。 人们可以轻松地将其生成到其他 GIS 工具中。

因为我使用了网络的一些预设值,所以我发现单独运行脚本并存储中间 json 对象更容易。

这是 nx.kamada_kawai_layout 正在完成的工作

import json
import networkx as nx

if __name__ == '__main__':
    print(f'loading net')
    # cf. networkx for the format. but here is what I am mainly doing.
    #  {"directed": false, "multigraph": false, "graph": {},
    #  "nodes": [{"id": 0}, ...],
    #  "links": [{"source": 0, "target": 1}, ...]}
    net_f = open(f'graph.json', 'r')  # load the graph definition.
    net = json.load(net_f)
    net_f.close()
    print(f'setting network')
    graph = nx.node_link_graph(net, edges="links")

    print(f'loading initial positions')
    # These points are normalised on the unit sphere.
    # {"0": [-1.0, 0.0, 0.0], "1": [-0.866, -0.4999, 0.0] .. etc}
    pos_f = open(f'pos.json', 'r')
    pos_n = json.load(pos_f)
    pos_f.close()
    print(f'setting initial positions')
    pos = {int(k): v for k, v in pos_n.items()}

    print(f'invoking kamada_kawai')
    k_pos = nx.kamada_kawai_layout(graph, pos=pos, dim=3)

    print(f'storing solved positions')
    f = open(f'solved_pos.json', 'w')
    #  {"0": [-0.903, -0.119, 5.309], ... etc }
    f.write(json.dumps({k: tuple(v) for k, v in k_pos.items()}))
    f.close()
    print(f'finished')

然后要转换为 KMZ,我将需要原始计算中的多边形信息 - 这仅用于显示目的,并且仅仅是每个周期中点的索引。我想,可以添加线条等。

还需要将单位球面转换为纬度和经度。 有很多 GIS 功能可以实现这一点,或者如下所示..

为了转换为kml,我使用simpleKML。 KMZ 需要一些奇怪的调整来环绕经度边界。因此多边形加法作为函数进行管理。

import json
import simplekml
import numpy

def xyz_ll(cls, xyz: (tuple | list)) -> tuple:
        x, y, z = xyz
        return float(np.degrees(np.arctan2(z, np.sqrt(x * x + y * y)))), float(np.degrees(np.arctan2(y, x)))

def add_poly(kz, pts, idx, name=None, col=(154, 154, 154), alpha=0.5, width=4):
    tpt = [pts[i] for i in idx]
    lo = [i[1] for i in tpt]
    mim = max(lo) - min(lo)
    if mim > 179.9999:
        path = [(o if o >= 0 else o + 360.0000, a) for (a, o) in tpt]
    else:
        path = [(o, a) for (a, o) in tpt]
    pol = kz.newpolygon(name=name)
    pol.outerboundaryis = path
    pol.style.polystyle.fill = 0
    pol.style.polystyle.outline = 1
    pol.style.linestyle.width = width
    if col:
        is_i = all([(i.is_integer() or i > 1) for i in col])
        (r, g, b) = [int(i) if is_i else int((i*255)) for i in col]
        a = int(255 * alpha)
        pol.style.polystyle.color = simplekml.Color.rgb(r, g, b, a)


if __name__ == '__main__':
    pos_f = open(f'solved_pos.json', 'r')
    pos_n = json.load(pos_f)
    pos_f.close()
    pos = dict()
    for k, v in pos_n.items():
        key = int(k)
        pos[key] = tuple(sp.xyz_ll(tuple(v)))
    # polygons are just a list of lists..
    # each polygon has a name, and the point ids..
    # eg [["04a", [13, 25, 28, 16, 13]], ...] 
    pol_f = open(f'poly.json', 'r')
    polys = json.load(pol_f)
    pol_f.close()
    print(f'generate kml {depth}')
    kml = simplekml.Kml(open=1)
    for poly in polys:
        add_poly(kml, pos, poly[1], poly[0])
    kml.save(f'graph.kml')
    print(f'kml file generated')

正在将文件加载到谷歌地球...

network on google earth

© www.soinside.com 2019 - 2024. All rights reserved.