# -*- coding: utf-8 -*-
from neo4j import GraphDatabase
import pandas as pd
import plotly.graph_objects as go
import networkx as nx
# Neo4j连接配置
URI = "neo4j://localhost:7687"
AUTH = ("neo4j", "lzx040106") # 替换为你的密码
# 中国省会城市及相邻关系数据
cities_data = [
{"name": "北京", "latitude": 39.9042, "longitude": 116.4074},
{"name": "天津", "latitude": 39.0842, "longitude": 117.3610},
{"name": "上海", "latitude": 31.2304, "longitude": 121.4737},
{"name": "重庆", "latitude": 29.5630, "longitude": 106.5516},
{"name": "哈尔滨", "latitude": 45.8038, "longitude": 126.5350},
{"name": "长春", "latitude": 43.8171, "longitude": 125.3235},
{"name": "沈阳", "latitude": 41.8057, "longitude": 123.4315},
{"name": "呼和浩特", "latitude": 40.8426, "longitude": 111.7492},
{"name": "石家庄", "latitude": 38.0422, "longitude": 114.5149},
{"name": "太原", "latitude": 37.8706, "longitude": 112.5489},
{"name": "西安", "latitude": 34.3416, "longitude": 108.9398},
{"name": "济南", "latitude": 36.6512, "longitude": 117.1201},
{"name": "郑州", "latitude": 34.7466, "longitude": 113.6253},
{"name": "合肥", "latitude": 31.8206, "longitude": 117.2272},
{"name": "南京", "latitude": 32.0603, "longitude": 118.7969},
{"name": "武汉", "latitude": 30.5928, "longitude": 114.3052},
{"name": "长沙", "latitude": 28.2282, "longitude": 112.9388},
{"name": "南昌", "latitude": 28.6820, "longitude": 115.8579},
{"name": "杭州", "latitude": 30.2741, "longitude": 120.1551},
{"name": "福州", "latitude": 26.0745, "longitude": 119.2965},
{"name": "广州", "latitude": 23.1291, "longitude": 113.2644},
{"name": "南宁", "latitude": 22.8170, "longitude": 108.3660},
{"name": "海口", "latitude": 20.0440, "longitude": 110.1999},
{"name": "昆明", "latitude": 24.8801, "longitude": 102.8329},
{"name": "贵阳", "latitude": 26.6470, "longitude": 106.6302},
{"name": "成都", "latitude": 30.5728, "longitude": 104.0668},
{"name": "拉萨", "latitude": 29.6535, "longitude": 91.1705},
{"name": "兰州", "latitude": 36.0611, "longitude": 103.8343},
{"name": "银川", "latitude": 38.4872, "longitude": 106.2309},
{"name": "西宁", "latitude": 36.6232, "longitude": 101.7788},
{"name": "乌鲁木齐", "latitude": 43.8256, "longitude": 87.6168}
]
# 相邻城市关系 (基于地理相邻的省份)
relationships = [
("北京", "天津", 120),
("北京", "石家庄", 280),
("天津", "石家庄", 300),
("哈尔滨", "长春", 240),
("长春", "沈阳", 300),
("沈阳", "呼和浩特", 800),
("呼和浩特", "石家庄", 500),
("石家庄", "太原", 200),
("石家庄", "济南", 300),
("太原", "西安", 600),
("西安", "郑州", 500),
("郑州", "合肥", 500),
("郑州", "武汉", 500),
("武汉", "长沙", 350),
("长沙", "广州", 600),
("广州", "南宁", 500),
("南宁", "昆明", 600),
("昆明", "贵阳", 500),
("贵阳", "成都", 700),
("成都", "西安", 700),
("西安", "兰州", 600),
("兰州", "银川", 400),
("银川", "呼和浩特", 700),
("兰州", "西宁", 200),
("西宁", "拉萨", 1800),
("兰州", "乌鲁木齐", 1800),
("合肥", "南京", 150),
("南京", "上海", 300),
("上海", "杭州", 180),
("杭州", "南昌", 500),
("南昌", "福州", 500),
("福州", "广州", 700),
("南昌", "长沙", 400),
("武汉", "南昌", 300),
("武汉", "南京", 500),
("济南", "合肥", 500)
]
# 创建图数据库
def create_graph(tx):
# 清空现有数据
tx.run("MATCH (n) DETACH DELETE n")
# 创建城市节点
for city in cities_data:
tx.run("""
CREATE (:City {
name: $name,
latitude: $latitude,
longitude: $longitude
})
""", **city)
# 创建城市间关系
for rel in relationships:
tx.run("""
MATCH (a:City {name: $city1})
MATCH (b:City {name: $city2})
MERGE (a)-[:ROAD {distance: $dist}]->(b)
MERGE (b)-[:ROAD {distance: $dist}]->(a)
""", city1=rel[0], city2=rel[1], dist=rel[2])
# 执行创建
with GraphDatabase.driver(URI, auth=AUTH) as driver:
with driver.session() as session:
session.execute_write(create_graph)
print("图数据库创建完成!")
def setup_gds_and_find_shortest_path(city1, city2):
with GraphDatabase.driver(URI, auth=AUTH) as driver:
with driver.session() as session:
# 先删除已存在的图投影
session.run("""
CALL gds.graph.exists('cityGraph') YIELD exists
WHERE exists
CALL gds.graph.drop('cityGraph') YIELD graphName
RETURN graphName
""")
# 创建图投影
session.run("""
CALL gds.graph.project(
'cityGraph',
'City',
{
ROAD: {
orientation: 'UNDIRECTED',
properties: 'distance'
}
}
)
""")
# 计算最短路径
result = session.run("""
MATCH (source:City {name: $city1})
MATCH (target:City {name: $city2})
CALL gds.shortestPath.dijkstra.stream('cityGraph', {
sourceNode: source,
targetNode: target,
relationshipWeightProperty: 'distance'
})
YIELD index, sourceNode, targetNode, totalCost, nodeIds, path
RETURN
totalCost AS totalDistance,
[node IN nodes(path) | node.name] AS pathCities
""", city1=city1, city2=city2)
record = result.single()
if record:
print(f"从 {city1} 到 {city2} 的最短路径:")
print(" -> ".join(record["pathCities"]))
print(f"总距离: {record['totalDistance']} 公里")
visualize_path_on_map(record["pathCities"], cities_data, relationships)
else:
print("未找到路径")
# 清理图投影
session.run("CALL gds.graph.drop('cityGraph')")
def visualize_path_on_map(path_cities, cities, relationships):
# 创建地图基础
fig = go.Figure()
# 创建城市字典
city_dict = {city['name']: (city['latitude'], city['longitude']) for city in cities}
# 绘制所有道路
for rel in relationships:
city1, city2, dist = rel
lat1, lon1 = city_dict[city1]
lat2, lon2 = city_dict[city2]
fig.add_trace(go.Scattergeo(
lon=[lon1, lon2],
lat=[lat1, lat2],
mode='lines',
line=dict(width=1, color='gray'),
opacity=0.5,
showlegend=False
))
# 绘制最短路径
for i in range(len(path_cities) - 1):
city1 = path_cities[i]
city2 = path_cities[i + 1]
lat1, lon1 = city_dict[city1]
lat2, lon2 = city_dict[city2]
fig.add_trace(go.Scattergeo(
lon=[lon1, lon2],
lat=[lat1, lat2],
mode='lines',
line=dict(width=3, color='red'),
showlegend=False
))
# 添加所有城市点
lats = [city['latitude'] for city in cities]
lons = [city['longitude'] for city in cities]
names = [city['name'] for city in cities]
# 非路径城市
non_path_cities = [city for city in names if city not in path_cities]
non_path_lats = [city_dict[city][0] for city in non_path_cities]
non_path_lons = [city_dict[city][1] for city in non_path_cities]
fig.add_trace(go.Scattergeo(
lon=non_path_lons,
lat=non_path_lats,
text=non_path_cities,
mode='markers',
marker=dict(size=8, color='blue'),
name='城市'
))
# 路径城市
path_lats = [city_dict[city][0] for city in path_cities]
path_lons = [city_dict[city][1] for city in path_cities]
fig.add_trace(go.Scattergeo(
lon=path_lons,
lat=path_lats,
text=path_cities,
mode='markers',
marker=dict(size=12, color='red'),
name='路径城市'
))
# 更新地图布局
fig.update_geos(
scope='asia',
showcountries=True,
countrycolor='Black',
showsubunits=True,
subunitcolor='Blue',
projection_type='mercator',
landcolor='LightGreen',
oceancolor='LightBlue',
showocean=True,
showland=True,
fitbounds='locations'
)
fig.update_layout(
title_text=f'中国省会城市最短路径: {" → ".join(path_cities)}',
title_x=0.5,
geo=dict(
center=dict(lat=35, lon=105),
projection_scale=4
),
height=800
)
fig.show()
def main():
# 创建图
with GraphDatabase.driver(URI, auth=AUTH) as driver:
with driver.session() as session:
session.execute_write(create_graph)
# 交互式查询
while True:
print("\n中国省会城市最短路径查询")
print("输入两个省会城市名称(用空格分隔),或输入'quit'退出")
user_input = input("> ").strip()
if user_input.lower() == 'quit':
break
cities = user_input.split()
if len(cities) != 2:
print("请输入两个有效的城市名称")
continue
city1, city2 = cities[0], cities[1]
# 验证城市是否存在
with GraphDatabase.driver(URI, auth=AUTH) as driver:
with driver.session() as session:
result = session.run("""
MATCH (c:City)
WHERE c.name IN [$city1, $city2]
RETURN c.name AS name
""", city1=city1, city2=city2)
found_cities = [record["name"] for record in result]
if city1 not in found_cities or city2 not in found_cities:
print("一个或两个城市不存在,请重试")
continue
# 计算并显示最短路径
setup_gds_and_find_shortest_path(city1, city2)
if __name__ == "__main__":
main() C:\Users\林卓昕\PycharmProjects\qmkh\.venv\Scripts\python.exe C:\Users\林卓昕\PycharmProjects\qmkh\test1.py
图数据库创建完成!
中国省会城市最短路径查询
输入两个省会城市名称(用空格分隔),或输入'quit'退出
> 北京 广州
从 北京 到 广州 的最短路径:
北京 -> 石家庄 -> 济南 -> 合肥 -> 南京 -> 武汉 -> 长沙 -> 广州
总距离: 2680.0 公里
Traceback (most recent call last):
File "C:\Users\林卓昕\PycharmProjects\qmkh\test1.py", line 312, in <module>
main()
File "C:\Users\林卓昕\PycharmProjects\qmkh\test1.py", line 308, in main
setup_gds_and_find_shortest_path(city1, city2)
File "C:\Users\林卓昕\PycharmProjects\qmkh\test1.py", line 165, in setup_gds_and_find_shortest_path
visualize_path_on_map(record["pathCities"], cities_data, relationships)
File "C:\Users\林卓昕\PycharmProjects\qmkh\test1.py", line 267, in visualize_path_on_map
fig.show()
File "C:\Users\林卓昕\PycharmProjects\qmkh\.venv\Lib\site-packages\plotly\basedatatypes.py", line 3436, in show
return pio.show(self, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\林卓昕\PycharmProjects\qmkh\.venv\Lib\site-packages\plotly\io\_renderers.py", line 417, in show
bundle = renderers._build_mime_bundle(fig_dict, renderers_string=renderer, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\林卓昕\PycharmProjects\qmkh\.venv\Lib\site-packages\plotly\io\_renderers.py", line 314, in _build_mime_bundle
self._activate_pending_renderers(cls=MimetypeRenderer)
File "C:\Users\林卓昕\PycharmProjects\qmkh\.venv\Lib\site-packages\plotly\io\_renderers.py", line 221, in _activate_pending_renderers
renderer.activate()
File "C:\Users\林卓昕\PycharmProjects\qmkh\.venv\Lib\site-packages\plotly\io\_base_renderers.py", line 305, in activate
ipython_display.display_html(script, raw=True)
File "C:\Users\林卓昕\PycharmProjects\qmkh\.venv\Lib\site-packages\IPython\core\display.py", line 126, in display_html
_display_mimetype('text/html', objs, **kwargs)
File "C:\Users\林卓昕\PycharmProjects\qmkh\.venv\Lib\site-packages\IPython\core\display.py", line 85, in _display_mimetype
display_functions.display(*objs, raw=raw, metadata=metadata, include=[mimetype])
File "C:\Users\林卓昕\PycharmProjects\qmkh\.venv\Lib\site-packages\IPython\core\display_functions.py", line 245, in display
print(*objs)
UnicodeEncodeError: 'gbk' codec can't encode character '\xb5' in position 4433978: illegal multibyte sequence
Process finished with exit code 1
最新发布