Echarts5.* 关系图谱(relation graph)添加节点拖拽、点击节点高亮效果

前言

图表的需求为:

  • 展现不同节点之间的联系,比如,公司为节点A,张三为节点B,它们之间的联线为雇佣,这些信息皆需展示出来
  • 点击节点A,只会高亮与它相关的节点
  • 单个节点可拖拽

echartsg6权衡了一段时间,最终选定echarts,原因无非以下两种

  • echarts文档齐全,上手简单
  • echarts使用人更多一些,遇到问题可在网上找到对应方案

绘制节点,显示信息

部分节点与连线的原始数据信息如下所示:

var nodes = [
	{
		id: 0,
		labels: ["Building"],
		properties: {
			name: "辉隆大厦",
			building_id: "4a9387792719s"
		}
	}
	...
]

var links = [
	{
		id: 0,
		source: 0,
		target: 10,
		type: "HIRE",
		properties: {
			relation: "雇佣"
		}
	}
]

注意点:在echarts的关系图里,id必须为字符串,否则节点不会渲染。

随机生成-600~800之间的坐标点,代码如下所示:

function generateRandomNum() {
    const startNumber = -600
    const endNumber = 800
    var choice = endNumber - startNumber + 10;
    return Math.floor(Math.random() * choice + startNumber)
}

处理原始数据,显示节点信息,及连线上的文字信息,代码如下所示:

nodes = nodes.map(a => {
	const {labels, id, properties} = a
	const name = labels[0]
	const { category, symbolSize, value } = this.initSingleNodeParam(name, properties)
    const showName = properties.name === 'None' ? properties.label : (properties.name || properties.component_name || properties.label)
	return {
		id: String(id),
        name: showName,
        symbolSize,
        x: generateRandomNum(),
        y: generateRandomNum(),
        label: {
        	show: true
        },
        type: name,
        value,
        category,
        properties
	}
})

links = links.map(a => {
    return {
        source: String(a.source),
        target: String(a.target),
        label: {
            normal: {
                show: true,
                formatter: a.properties.relation
            }
        }
    }
})

initSingleNodeParam这个函数根据不同的节点类型,返回不同的categorysymbolSizevalue信息,部分代码如下所示:

initSingleNodeParam(name, properties) {
    let category = 0
    let symbolSize = 40
    let value = `类别:楼栋`
    if (name === 'Room') {
        category = 1
        symbolSize = 32
        value = `类别:${properties.label}`
    } else if (name === 'Company') {
        category = 2
        symbolSize = 32
        value = `类别:${properties.label}`
    } else if (name === 'Person') {
        ...
    }
    return {
        category,
        symbolSize,
        value
    }
}

将参数配置到option内,初始化图表

const categories = [
    {
        name: 'A'
    }, {
        name: 'B'
    }, {
        name: 'C'
    }, {
        name: 'D'
    }, {
        name: 'E'
    }, {
        name: 'F'
    }
]


var option = {
    tooltip: {},
    animationDuration: 1500,
    animationEasingUpdate: 'quinticInOut',
    hoverAnimation:false,
    series: [
        {
            // name: '孪生',
            type: 'graph',
            layout: 'none',
            circular:{rotateLabel:true},
            animation: false,
            data: nodes,
            links: links,
            categories: categories,
            roam: true,
            draggable: false,
            label: {
                position: 'right',
                formatter: '{b}'
            },
            lineStyle: {
                color: 'source',
                curveness: 0.3
            },
            emphasis: {
                focus: 'adjacency',
                lineStyle: {
                    width: 5,
                    color: "#ffff00"
                }
            },
            draggable: true,
            hoverAnimation:false
        }
    ]
};
myChart.setOption(option);

节点可拖拽

echarts官网推荐的办法是,在每个节点上创建一个circle,拖动circle时,实时更新节点的坐标位置即可,切记:下面的方法需在myChart.setOption(option)之后执行

myChart.setOption({
    graphic: echarts.util.map(option.series[0].data, function (item, dataIndex) {
        //使用图形元素组件在节点上划出一个隐形的图形覆盖住节点
        var tmpPos=myChart.convertToPixel({'seriesIndex': 0},[item.x,item.y]);
        return {
            type: 'circle',
            id:String(item.id),
            position: tmpPos,
            shape: {
                cx: 0,
                cy: 0,
                r: 20
            },
            // silent:true,
            invisible: true,
            draggable: true,
            properties: item.properties,
            nodeType: item.type,
            dataIndex,
            ondrag: echarts.util.curry(onPointDragging, dataIndex),
            z: 100              //使图层在最高层
        };
    })
});
window.addEventListener('resize', updatePosition);
myChart.on('dataZoom', updatePosition);
myChart.on('graphRoam', updatePosition);

function updatePosition() {    //更新节点定位的函数
    myChart.setOption({
        graphic: echarts.util.map(option.series[0].data, function (item, dataIndex) {
            var tmpPos=myChart.convertToPixel({'seriesIndex': 0},[item.x,item.y]);
            return {
                position: tmpPos
            };
        })
    });

}
function onPointDragging(dataIndex) {      //节点上图层拖拽执行的函数
    var tmpPos=myChart.convertFromPixel({'seriesIndex': 0},this.position);
    option.series[0].data[dataIndex].x = tmpPos[0];
    option.series[0].data[dataIndex].y = tmpPos[1];
    myChart.setOption(option);
    updatePosition();
}

添加完上面的方法后,发现一个问题,只能在界面初始化拖动节点,一旦鼠标释放,便再也不能拖动任意节点了。最开始,将echarts版本降至4.0.0,发现可任意拖动节点(但不能高亮节点),后面,将echarts版本固定在5.0.0,便无该问题了,需注意的是,安装echarts时,需指定5.0.0版本

