前言
在之前代码的基础上进行了优化,考虑到高考后对计算机操作还不是很熟练,读取数据从json格式换成了Excel表格,当Excel表格不存在的时候可以随机生成150条数据。
使用Python绘制毕业生城市分布图-CSDN博客https://blog.csdn.net/weixin_64066303/article/details/148822546?spm=1001.2014.3001.5501Python绘图库及图像类型之基础图表_arrowprops=dict(arrowstyle='->', lw=1.5)-CSDN博客
https://blog.csdn.net/weixin_64066303/article/details/148433762?spm=1001.2014.3001.5502Python绘图库及图像类型之高级可视化-CSDN博客
https://blog.csdn.net/weixin_64066303/article/details/148450750?spm=1001.2014.3001.5502Python绘图库及图像类型之特殊领域可视化-CSDN博客
https://blog.csdn.net/weixin_64066303/article/details/148450970?spm=1001.2014.3001.5502Python绘制三十六计_import matplotlib.pyplot as pltfrom matplotlib.pat-CSDN博客
https://blog.csdn.net/weixin_64066303/article/details/148498880?spm=1001.2014.3001.5502使用Python语言进行函数作画绘制芙莉莲&勇者-CSDN博客
https://blog.csdn.net/weixin_64066303/article/details/148412637?spm=1001.2014.3001.5502使用Python进行函数作画-CSDN博客
https://blog.csdn.net/weixin_64066303/article/details/148383559?spm=1001.2014.3001.5502
代码
本程序的功能和前面是毕业生分布是一致的,提供了搜索功能,方便找到自己心仪同学的位置和联系方式。
import folium
import pandas as pd
import json
from folium.plugins import MarkerCluster
import os
import random
import requests
print("=" * 50)
print("湖南省高校分布交互式地图生成器")
print("湖南师范大学树达学院2021级计算机科学与技术专业刘健到此一游")
print("=" * 50)
university_coords = {
# 本科院校 (修正了所有重复坐标)
'中南大学': [28.1628, 112.9353],
'湖南大学': [28.1792, 112.9438],
'湖南师范大学': [28.1845, 112.9506],
'湖南师范大学树达学院': [28.1900, 112.9400], # 新增
'湘潭大学': [27.8962, 112.9513],
'湖南农业大学': [28.1811, 113.0836],
'长沙理工大学': [28.0721, 113.0102],
'中南林业科技大学': [28.1338, 113.0387],
'湖南科技大学': [27.9150, 112.9440], # 修正
'南华大学': [26.8932, 112.8520], # 修正
'湖南工业大学': [27.8297, 113.1338],
'吉首大学': [28.3119, 109.7389],
'湖南工商大学': [28.2161, 113.0312],
'湖南理工学院': [29.3572, 113.1289],
'衡阳师范学院': [26.9030, 112.6330], # 修正
'湖南文理学院': [29.0316, 111.6991],
'湖南工程学院': [27.8550, 112.9420], # 修正
'湖南城市学院': [28.5539, 112.3552],
'湖南科技学院': [26.4203, 111.6122],
'湖南人文科技学院': [27.7000, 111.9964],
'湖南第一师范学院': [28.1212, 113.0009],
'长沙学院': [28.2458, 113.0812],
'湖南财政经济学院': [28.2100, 112.9100], # 修正
'湖南警察学院': [28.1449, 113.0363],
'湖南女子学院': [28.1020, 113.0170], # 修正
'湖南医药学院': [27.5600, 110.0000], # 修正
'怀化学院': [27.5540, 109.9985],
# 专科院校 (全部独立坐标)
'湖南工业职业技术学院': [28.1260, 112.8810],
'长沙民政职业技术学院': [28.1478, 112.9235],
'湖南铁道职业技术学院': [27.8700, 113.1000],
'湖南交通职业技术学院': [28.1650, 113.1100],
'湖南大众传媒职业技术学院': [28.2380, 113.0850],
'湖南科技职业学院': [28.1000, 113.0200],
'湖南生物机电职业技术学院': [28.1870, 113.0900],
'湖南商务职业技术学院': [28.2300, 112.9200],
'湖南工程职业技术学院': [28.2700, 113.0500],
'长沙航空职业技术学院': [28.0700, 113.0500],
'湖南汽车工程职业学院': [27.9500, 113.1500],
'湖南化工职业技术学院': [27.9400, 113.1400],
'湖南城建职业技术学院': [27.8600, 112.9500],
'湖南环境生物职业技术学院': [26.9300, 112.6000],
'湖南机电职业技术学院': [28.2800, 113.0500],
'湖南工艺美术职业学院': [28.5600, 112.3500],
'湖南石油化工职业技术学院': [29.4500, 113.1500],
'湖南国防工业职业技术学院': [27.8500, 112.8500],
'长沙商贸旅游职业技术学院': [28.1000, 113.0500],
'湖南网络工程职业学院': [28.1000, 112.9900],
'湖南司法警官职业学院': [28.2200, 113.1000],
'长沙环境保护职业技术学院': [28.1300, 113.0000],
'湖南现代物流职业技术学院': [28.3000, 113.1000],
'湖南安全技术职业学院': [28.2500, 113.0800],
'湖南外国语职业学院': [28.3500, 112.8000],
'湖南电子科技职业学院': [28.3300, 112.8200],
'湖南都市职业学院': [28.2400, 113.2000],
'湖南三一工业职业技术学院': [28.1700, 113.1500],
'长沙电力职业技术学院': [28.2000, 113.0800],
'湖南水利水电职业技术学院': [28.2300, 113.1100],
'湖南信息职业技术学院': [28.3400, 112.8300],
'湖南食品药品职业学院': [28.2100, 112.8700],
'湖南劳动人事职业学院': [28.3100, 113.1100],
'湖南有色金属职业技术学院': [27.9450, 113.1450],
'湖南九嶷职业技术学院': [26.4500, 111.6000],
'潇湘职业学院': [27.7200, 111.9900],
'湖南软件职业学院': [27.9200, 112.9400],
'湘西民族职业技术学院': [28.3200, 109.7500],
'张家界航空工业职业技术学院': [29.1171, 110.4792],
'益阳职业技术学院': [28.6000, 112.3500],
'郴州职业技术学院': [25.7705, 113.0147],
'娄底职业技术学院': [27.7300, 111.9800],
'怀化职业技术学院': [27.5600, 109.9900],
'永州职业技术学院': [26.4500, 111.6200],
'常德职业技术学院': [29.0500, 111.7000],
'岳阳职业技术学院': [29.3500, 113.1500],
'邵阳职业技术学院': [27.2389, 111.4672],
'株洲师范高等专科学校': [27.8300, 113.1300],
'益阳师范高等专科学校': [28.5700, 112.3600],
'湖南幼儿师范高等专科学校': [29.0400, 111.6800],
'湘南幼儿师范高等专科学校': [25.7800, 113.0200],
'怀化师范高等专科学校': [27.4000, 110.0000],
'永州师范高等专科学校': [26.4300, 111.6200],
'衡阳幼儿师范高等专科学校': [26.8800, 112.6100],
'长沙幼儿师范高等专科学校': [28.0900, 113.0000],
'株洲幼儿师范学校': [27.9450, 113.1420]
}
# 定义Excel文件路径
excel_file = "universities.xlsx"
# 检查Excel文件是否存在
if not os.path.exists(excel_file):
print(f"错误:未找到数据文件 '{excel_file}'")
print("请创建包含高校数据的Excel文件,格式如下:")
print("列名: '姓名', '大学', '专业', '电话'")
# 创建50条示例数据
universities = list(university_coords.keys())
# 多样化的专业列表
majors = [
"计算机科学与技术", "软件工程", "人工智能", "数据科学", "网络安全",
"物联网工程", "云计算", "大数据", "区块链",
"电子信息工程", "通信工程", "自动化", "电气工程及其自动化",
"机械工程", "材料科学与工程", "土木工程", "建筑学",
"金融学", "会计学", "市场营销", "国际经济与贸易", "工商管理",
"法学", "汉语言文学", "英语", "新闻学", "广告学",
"数学与应用数学", "物理学", "化学", "生物科学",
"临床医学", "口腔医学", "护理学", "药学",
"艺术设计", "音乐表演", "舞蹈学", "戏剧影视文学",
"体育教育", "运动训练", "社会体育指导与管理",
"心理学", "教育学", "学前教育", "小学教育"
]
# 姓氏和名字
surnames = ["张", "王", "李", "赵", "陈", "刘", "杨", "黄", "周", "吴"]
given_names = ["伟", "芳", "娜", "秀英", "敏", "静", "丽", "强", "磊", "军",
"洋", "勇", "艳", "杰", "涛", "明", "超", "秀兰", "霞", "平",
"刚", "桂英", "文", "兰", "红", "志强", "建国", "建华", "桂兰", "桂香"]
sample_data = {
'姓名': [],
'大学': [],
'专业': [],
'电话': []
}
# 生成150条记录
for i in range(150):
# 随机生成姓名
surname = random.choice(surnames)
given_name = random.choice(given_names)
name = surname + given_name
# 随机选择大学和专业
university = random.choice(universities)
major = random.choice(majors)
# 生成随机电话号码
prefix = random.choice(['130', '131', '132', '133', '134', '135', '136', '137', '138', '139'])
suffix = ''.join(random.choices('0123456789', k=8))
phone = prefix + suffix
sample_data['姓名'].append(name)
sample_data['大学'].append(university)
sample_data['专业'].append(major)
sample_data['电话'].append(phone)
# 创建DataFrame并保存为Excel
df_sample = pd.DataFrame(sample_data)
df_sample.to_excel(excel_file, index=False)
print(f"已创建包含150条示例数据的文件 '{excel_file}',请修改后重新运行程序")
exit()
# 从Excel文件读取数据
try:
df = pd.read_excel(excel_file)
print(f"成功从 '{excel_file}' 加载 {len(df)} 条高校数据")
# 检查必要的列是否存在
required_columns = ['姓名', '大学']
missing_columns = [col for col in required_columns if col not in df.columns]
if missing_columns:
print(f"错误:Excel文件中缺少必要的列: {', '.join(missing_columns)}")
exit()
# 重命名列(如果使用不同的列名)
column_mapping = {
'姓名': '姓名',
'大学': '大学',
'专业': '专业',
'电话': '电话',
'学校': '大学',
'名称': '姓名',
'专业名称': '专业',
'手机': '电话',
'联系电话': '电话'
}
# 应用列名映射
df = df.rename(columns=column_mapping)
# 确保所有必要的列都存在
if '专业' not in df.columns:
df['专业'] = '未提供专业信息'
if '电话' not in df.columns:
df['电话'] = ''
# 转换为列表字典
student_data = df.to_dict('records')
except Exception as e:
print(f"读取Excel文件时出错: {e}")
exit()
# 验证数据格式
valid_data = []
for student in student_data:
if pd.isna(student.get('姓名')) or pd.isna(student.get('大学')):
print(f"警告:跳过无效记录 - {student}")
continue
university = student["大学"]
if university not in university_coords:
print(f"警告:大学 '{university}' 不在坐标字典中,已跳过记录: {student['姓名']}")
continue
# 处理电话号码 - 如果没有则生成随机号码
phone = str(student.get("电话", ""))
if not phone or phone.lower() in ['nan', 'null', 'none', '']:
# 生成随机的中国大陆手机号码
prefix = random.choice(['130', '131', '132', '133', '134', '135', '136', '137', '138', '139'])
suffix = ''.join(random.choices('0123456789', k=8))
phone = prefix + suffix
print(f"提示:{student['姓名']} ({university}) 没有电话号码,已生成随机号码: {phone}")
# 获取专业信息
major = student.get("专业", "未提供专业信息")
if pd.isna(major):
major = "未提供专业信息"
valid_data.append({
'姓名': student['姓名'],
'大学': university,
'专业': major,
'电话': phone,
'坐标': university_coords[university]
})
if not valid_data:
print("错误:没有有效数据可用于创建地图")
exit()
print(f"有效数据记录: {len(valid_data)} 条")
# 创建数据框
df = pd.DataFrame(valid_data)
# 获取湖南省GeoJSON边界数据
def get_hunan_geojson():
try:
# 尝试从网络获取最新数据
url = "https://geo.datav.aliyun.com/areas_v3/bound/430000_full.json"
response = requests.get(url, timeout=10)
response.raise_for_status()
print("成功从网络获取湖南省边界数据")
return response.json()
except:
# 如果网络请求失败,使用本地备份数据
print("网络请求失败,使用本地备份的湖南省边界数据")
return {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"adcode": 430000,
"name": "湖南省",
"center": [112.982279, 28.19409],
"centroid": [112.113889, 28.547812],
"childrenNum": 14,
"level": "province",
"parent": {"adcode": 100000},
"subFeatureIndex": 0,
"acroutes": [100000]
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [[[
[109.423469, 29.117102], [109.423469, 29.117102],
[113.382312, 29.117102], [113.382312, 24.977642],
[109.423469, 24.977642], [109.423469, 29.117102]
]]]
}
}
]
}
# 获取湖南省边界数据
hunan_geojson = get_hunan_geojson()
# 创建湖南省地图 - 使用OpenStreetMap中文版
print("正在创建地图...")
m = folium.Map(location=[27.5, 112.0],
zoom_start=7,
tiles='https://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
attr='高德地图',
control_scale=True)
# 添加湖南省精确边界
folium.GeoJson(
hunan_geojson,
name='湖南省边界',
style_function=lambda feature: {
'fillColor': '#f0f8ff',
'color': '#1e90ff',
'weight': 2,
'fillOpacity': 0.2
},
tooltip=folium.GeoJsonTooltip(fields=['name'], aliases=['省份: '])
).add_to(m)
# 按大学分组学生
university_groups = df.groupby('大学')
# 创建自定义标记聚类 - 显示总人数而不是标记数量
marker_cluster = MarkerCluster(
name="高校分布",
icon_create_function='''function(cluster) {
var markers = cluster.getAllChildMarkers();
var totalStudents = 0;
// 计算所有标记中的学生总数
for (var i = 0; i < markers.length; i++) {
totalStudents += markers[i].options.studentCount;
}
// 根据总人数确定图标大小和颜色
var iconSize = 40;
var iconColor = '#4CAF50'; // 绿色
var fontSize = 16;
if (totalStudents > 30) {
iconSize = 55;
iconColor = '#f44336'; // 红色
fontSize = 18;
} else if (totalStudents > 20) {
iconSize = 50;
iconColor = '#FF9800'; // 橙色
fontSize = 17;
} else if (totalStudents > 10) {
iconSize = 45;
iconColor = '#FFC107'; // 黄色
fontSize = 16;
} else if (totalStudents > 5) {
iconSize = 40;
iconColor = '#4CAF50'; // 绿色
fontSize = 16;
}
// 创建圆形图标
return L.divIcon({
html: '<div style="background-color:' + iconColor + '; width: ' + iconSize + 'px; height: ' + iconSize + 'px; border-radius: 50%; border: 3px solid white; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; font-size: ' + fontSize + 'px; box-shadow: 0 0 10px rgba(0,0,0,0.3);">' + totalStudents + '</div>',
className: 'marker-cluster-custom',
iconSize: L.point(iconSize, iconSize),
iconAnchor: [iconSize / 2, iconSize / 2]
});
}'''
).add_to(m)
# 为每个大学添加标记
print("正在添加高校标记...")
for university, group in university_groups:
count = len(group)
university_type = "本科" if "大学" in university or "学院" in university else "专科"
# 创建弹出窗口内容 - 使用表格显示姓名和电话
popup_html = f"""
<div style="width:350px; font-family: 'Microsoft YaHei', sans-serif;">
<h4 style="margin-top:0; color:#1e90ff; border-bottom:1px solid #eee; padding-bottom:5px;">
{university}</h4>
<div style="display:flex; justify-content:space-between; margin-bottom:10px;">
<span style="background-color:#4CAF50; color:white; padding:3px 8px; border-radius:4px;">
{university_type}院校
</span>
<span style="font-weight:bold; color:#333;">
学生人数: {count}人
</span>
</div>
<div style="max-height:300px; overflow-y:auto; border:1px solid #eee; padding:5px; margin-top:5px;">
<table style="width:100%; border-collapse: collapse;">
<thead>
<tr style="background-color: #f2f2f2;">
<th style="border: 1px solid #ddd; padding: 8px; text-align: left;">姓名</th>
<th style="border: 1px solid #ddd; padding: 8px; text-align: left;">专业</th>
<th style="border: 1px solid #ddd; padding: 8px; text-align: left;">电话</th>
</tr>
</thead>
<tbody>
"""
# 添加表格行
for _, row in group.iterrows():
popup_html += f"""
<tr>
<td style="border: 1px solid #ddd; padding: 8px;">{row['姓名']}</td>
<td style="border: 1px solid #ddd; padding: 8px;">{row['专业']}</td>
<td style="border: 1px solid #ddd; padding: 8px;">
<a href="tel:{row['电话']}">{row['电话']}</a>
</td>
</tr>
"""
popup_html += """
</tbody>
</table>
</div>
<div style="margin-top:10px; font-size:12px; color:#888; text-align:center;">
点击电话可直接拨打(在移动设备上)
</div>
</div>
"""
# 根据人数确定图标大小
if count > 7:
icon_size = (60, 60)
icon_color = '#8B0000' # 深红
elif count > 5:
icon_size = (45, 45)
icon_color = '#FF8C00' # 深橙色
elif count > 3:
icon_size = (42, 42)
icon_color = '#FFA500' # 橙色
elif count > 1:
icon_size = (38, 38)
icon_color = '#FFD700' # 金色
else:
icon_size = (30, 30)
icon_color = '#00CED1' # 青蓝色
# 创建自定义图标
icon = folium.DivIcon(
icon_size=icon_size,
icon_anchor=(icon_size[0] // 2, icon_size[1] // 2),
html=f"""
<div style="
background-color: {icon_color};
width: {icon_size[0]}px;
height: {icon_size[1]}px;
border-radius: 50%;
border: 2px solid white;
display: flex;
justify-content: center;
align-items: center;
color: white;
font-weight: bold;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
font-family: 'Microsoft YaHei', sans-serif;
font-size: {min(16, max(12, icon_size[0] // 2))}px;
">
{count}
</div>
"""
)
# 添加标记并设置学生人数属性
marker = folium.Marker(
location=university_coords[university],
popup=folium.Popup(popup_html, max_width=400),
icon=icon,
tooltip=f"{university} ({count}名学生)"
)
# 添加学生人数属性,用于聚类计算
marker.options['studentCount'] = count
marker.add_to(marker_cluster)
# 添加标题
title_html = '''
<h3 align="center" style="font-size:18px; background-color:rgba(255,255,255,0.8);
padding:10px; margin:10px; border-radius:5px; font-family: 'Microsoft YaHei', sans-serif;">
<b>耒阳市正源学校2021级毕业生分布图</b></h3>
'''
m.get_root().html.add_child(folium.Element(title_html))
# 添加搜索框的CSS样式
search_css = '''
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<style>
#search-container {
position: fixed;
top: 100px;
right: 50px;
z-index:9999;
font-size:14px;
background-color:rgba(255,255,255,0.95);
padding: 15px;
border-radius: 5px;
box-shadow: 0 0 15px rgba(0,0,0,0.3);
font-family: 'Microsoft YaHei', sans-serif;
}
.collapsible-toggle-btn {
position: absolute;
top: 5px;
right: 5px;
width: 30px;
height: 30px;
border-radius: 50%;
background-color: #4CAF50;
color: white;
border: none;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
z-index: 10000;
}
.collapsible-content {
transition: all 0.3s ease;
overflow: hidden;
}
.collapsible-content.collapsed {
max-height: 0;
opacity: 0;
padding: 0;
margin: 0;
}
@media (max-width: 768px) {
#search-container {
width: 90%;
right: 5%;
top: 20px;
}
.collapsible-content {
max-height: 400px;
opacity: 1;
}
.collapsible-content.collapsed {
max-height: 0;
opacity: 0;
padding: 0;
margin: 0;
}
}
#search-input {
width: 280px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
margin-bottom: 12px;
font-size:14px;
}
#search-results {
margin-top:15px;
max-height:400px;
overflow-y:auto;
display:none;
border:1px solid #ddd;
border-radius:4px;
padding:5px;
}
</style>
'''
m.get_root().html.add_child(folium.Element(search_css))
# 添加搜索框(带折叠功能)
search_html = '''
<div id="search-container">
<button class="collapsible-toggle-btn" id="toggle-search" onclick="toggleElement('search-content')">
<i class="fa fa-chevron-up" id="toggle-search-icon"></i>
</button>
<h4 style="margin-top:0; margin-bottom:10px; color:#333; border-bottom:1px solid #eee; padding-bottom:5px; position:relative;">
<i class="fa fa-search" style="margin-right:5px;"></i>高校与学生搜索
</h4>
<div class="collapsible-content" id="search-content">
<input type="text" id="search-input" placeholder="输入高校名称或学生姓名...">
<div>
<button onclick="searchGraduates()"
style="padding: 10px 20px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight:bold;">
<i class="fa fa-search" style="margin-right:5px;"></i>搜索
</button>
<button onclick="clearSearch()"
style="padding: 10px 20px; background: #f44336; color: white; border: none; border-radius: 4px; cursor: pointer; margin-left:8px; font-weight:bold;">
<i class="fa fa-times" style="margin-right:5px;"></i>清除
</button>
</div>
<div id="search-results"></div>
</div>
</div>
'''
m.get_root().html.add_child(folium.Element(search_html))
# 获取地图的div ID
map_div_id = m.get_name()
# 添加JavaScript功能
search_js = f"""
<script>
// 存储所有毕业生数据
var allGraduates = {json.dumps(valid_data, ensure_ascii=False)};
// 切换元素状态函数
function toggleElement(elementId) {{
var content = document.getElementById(elementId);
var icon = document.getElementById('toggle-search-icon');
if (content.classList.contains('collapsed')) {{
content.classList.remove('collapsed');
icon.className = 'fa fa-chevron-up';
}} else {{
content.classList.add('collapsed');
icon.className = 'fa fa-chevron-down';
}}
}}
// 搜索函数
function searchGraduates() {{
var input = document.getElementById('search-input').value.trim().toLowerCase();
var resultsContainer = document.getElementById('search-results');
resultsContainer.innerHTML = '';
resultsContainer.style.display = 'block';
if (!input) {{
resultsContainer.innerHTML = '<div style="color:#888; text-align:center; padding:20px;">请输入搜索内容</div>';
return;
}}
var found = false;
var resultsHtml = '<div style="font-size:14px;"><table style="width:100%; border-collapse: collapse; border-spacing:0;">';
resultsHtml += '<thead><tr style="background-color:#f5f5f5; font-weight:bold;">';
resultsHtml += '<th style="padding:10px; border-bottom:1px solid #ddd; text-align:left;">姓名</th>';
resultsHtml += '<th style="padding:10px; border-bottom:1px solid #ddd; text-align:left;">大学</th>';
resultsHtml += '<th style="padding:10px; border-bottom:1px solid #ddd; text-align:left;">专业</th>';
resultsHtml += '<th style="padding:10px; border-bottom:1px solid #ddd; text-align:left;">电话</th>';
resultsHtml += '</tr></thead><tbody>';
var resultCount = 0;
var firstResultLocation = null;
for (var i = 0; i < allGraduates.length; i++) {{
var graduate = allGraduates[i];
var name = graduate.姓名.toLowerCase();
var university = graduate.大学.toLowerCase();
var major = graduate.专业.toLowerCase();
var phone = graduate.电话;
var location = graduate.坐标;
if (name.includes(input) || university.includes(input) || major.includes(input)) {{
found = true;
resultCount++;
if (resultCount === 1) {{
firstResultLocation = location;
}}
var displayName = graduate.姓名;
var displayUniversity = graduate.大学;
var displayMajor = graduate.专业;
if (name.includes(input)) {{
var startIndex = name.indexOf(input);
displayName = graduate.姓名.substring(0, startIndex) +
'<span style="background-color:yellow;">' +
graduate.姓名.substring(startIndex, startIndex + input.length) +
'</span>' +
graduate.姓名.substring(startIndex + input.length);
}}
if (university.includes(input)) {{
var startIndex = university.indexOf(input);
displayUniversity = graduate.大学.substring(0, startIndex) +
'<span style="background-color:yellow;">' +
graduate.大学.substring(startIndex, startIndex + input.length) +
'</span>' +
graduate.大学.substring(startIndex + input.length);
}}
if (major.includes(input)) {{
var startIndex = major.indexOf(input);
displayMajor = graduate.专业.substring(0, startIndex) +
'<span style="background-color:yellow;">' +
graduate.专业.substring(startIndex, startIndex + input.length) +
'</span>' +
graduate.专业.substring(startIndex + input.length);
}}
resultsHtml += '<tr style="border-bottom:1px solid #eee; cursor:pointer;" onclick="flyToLocation([' + location[0] + ',' + location[1] + '])">';
resultsHtml += '<td style="padding:10px; border-bottom:1px solid #eee;">' + displayName + '</td>';
resultsHtml += '<td style="padding:10px; border-bottom:1px solid #eee;">' + displayUniversity + '</td>';
resultsHtml += '<td style="padding:10px; border-bottom:1px solid #eee;">' + displayMajor + '</td>';
resultsHtml += '<td style="padding:10px; border-bottom:1px solid #eee;"><a href="tel:' + phone + '" style="color:#1e90ff; text-decoration:none;">' +
'<i class="fa fa-phone" style="margin-right:5px;"></i>' + phone + '</a></td>';
resultsHtml += '</tr>';
}}
}}
resultsHtml += '</tbody></table>';
if (found) {{
resultsHtml += '<div style="padding:10px; background-color:#f9f9f9; text-align:center; font-size:13px; color:#666;">';
resultsHtml += '共找到 ' + resultCount + ' 条匹配记录';
resultsHtml += '<br><span style="color:#4CAF50;">点击任意结果可定位到地图位置</span>';
resultsHtml += '</div>';
if (firstResultLocation) {{
flyToLocation(firstResultLocation);
}}
}} else {{
resultsHtml = '<div style="color:#f44336; text-align:center; padding:30px; font-size:16px;">';
resultsHtml += '<i class="fa fa-exclamation-triangle" style="font-size:24px; margin-bottom:10px; display:block;"></i>';
resultsHtml += '未找到匹配的学生或高校';
resultsHtml += '</div>';
}}
resultsHtml += '</div>';
resultsContainer.innerHTML = resultsHtml;
}}
// 清除搜索函数
function clearSearch() {{
document.getElementById('search-input').value = '';
var resultsContainer = document.getElementById('search-results');
resultsContainer.innerHTML = '';
resultsContainer.style.display = 'none';
}}
// 定位函数
function flyToLocation(location) {{
var lat = parseFloat(location[0]);
var lng = parseFloat(location[1]);
if (isNaN(lat) || isNaN(lng)) {{
console.error("无效的坐标:", location);
return;
}}
var mapElements = document.getElementsByClassName('folium-map');
if (mapElements.length === 0) {{
console.error("未找到地图元素");
return;
}}
var mapDiv = mapElements[0];
// 获取地图实例
var map = null;
if (typeof window[mapDiv.id] !== 'undefined') {{
map = window[mapDiv.id];
}} else if (typeof window['map_' + mapDiv.id] !== 'undefined') {{
map = window['map_' + mapDiv.id];
}} else {{
var mapObjects = mapDiv.getElementsByClassName('leaflet-container');
if (mapObjects.length > 0) {{
map = mapObjects[0]._map;
}}
}}
if (!map) {{
console.error("无法获取地图实例");
return;
}}
// 平滑移动地图
map.flyTo([lat, lng], 12, {{
animate: true,
duration: 1.5
}});
// 添加高亮标记
var marker = L.marker([lat, lng], {{
icon: L.divIcon({{
className: 'highlight-marker',
html: '<div style="background-color:red; width:40px; height:40px; border-radius:50%; border:3px solid white; box-shadow:0 0 15px rgba(255,0,0,0.9); display:flex; align-items:center; justify-content:center; font-size:18px; font-weight:bold;">!</div>',
iconSize: [40, 40],
iconAnchor: [20, 20]
}}),
zIndexOffset: 1000
}}).addTo(map);
// 3秒后移除高亮标记
setTimeout(function() {{
if (marker && map.hasLayer(marker)) {{
map.removeLayer(marker);
}}
}}, 3000);
}}
// 支持按Enter键搜索
document.getElementById('search-input').addEventListener('keypress', function(e) {{
if (e.key === 'Enter') {{
searchGraduates();
}}
}});
// 移动端初始化:默认折叠搜索框
function initMobileView() {{
if (window.innerWidth <= 768) {{
toggleElement('search-content');
}}
}}
// 页面加载完成后初始化
window.addEventListener('load', initMobileView);
</script>
"""
m.get_root().html.add_child(folium.Element(search_js))
# 添加全屏控件
folium.plugins.Fullscreen(
position='topleft',
title='全屏查看',
title_cancel='退出全屏',
force_separate_button=True
).add_to(m)
# 添加比例尺
folium.plugins.MousePosition(
position='bottomright',
separator=' : ',
empty_string='坐标未获取',
lng_first=False,
num_digits=4
).add_to(m)
# 添加图层控制
folium.LayerControl().add_to(m)
# 保存为HTML文件
output_file = "hunan_universities.html"
m.save(output_file)
print("=" * 50)
print(f"地图已成功生成: {output_file}")
print("请用浏览器打开该文件查看交互式地图")
print("=" * 50)