npm install echarts@5.0.0

节点可点击高亮

由于节点本身具备鼠标移入高亮事件,需在节点点击高亮后,特意屏蔽节点mouseover功能,代码如下所示:

myChart.on("click", params => {
    const { properties, dataIndex, nodeType } = params.event.target
    this.prevIndex = dataIndex
    this.highlight(dataIndex, myChart)
    const entityProperties = {
        ...properties,
        type: nodeType
    }
    this.$store.commit("jobInstance/SET_HIGHLIGHTENTITY", entityProperties);
})
myChart.on("mouseout", _ => {
    if (this.prevIndex === null) return
    this.highlight(this.prevIndex, myChart)
})
myChart.on("mouseover", _ => {
    myChart.dispatchAction({
        type: 'downplay'
    });
})

highlight函数代码如下所示:

highlight(index, myChart) {
    myChart.dispatchAction({
        type: "highlight",
        dataIndex: index
    })
}

有任何问题,欢迎大家留言讨论

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: 要统计py2neo知识图谱中每个节点及其关系的频次,需要使用Cypher查询语言。以下是一个示例查询语句,可以帮助您获取每个节点及其关系的频次: ``` MATCH (n)-[r]->() RETURN labels(n) AS node_type, type(r) AS relationship_type, count(*) AS frequency ORDER BY frequency DESC ``` 上述查询语句将返回一个结果集,其中包含每个节点的标签、每个关系的类型以及对应的频次。请注意,这个查询语句假定每个节点都有一个标签(即类别),如果您的图中存在没有标签的节点,则需要对查询语句进行相应的修改。 此外,如果您想进一步筛选结果,可以添加WHERE子句来限定查询范围,例如: ``` MATCH (n)-[r]->() WHERE labels(n) = 'Person' RETURN labels(n) AS node_type, type(r) AS relationship_type, count(*) AS frequency ORDER BY frequency DESC ``` 上述查询语句将只返回标签为“Person”的节点及其关系的频次。 ### 回答2: Py2neo是Python中的一个库,用于操作Neo4j图数据库。在知识图谱中,每个节点都代表一个实体,而这些实体之间的关系可以通过边来表示。每个节点关系都可以有自己的属性,例如类别、频次等。要计算每个节点关系的频次,首先需要对图谱中的节点关系进行遍历。 首先,我们可以通过Py2neo库连接到Neo4j数据库,并查询所有的节点关系。然后,我们可以使用Cypher查询语言对图谱进行遍历,计算每个节点关系的频次。 下面是一个示例代码: ```python from py2neo import Graph, NodeMatcher # 连接到Neo4j数据库 graph = Graph("bolt://localhost:7687", auth=("username", "password")) # 创建节点匹配器 matcher = NodeMatcher(graph) # 存储节点关系频次的字典 relation_freq = {} # 查询所有节点关系 nodes = matcher.match() # 查询所有节点 relationships = graph.relationships # 查询所有关系 # 遍历所有节点 for node in nodes: node_type = node.get("type") # 获取节点类别 # 初始化节点类别的关系频次为0 if node_type not in relation_freq: relation_freq[node_type] = {} # 遍历所有关系 for relationship in relationships: start_node = relationship.start_node # 获取关系的开始节点 # 如果开始节点类型等于当前节点类型 if start_node.get("type") == node_type: rel_type = relationship.type() # 获取关系的类别 # 更新该节点类别的关系频次 if rel_type not in relation_freq[node_type]: relation_freq[node_type][rel_type] = 1 else: relation_freq[node_type][rel_type] += 1 # 打印节点类别的关系频次 for node_type, rel_freq in relation_freq.items(): print("节点类别: {}".format(node_type)) for rel_type, freq in rel_freq.items(): print("关系类别: {}, 频次: {}".format(rel_type, freq)) print("\n") ``` 以上代码中,我们首先连接到Neo4j数据库,然后创建一个节点匹配器,用于查询所有节点。通过遍历所有节点关系,我们可以获取节点的类别和关系的类别,并计算每个节点关系的频次。最后,我们打印出节点类别的关系频次。 请注意,此代码仅做示例用途,实际情况可能会因数据库结构和数据模型的不同而有所差异。请根据实际需求进行适当的修改。 ### 回答3: py2neo是一个用于构建和操作Neo4j图数据库的Python库。知识图谱是一种用于表示和组织知识的图状数据结构。在py2neo中,每个节点都可以有一个类别标签,用于指定节点的类型。节点之间的关系也可以被分类别标记,以指定不同关系之间的语义含义。 通过py2neo可以方便地计算每个节点关系频次。以下是一个简单的示例代码,用于统计节点关系的频次: ```python from py2neo import Graph # 连接到Neo4j数据库 graph = Graph("<你的Neo4j数据库连接地址>") # 查询节点的类别和关系的分类别频次 node_freq = graph.run("MATCH (n) RETURN DISTINCT labels(n) AS label, count(*) AS freq") rel_freq = graph.run("MATCH ()-[r]->() RETURN DISTINCT type(r) AS type, count(*) AS freq") # 打印节点的类别和关系的分类别频次 print("节点类别频次:") for record in node_freq: node_label = record["label"] freq = record["freq"] print(f"{node_label}: {freq}") print("\n关系分类别频次:") for record in rel_freq: rel_type = record["type"] freq = record["freq"] print(f"{rel_type}: {freq}") ``` 该代码会查询数据库中所有节点的类别和关系的分类别,并计算他们的频次。最后,会打印出节点类别频次和关系分类别频次。 需要注意的是,上述代码仅提供了一个简单的示例,具体的查询语句和统计方法可以根据实际需求进行修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值