【零代码秘籍】Folium交互式地图可视化:10分钟从菜鸟到大师的完全指南

【零代码秘籍】Folium交互式地图可视化:10分钟从菜鸟到大师的完全指南

1. 引言:地图可视化的利器

在数据可视化领域,地图作为一种直观的空间信息表达方式,具有无可替代的优势。而当地图从静态走向交互式,其表达力更是呈指数级提升。Folium库作为Python生态中最受欢迎的交互式地图可视化工具,以其简洁的语法和强大的功能,让数据分析师和开发者能够轻松创建专业级的Web地图应用。

本文将带你全面掌握Folium的使用技巧,从基础地图创建到高级功能应用,帮助你快速实现各类地理数据的可视化需求,无需编写一行JavaScript代码。

2. Folium基础:理解与安装

2.1 Folium是什么?

Folium是Python的一个强大库,它将Python的数据处理能力与Leaflet.js的交互式地图可视化能力完美结合。使用Folium,你可以:

  • 在Python中创建交互式Web地图
  • 利用各种底图服务(OpenStreetMap, Mapbox等)
  • 添加标记、弹出窗口、线条、多边形等地图元素
  • 集成地理数据(GeoJSON, TopoJSON等)
  • 创建分级统计图(Choropleth Maps)
  • 生成热力图、聚类图等高级可视化
  • 导出为独立的HTML文件,可在任何浏览器中查看

2.2 安装与环境配置

# 使用pip安装Folium
pip install folium

# 如果需要高级功能,安装扩展包
pip install folium branca geojson numpy pandas

# 导入基础库
import folium
import pandas as pd
import numpy as np

3. 创建你的第一个交互式地图

3.1 基础地图创建

# 创建一个地图对象,指定中心点和缩放级别
m = folium.Map(location=[30.5928, 114.3055], zoom_start=12)  # 武汉市中心

# 保存为HTML文件
m.save('my_first_map.html')

3.2 切换底图样式

Folium提供多种底图样式选择:

# 常用的底图样式
tiles = {
    '默认OpenStreetMap': 'OpenStreetMap',
    '卫星图': 'Stamen Terrain',
    '黑白底图': 'CartoDB positron',
    '暗黑模式': 'CartoDB dark_matter',
    '水彩风格': 'Stamen Watercolor'
}

# 创建包含多种样式的地图
for name, tile in tiles.items():
    m = folium.Map(location=[30.5928, 114.3055], zoom_start=12, tiles=tile)
    m.save(f'map_{tile.replace(" ", "_")}.html')

3.3 添加简单标记

# 创建基础地图
m = folium.Map(location=[30.5928, 114.3055], zoom_start=12)

# 添加标记
folium.Marker(
    location=[30.5928, 114.3055],
    popup='武汉市中心',
    tooltip='点击查看详情',
    icon=folium.Icon(color='red', icon='info-sign')
).add_to(m)

# 保存地图
m.save('map_with_marker.html')

4. 基础地图元素

4.1 标记与图标

# 创建地图
m = folium.Map(location=[30.5928, 114.3055], zoom_start=12)

# 自定义图标
custom_icon = folium.Icon(
    color='green',           # 图标颜色
    icon_color='white',      # 图标中符号的颜色
    icon='university',       # Font Awesome图标名称
    prefix='fa'              # 图标库前缀
)

# 添加带自定义图标的标记
folium.Marker(
    location=[30.5928, 114.3055],
    popup='<b>武汉大学</b><br>中国顶尖高校',
    tooltip='武汉大学',
    icon=custom_icon
).add_to(m)

# 使用圆形标记
folium.CircleMarker(
    location=[30.5830, 114.2830],
    radius=50,               # 半径(像素)
    popup='华中科技大学',
    color='#3186cc',         # 边框颜色
    fill=True,
    fill_color='#3186cc',    # 填充颜色
    fill_opacity=0.2
).add_to(m)

m.save('markers_and_icons.html')

4.2 弹出窗口与工具提示

# 创建地图
m = folium.Map(location=[30.5928, 114.3055], zoom_start=12)

# 添加带HTML格式弹出窗口的标记
folium.Marker(
    location=[30.5928, 114.3055],
    popup=folium.Popup(
        '<h3>武汉大学</h3>'
        '<img src="https://example.com/whu.jpg" width="200px"><br>'
        '<p>始建于1893年,是中国著名的综合性大学。</p>'
        '<a href="https://www.whu.edu.cn" target="_blank">官网链接</a>',
        max_width=300
    ),
    tooltip='点击查看详情',
    icon=folium.Icon(color='red', icon='graduation-cap', prefix='fa')
).add_to(m)

# 添加IFrame弹出窗口
iframe = folium.IFrame(html='<h3>华中科技大学</h3>', width=200, height=100)
popup = folium.Popup(iframe, max_width=300)

folium.Marker(
    location=[30.5830, 114.2830],
    popup=popup,
    tooltip='华中科技大学',
    icon=folium.Icon(color='blue', icon='book', prefix='fa')
).add_to(m)

m.save('markers_with_popups.html')

4.3 线条与路径

# 创建地图
m = folium.Map(location=[30.5928, 114.3055], zoom_start=12)

# 添加一条线段
folium.PolyLine(
    locations=[
        [30.5928, 114.3055],  # 武汉大学
        [30.5830, 114.2830],  # 华中科技大学
        [30.5223, 114.3559]   # 华中师范大学
    ],
    color='blue',
    weight=5,                # 线宽
    opacity=0.8,
    popup='武汉高校连线'
).add_to(m)

# 添加带箭头的线段
folium.plugins.AntPath(
    locations=[
        [30.5928, 114.3055],  # 起点
        [30.6100, 114.3600]   # 终点
    ],
    color='red',
    weight=5,
    opacity=0.8,
    popup='路线A',
    tooltip='带动画效果的路径'
).add_to(m)

m.save('lines_and_paths.html')

4.4 多边形区域

# 创建地图
m = folium.Map(location=[30.5928, 114.3055], zoom_start=12)

# 创建多边形
folium.Polygon(
    locations=[
        [30.6000, 114.3000],
        [30.6000, 114.3200],
        [30.5800, 114.3200],
        [30.5800, 114.3000]
    ],
    color='purple',
    fill=True,
    fill_color='purple',
    fill_opacity=0.4,
    popup='武昌区域范围'
).add_to(m)

# 添加圆形区域
folium.Circle(
    location=[30.5928, 114.3055],
    radius=2000,             # 半径(米)
    color='green',
    fill=True,
    fill_color='green',
    fill_opacity=0.2,
    popup='2公里覆盖范围'
).add_to(m)

m.save('polygons_and_areas.html')

5. 高级地图功能

5.1 分级统计图 (Choropleth Maps)

# 加载示例数据
import pandas as pd
data = pd.DataFrame({
    'district': ['东湖高新区', '江岸区', '江汉区', '硚口区', '汉阳区', '武昌区', '洪山区'],
    'population': [10000, 85000, 75000, 65000, 70000, 90000, 80000]
})

# 加载区域GeoJSON数据(假设已有武汉市区域GeoJSON文件)
import json
with open('wuhan_districts.geojson', 'r', encoding='utf-8') as f:
    wuhan_geo = json.load(f)

# 创建地图
m = folium.Map(location=[30.5928, 114.3055], zoom_start=11)

# 添加分级统计图
folium.Choropleth(
    geo_data=wuhan_geo,
    name='人口分布',
    data=data,
    columns=['district', 'population'],
    key_on='feature.properties.name',  # GeoJSON中的属性名
    fill_color='YlOrRd',              # 色阶
    fill_opacity=0.7,
    line_opacity=0.2,
    legend_name='人口数量'
).add_to(m)

# 添加图层控制
folium.LayerControl().add_to(m)

m.save('choropleth_map.html')

5.2 热力图 (Heatmap)

# 加载示例点数据
import numpy as np
np.random.seed(42)
data = pd.DataFrame({
    'lat': np.random.normal(30.58, 0.05, 500),
    'lon': np.random.normal(114.30, 0.05, 500),
    'value': np.random.uniform(1, 10, 500)  # 权重值
})

# 创建地图
m = folium.Map(location=[30.5928, 114.3055], zoom_start=12)

# 添加热力图
from folium.plugins import HeatMap
HeatMap(
    data=data[['lat', 'lon', 'value']].values.tolist(),
    radius=15,              # 热点半径
    gradient={0.4: 'blue', 0.65: 'lime', 1: 'red'},  # 自定义色阶
    min_opacity=0.2,
    max_opacity=0.8,
    blur=10
).add_to(m)

m.save('heatmap.html')

5.3 标记聚类 (Marker Cluster)

# 创建地图
m = folium.Map(location=[30.5928, 114.3055], zoom_start=12)

# 创建聚类标记组
from folium.plugins import MarkerCluster
marker_cluster = MarkerCluster().add_to(m)

# 生成随机点
np.random.seed(42)
for i in range(100):
    lat = np.random.uniform(30.55, 30.65)
    lon = np.random.uniform(114.25, 114.35)
    
    folium.Marker(
        location=[lat, lon],
        popup=f'标记 #{i+1}',
        icon=folium.Icon(color='green', icon='info-sign')
    ).add_to(marker_cluster)

m.save('marker_cluster.html')

5.4 自定义图层控制

# 创建地图
m = folium.Map(location=[30.5928, 114.3055], zoom_start=12)

# 创建不同的特征组
universities = folium.FeatureGroup(name='高校')
hospitals = folium.FeatureGroup(name='医院')
parks = folium.FeatureGroup(name='公园')

# 添加高校标记
universities_data = [
    {'name': '武汉大学', 'location': [30.5928, 114.3055]},
    {'name': '华中科技大学', 'location': [30.5830, 114.2830]},
    {'name': '华中师范大学', 'location': [30.5223, 114.3559]}
]

for uni in universities_data:
    folium.Marker(
        location=uni['location'],
        popup=uni['name'],
        icon=folium.Icon(color='blue', icon='graduation-cap', prefix='fa')
    ).add_to(universities)

# 添加医院标记
hospitals_data = [
    {'name': '武汉协和医院', 'location': [30.5800, 114.3000]},
    {'name': '湖北省人民医院', 'location': [30.6000, 114.2800]}
]

for hospital in hospitals_data:
    folium.Marker(
        location=hospital['location'],
        popup=hospital['name'],
        icon=folium.Icon(color='red', icon='plus', prefix='fa')
    ).add_to(hospitals)

# 添加公园区域
parks_data = [
    {'name': '东湖', 'polygon': [
        [30.5800, 114.3600],
        [30.5800, 114.3900],
        [30.5500, 114.3900],
        [30.5500, 114.3600]
    ]}
]

for park in parks_data:
    folium.Polygon(
        locations=park['polygon'],
        popup=park['name'],
        color='green',
        fill=True,
        fill_color='green',
        fill_opacity=0.4
    ).add_to(parks)

# 将所有特征组添加到地图
universities.add_to(m)
hospitals.add_to(m)
parks.add_to(m)

# 添加图层控制
folium.LayerControl().add_to(m)

m.save('custom_layer_control.html')

6. 实用案例与扩展功能

6.1 房地产选址分析地图

# 创建地图
m = folium.Map(location=[30.5928, 114.3055], zoom_start=12)

# 添加房产标记
properties = [
    {'name': '项目A', 'location': [30.5900, 114.3100], 'price': 15000, 'area': 120},
    {'name': '项目B', 'location': [30.6000, 114.3200], 'price': 18000, 'area': 90},
    {'name': '项目C', 'location': [30.5800, 114.3000], 'price': 12000, 'area': 150},
    {'name': '项目D', 'location': [30.6100, 114.3300], 'price': 20000, 'area': 85}
]

# 创建设施标记图层
facilities = folium.FeatureGroup(name='周边设施')

# 添加学校、商场等设施
facilities_data = [
    {'name': '学校A', 'location': [30.5920, 114.3080], 'type': 'school'},
    {'name': '商场B', 'location': [30.5950, 114.3150], 'type': 'shopping'},
    {'name': '医院C', 'location': [30.6050, 114.3250], 'type': 'hospital'},
    {'name': '地铁站D', 'location': [30.5880, 114.3020], 'type': 'subway'}
]

icon_map = {
    'school': {'color': 'blue', 'icon': 'graduation-cap'},
    'shopping': {'color': 'green', 'icon': 'shopping-cart'},
    'hospital': {'color': 'red', 'icon': 'plus'},
    'subway': {'color': 'purple', 'icon': 'subway'}
}

for facility in facilities_data:
    icon_info = icon_map.get(facility['type'], {'color': 'gray', 'icon': 'info'})
    folium.Marker(
        location=facility['location'],
        popup=facility['name'],
        icon=folium.Icon(color=icon_info['color'], icon=icon_info['icon'], prefix='fa')
    ).add_to(facilities)

# 创建房产标记图层
property_group = folium.FeatureGroup(name='房产项目')

for prop in properties:
    # 创建自定义HTML弹出窗口
    html = f"""
    <h3>{prop['name']}</h3>
    <table style="width:100%">
      <tr>
        <td>均价:</td>
        <td>{prop['price']}元/㎡</td>
      </tr>
      <tr>
        <td>面积:</td>
        <td>{prop['area']}㎡</td>
      </tr>
      <tr>
        <td>总价:</td>
        <td>{prop['price'] * prop['area'] / 10000:.2f}万元</td>
      </tr>
    </table>
    """
    
    iframe = folium.IFrame(html=html, width=200, height=150)
    popup = folium.Popup(iframe)
    
    # 圆形标记,大小与价格相关
    folium.CircleMarker(
        location=prop['location'],
        radius=prop['price']/1000,  # 价格越高,圆越大
        popup=popup,
        color='red',
        fill=True,
        fill_color='red',
        fill_opacity=0.6,
        tooltip=f"{prop['name']} - {prop['price']}元/㎡"
    ).add_to(property_group)

# 添加1公里和3公里半径圈(以市中心为原点)
travel_distance = folium.FeatureGroup(name='出行距离')

folium.Circle(
    location=[30.5928, 114.3055],
    radius=1000,  # 1公里
    color='blue',
    fill=True,
    fill_opacity=0.1,
    popup='1公里范围'
).add_to(travel_distance)

folium.Circle(
    location=[30.5928, 114.3055],
    radius=3000,  # 3公里
    color='green',
    fill=True,
    fill_opacity=0.1,
    popup='3公里范围'
).add_to(travel_distance)

# 将图层添加到地图
facilities.add_to(m)
property_group.add_to(m)
travel_distance.add_to(m)

# 添加图层控制
folium.LayerControl().add_to(m)

# 添加迷你地图
from folium.plugins import MiniMap
MiniMap().add_to(m)

m.save('real_estate_analysis.html')

6.2 交通路线规划与分析

# 创建地图
m = folium.Map(location=[30.5928, 114.3055], zoom_start=12)

# 添加起点和终点标记
start_point = [30.5928, 114.3055]  # 武汉大学
end_point = [30.5830, 114.2830]    # 华中科技大学

folium.Marker(
    location=start_point,
    popup='起点:武汉大学',
    icon=folium.Icon(color='green', icon='play', prefix='fa')
).add_to(m)

folium.Marker(
    location=end_point,
    popup='终点:华中科技大学',
    icon=folium.Icon(color='red', icon='stop', prefix='fa')
).add_to(m)

# 定义多条路线
routes = [
    {
        'name': '路线1 (最短)',
        'path': [
            start_point,
            [30.5900, 114.2950],
            [30.5850, 114.2900],
            end_point
        ],
        'color': 'blue',
        'time': '25分钟',
        'distance': '5.2公里'
    },
    {
        'name': '路线2 (最快)',
        'path': [
            start_point,
            [30.6000, 114.3000],
            [30.5950, 114.2850],
            end_point
        ],
        'color': 'red',
        'time': '20分钟',
        'distance': '6.5公里'
    },
    {
        'name': '路线3 (风景好)',
        'path': [
            start_point,
            [30.5800, 114.3100],
            [30.5750, 114.3000],
            [30.5800, 114.2900],
            end_point
        ],
        'color': 'green',
        'time': '30分钟',
        'distance': '7.0公里'
    }
]

# 创建路线图层
for route in routes:
    # 创建路线弹出信息
    html = f"""
    <h3>{route['name']}</h3>
    <p>行程时间: {route['time']}</p>
    <p>距离: {route['distance']}</p>
    """
    
    # 添加路线
    folium.PolyLine(
        locations=route['path'],
        color=route['color'],
        weight=5,
        opacity=0.7,
        popup=folium.Popup(html, max_width=300)
    ).add_to(m)

# 添加交通拥堵点
congestion_points = [
    {'location': [30.5880, 114.2930], 'level': 'high', 'time': '08:00-09:00'},
    {'location': [30.5950, 114.2980], 'level': 'medium', 'time': '17:00-19:00'}
]

for point in congestion_points:
    color = 'red' if point['level'] == 'high' else 'orange'
    folium.CircleMarker(
        location=point['location'],
        radius=10,
        color=color,
        fill=True,
        fill_opacity=0.7,
        popup=f"拥堵等级: {point['level']}<br>时段: {point['time']}"
    ).add_to(m)

# 添加交通信息图层控制
folium.LayerControl().add_to(m)

m.save('route_planning.html')

6.3 位置搜索与地理编码功能

# 创建地图
m = folium.Map(location=[30.5928, 114.3055], zoom_start=12)

# 添加搜索功能
from folium.plugins import Search

# 假设我们有POI数据
poi_data = {
    'type': 'FeatureCollection',
    'features': [
        {
            'type': 'Feature',
            'properties': {'name': '武汉大学', 'type': '教育'},
            'geometry': {'type': 'Point', 'coordinates': [114.3055, 30.5928]}
        },
        {
            'type': 'Feature',
            'properties': {'name': '华中科技大学', 'type': '教育'},
            'geometry': {'type': 'Point', 'coordinates': [114.2830, 30.5830]}
        },
        {
            'type': 'Feature',
            'properties': {'name': '武汉协和医院', 'type': '医疗'},
            'geometry': {'type': 'Point', 'coordinates': [114.3000, 30.5800]}
        }
    ]
}

# 创建GeoJSON图层
poi_layer = folium.GeoJson(
    poi_data,
    name='兴趣点',
    popup=folium.GeoJsonPopup(fields=['name', 'type'])
)
poi_layer.add_to(m)

# 添加搜索控件
search = Search(
    layer=poi_layer,
    geom_type='Point',
    placeholder='搜索地点...',
    collapsed=False,
    search_label='name'
)
m.add_child(search)

# 添加地理定位控件
from folium.plugins import LocateControl
LocateControl().add_to(m)

m.save('location_search.html')

6.4 时间序列数据可视化

# 创建地图
m = folium.Map(location=[30.5928, 114.3055], zoom_start=12)

# 准备时间序列数据(模拟一周内的交通流量)
time_data = []
days = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
np.random.seed(42)

for i, day in enumerate(days):
    # 模拟交通路口在不同时间的流量
    flow = np.random.normal(100, 30, 24)  # 24小时
    
    # 模拟高峰时段
    flow[7:9] += 100  # 早高峰
    flow[17:19] += 150  # 晚高峰
    
    # 周末模式不同
    if i >= 5:
        flow = flow * 0.7  # 周末交通量较低
        flow[10:14] += 80  # 午间购物高峰
    
    for hour in range(24):
        time_data.append({
            'day': day,
            'hour': hour,
            'flow': int(max(0, flow[hour])),
            'location': [30.5928, 114.3055]
        })

# 使用TimestampedGeoJson插件可视化时间序列数据
from folium.plugins import TimestampedGeoJson

# 准备GeoJSON格式数据
features = []
for entry in time_data:
    features.append({
        'type': 'Feature',
        'geometry': {
            'type': 'Point',
            'coordinates': [entry['location'][1], entry['location'][0]]
        },
        'properties': {
            'time': f"{entry['day']} {entry['hour']:02d}:00",
            'icon': 'circle',
            'iconstyle': {
                'fillColor': f"{'green' if entry['flow'] < 100 else 'yellow' if entry['flow'] < 200 else 'red'}",
                'fillOpacity': 0.8,
                'stroke': True,
                'radius': entry['flow'] / 20
            },
            'popup': f"{entry['day']} {entry['hour']:02d}:00<br>流量: {entry['flow']}"
        }
    })

# 添加时间序列图层
time_layer = TimestampedGeoJson(
    {
        'type': 'FeatureCollection',
        'features': features
    },
    period='PT1H',  # 1小时间隔
    duration='PT1H',  # 每个时间点显示1小时
    transition_time=200,  # 过渡动画时间
    auto_play=True,  # 自动播放
    loop=True  # 循环播放
)
time_layer.add_to(m)

m.save('time_series_visualization.html')

7. 与其他Python库集成

7.1 与Pandas集成

# 从Pandas DataFrame创建地图
# 假设我们有一个包含城市数据的DataFrame
cities_data = pd.DataFrame({
    'city': ['武汉', '北京', '上海', '广州', '深圳'],
    'lat': [30.5928, 39.9042, 31.2304, 23.1291, 22.5431],
    'lon': [114.3055, 116.4074, 121.4737, 113.2644, 114.0579],
    'population': [11.21, 21.54, 24.28, 15.31, 17.56],  # 单位:百万
    'gdp': [1.77, 3.61, 3.87, 2.39, 2.69]  # 单位:万亿元
})

# 创建地图
m = folium.Map(location=[35, 105], zoom_start=5)

# 添加城市标记,大小表示人口,颜色表示GDP
for _, city in cities_data.iterrows():
    # GDP决定颜色深浅
    gdp_normalized = (city['gdp'] - cities_data['gdp'].min()) / (cities_data['gdp'].max() - cities_data['gdp'].min())
    color = f'#{int(255 * (1 - gdp_normalized)):02x}0000'  # 从浅红到深红
    
    # 创建HTML信息窗口
    html = f"""
    <h3>{city['city']}</h3>
    <table>
      <tr><td>人口:</td><td>{city['population']}百万</td></tr>
      <tr><td>GDP:</td><td>{city['gdp']}万亿元</td></tr>
    </table>
    """
    iframe = folium.IFrame(html=html, width=200, height=100)
    popup = folium.Popup(iframe)
    
    # 创建圆形标记,人口决定大小
    folium.CircleMarker(
        location=[city['lat'], city['lon']],
        radius=city['population']/2,  # 人口越多,圆越大
        popup=popup,
        color=color,
        fill=True,
        fill_color=color,
        fill_opacity=0.6,
        tooltip=city['city']
    ).add_to(m)

m.save('pandas_integration.html')

7.2 与GeoPandas集成

# 安装GeoPandas
# pip install geopandas

import geopandas as gpd

# 加载中国省级行政区划数据
china = gpd.read_file('china_provinces.geojson')

# 创建一些示例数据
china['value'] = np.random.randint(100, 1000, size=len(china))

# 创建地图
m = folium.Map(location=[35, 105], zoom_start=4)

# 添加区域多边形
folium.GeoJson(
    china,
    name='中国省份',
    style_function=lambda feature: {
        'fillColor': '#ffff00' if feature['properties']['value'] < 500 else '#ff0000',
        'color': 'black',
        'weight': 1,
        'fillOpacity': 0.5
    },
    tooltip=folium.GeoJsonTooltip(
        fields=['name', 'value'],
        aliases=['省份:', '数值:'],
        localize=True
    ),
    popup=folium.GeoJsonPopup(
        fields=['name', 'value'],
        aliases=['省份:', '数值:'],
        localize=True
    )
).add_to(m)

# 添加分级统计图
folium.Choropleth(
    geo_data=china,
    name='分级统计图',
    data=china,
    columns=['name', 'value'],
    key_on='feature.properties.name',
    fill_color='YlOrRd',
    fill_opacity=0.7,
    line_opacity=0.2,
    legend_name='指标值'
).add_to(m)

# 添加图层控制
folium.LayerControl().add_to(m)

m.save('geopandas_integration.html')

7.3 与Matplotlib集成

# 在Folium地图中嵌入Matplotlib图表
import matplotlib.pyplot as plt
import io
import base64
from PIL import Image

# 创建地图
m = folium.Map(location=[30.5928, 114.3055], zoom_start=12)

# 创建Matplotlib图表
def create_chart(city_name, data):
    fig, ax = plt.subplots(figsize=(6, 4))
    categories = ['教育', '医疗', '交通', '环境', '文化']
    ax.bar(categories, data, color='skyblue')
    ax.set_ylim(0, 100)
    ax.set_title(f'{city_name}城市评分')
    ax.set_ylabel('得分')
    
    # 保存图表为内存中的图像
    img_bytes = io.BytesIO()
    plt.savefig(img_bytes, format='png', bbox_inches='tight')
    plt.close()
    img_bytes.seek(0)
    
    # 转换为base64编码
    encoded = base64.b64encode(img_bytes.read()).decode('utf-8')
    
    return encoded

# 城市数据
cities = [
    {'name': '武汉', 'location': [30.5928, 114.3055], 'scores': [85, 78, 82, 75, 88]},
    {'name': '上海', 'location': [31.2304, 121.4737], 'scores': [92, 90, 95, 80, 93]},
    {'name': '北京', 'location': [39.9042, 116.4074], 'scores': [90, 85, 88, 70, 95]}
]

# 为每个城市添加包含图表的标记
for city in cities:
    chart_img = create_chart(city['name'], city['scores'])
    
    # 创建包含图表的HTML
    html = f"""
    <h3>{city['name']}城市评分</h3>
    <img src="data:image/png;base64,{chart_img}" width="300">
    """
    
    iframe = folium.IFrame(html=html, width=320, height=280)
    popup = folium.Popup(iframe)
    
    folium.Marker(
        location=city['location'],
        popup=popup,
        tooltip=f"{city['name']}城市评分",
        icon=folium.Icon(color='blue', icon='info-sign')
    ).add_to(m)

m.save('matplotlib_integration.html')

8. 高级定制与性能优化

8.1 自定义样式与主题

# 创建自定义样式的地图
from branca.element import Template, MacroElement

# 创建基础地图
m = folium.Map(location=[30.5928, 114.3055], zoom_start=12, tiles='CartoDB dark_matter')

# 添加自定义CSS样式
custom_css = """
<style>
.leaflet-popup-content-wrapper {
    background-color: rgba(0, 0, 0, 0.8);
    color: white;
    border-radius: 0px;
    padding: 0px;
}
.leaflet-popup-content {
    margin: 15px;
}
.leaflet-popup-tip {
    background-color: rgba(0, 0, 0, 0.8);
}
.leaflet-container a.leaflet-popup-close-button {
    color: white;
}
.leaflet-control-layers {
    background-color: rgba(0, 0, 0, 0.7);
    color: white;
    border-radius: 0px;
}
.leaflet-control-layers-expanded {
    padding: 10px;
}
.legend {
    background-color: rgba(0, 0, 0, 0.7);
    color: white;
    padding: 10px;
    border-radius: 0px;
}
</style>
"""

# 创建自定义CSS元素
class CustomCSS(MacroElement):
    def __init__(self, css):
        super(CustomCSS, self).__init__()
        self._css = css
        
    def render(self, **kwargs):
        self._parent.header.add_child(Template(self._css))

# 添加自定义CSS到地图
m.get_root().add_child(CustomCSS(custom_css))

# 添加一些标记和弹出窗口
locations = [
    {'name': '地点A', 'location': [30.5928, 114.3055], 'info': '这是地点A的详细信息'},
    {'name': '地点B', 'location': [30.6000, 114.3100], 'info': '这是地点B的详细信息'},
    {'name': '地点C', 'location': [30.5850, 114.2950], 'info': '这是地点C的详细信息'}
]

for loc in locations:
    html = f"""
    <div style="font-family: 'Arial', sans-serif;">
        <h3 style="margin-top: 0;">{loc['name']}</h3>
        <p>{loc['info']}</p>
    </div>
    """
    
    folium.Marker(
        location=loc['location'],
        popup=folium.Popup(html),
        tooltip=loc['name'],
        icon=folium.Icon(color='white', icon_color='#1a1a1a', icon='info')
    ).add_to(m)

# 添加自定义图例
legend_html = """
<div class='legend'>
    <h4 style="margin-top: 0;">图例</h4>
    <div><i class="fa fa-circle" style="color:white"></i> 兴趣点</div>
</div>
"""

# 添加图例
m.get_root().html.add_child(folium.Element(legend_html))

m.save('custom_styled_map.html')

8.2 性能优化与大数据处理

# 处理大量数据点的策略
# 创建地图
m = folium.Map(location=[30.5928, 114.3055], zoom_start=12)

# 方法1: 使用聚类减少标记数量
from folium.plugins import MarkerCluster

# 生成大量标记点
np.random.seed(42)
n_points = 10000  # 一万个点

# 创建聚类
marker_cluster = MarkerCluster(
    name='聚类标记',
    overlay=True,
    control=True,
    icon_create_function=None
).add_to(m)

# 添加部分点到聚类中
for i in range(min(1000, n_points)):  # 限制初始加载点数
    lat = np.random.normal(30.59, 0.05)
    lon = np.random.normal(114.30, 0.05)
    
    folium.Marker(
        location=[lat, lon],
        popup=f'点 {i+1}'
    ).add_to(marker_cluster)

# 方法2: 使用热力图代替大量单独标记
from folium.plugins import HeatMap

# 生成热力图数据
heat_data = []
for i in range(n_points):
    lat = np.random.normal(30.59, 0.05)
    lon = np.random.normal(114.30, 0.05)
    intensity = np.random.uniform(0.1, 1.0)  # 随机强度
    heat_data.append([lat, lon, intensity])

# 添加热力图
HeatMap(
    heat_data,
    name='热力图',
    radius=10,
    blur=15,
    gradient={0.4: 'blue', 0.65: 'lime', 1: 'red'}
).add_to(m)

# 方法3: 使用WebGL点图层
from folium.plugins import FastMarkerCluster

# 准备数据
callback = """
function (row) {
    var icon = L.divIcon({
        html: '<div style="background-color: #3186cc; border-radius: 50%; width: 5px; height: 5px;"></div>',
        className: 'dummy'
    });
    return L.marker(new L.LatLng(row[0], row[1]), {icon: icon});
};
"""

fast_markers = np.random.normal(
    loc=[30.59, 114.30], 
    scale=[0.05, 0.05], 
    size=(n_points, 2)
).tolist()

# 添加快速标记聚类
FastMarkerCluster(
    data=fast_markers,
    name='快速点图层',
    callback=callback
).add_to(m)

# 添加图层控制
folium.LayerControl().add_to(m)

m.save('performance_optimized_map.html')

8.3 移动端优化

# 为移动设备优化地图
m = folium.Map(
    location=[30.5928, 114.3055],
    zoom_start=12,
    preferCanvas=True  # 使用Canvas渲染,提高移动端性能
)

# 添加定位控件
from folium.plugins import LocateControl
LocateControl(
    position='topleft',
    strings={'title': '定位'},
    locateOptions={
        'enableHighAccuracy': True,  # 高精度定位
        'watch': True,  # 持续追踪位置
        'setView': True,  # 定位后自动将视图移动到当前位置
        'maxZoom': 16,  # 最大缩放级别
        'timeout': 10000  # 定位超时时间(毫秒)
    }
).add_to(m)

# 添加移动友好的图层控制
from folium.plugins import GroupedLayerControl

# 创建不同组的图层
poi_group = folium.FeatureGroup(name='兴趣点')
poi_markers = [
    {'name': '餐厅A', 'location': [30.5928, 114.3055], 'type': 'restaurant'},
    {'name': '商店B', 'location': [30.5950, 114.3100], 'type': 'shopping'},
    {'name': '景点C', 'location': [30.5900, 114.3000], 'type': 'attraction'}
]

for poi in poi_markers:
    folium.Marker(
        location=poi['location'],
        popup=poi['name'],
        tooltip=poi['name'],
        icon=folium.Icon(
            icon='cutlery' if poi['type'] == 'restaurant' else
                 'shopping-cart' if poi['type'] == 'shopping' else
                 'camera',
            prefix='fa'
        )
    ).add_to(poi_group)

# 添加交通图层
transport_group = folium.FeatureGroup(name='交通')
bus_stops = [
    {'name': '公交站A', 'location': [30.5920, 114.3050]},
    {'name': '公交站B', 'location': [30.5940, 114.3070]},
    {'name': '地铁站C', 'location': [30.5910, 114.3030]}
]

for stop in bus_stops:
    folium.CircleMarker(
        location=stop['location'],
        radius=5,
        color='blue',
        fill=True,
        popup=stop['name']
    ).add_to(transport_group)

# 将图层添加到地图
poi_group.add_to(m)
transport_group.add_to(m)

# 添加移动友好的图层控制
folium.LayerControl(collapsed=True).add_to(m)  # 默认折叠图层控制面板

# 添加全屏控件
from folium.plugins import Fullscreen
Fullscreen(
    position='topleft',
    title='全屏',
    title_cancel='退出全屏',
    force_separate_button=True
).add_to(m)

# 添加响应式设计的元标签
meta_html = """
    <meta name="viewport" content="width=device-width, 
        initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
"""
m.get_root().header.add_child(folium.Element(meta_html))

# 添加触摸事件优化的CSS
touch_css = """
    <style>
    .leaflet-touch .leaflet-control-layers,
    .leaflet-touch .leaflet-bar {
        border: 2px solid rgba(0,0,0,0.2);
        background-clip: padding-box;
    }
    
    .leaflet-control-layers-toggle {
        width: 36px !important;
        height: 36px !important;
    }
    
    .leaflet-control-zoom-in, 
    .leaflet-control-zoom-out {
        width: 36px !important;
        height: 36px !important;
        line-height: 36px !important;
        font-size: 18px !important;
    }
    </style>
"""
m.get_root().header.add_child(folium.Element(touch_css))

m.save('mobile_optimized_map.html')

9. 实战项目案例

9.1 城市数据分析面板

# 创建综合城市数据分析面板
m = folium.Map(
    location=[30.5928, 114.3055],
    zoom_start=12,
    tiles='CartoDB positron'
)

# 添加城市行政区划
admin_data = {
    'type': 'FeatureCollection',
    'features': [
        {
            'type': 'Feature',
            'properties': {
                'name': '武昌区',
                'population': 1200000,
                'gdp': 125,
                'area': 82.4
            },
            'geometry': {
                'type': 'Polygon',
                'coordinates': [[[114.32, 30.57], [114.35, 30.57], [114.35, 30.60], [114.32, 30.60], [114.32, 30.57]]]
            }
        },
        {
            'type': 'Feature',
            'properties': {
                'name': '洪山区',
                'population': 1650000,
                'gdp': 145,
                'area': 123.1
            },
            'geometry': {
                'type': 'Polygon',
                'coordinates': [[[114.35, 30.57], [114.38, 30.57], [114.38, 30.60], [114.35, 30.60], [114.35, 30.57]]]
            }
        },
        {
            'type': 'Feature',
            'properties': {
                'name': '江汉区',
                'population': 950000,
                'gdp': 110,
                'area': 33.2
            },
            'geometry': {
                'type': 'Polygon',
                'coordinates': [[[114.29, 30.60], [114.32, 30.60], [114.32, 30.63], [114.29, 30.63], [114.29, 30.60]]]
            }
        }
    ]
}

# 添加分级统计图 - 人口密度
folium.Choropleth(
    geo_data=admin_data,
    name='人口密度',
    data=pd.DataFrame([
        {'name': '武昌区', 'density': 1200000/82.4},
        {'name': '洪山区', 'density': 1650000/123.1},
        {'name': '江汉区', 'density': 950000/33.2}
    ]),
    columns=['name', 'density'],
    key_on='feature.properties.name',
    fill_color='YlOrRd',
    fill_opacity=0.7,
    line_opacity=0.2,
    legend_name='人口密度 (人/平方公里)'
).add_to(m)

# 添加详细信息弹出窗口
style_function = lambda x: {'fillColor': '#ffffff', 'color': '#000000', 'fillOpacity': 0.1, 'weight': 0.5}
highlight_function = lambda x: {'fillColor': '#000000', 'color': '#000000', 'fillOpacity': 0.5, 'weight': 1}

admin_info = folium.features.GeoJson(
    admin_data,
    name='行政区信息',
    style_function=style_function,
    highlight_function=highlight_function,
    tooltip=folium.features.GeoJsonTooltip(
        fields=['name', 'population', 'gdp', 'area'],
        aliases=['区名:', '人口:', 'GDP (亿元):', '面积 (平方公里):'],
        localize=True
    ),
    popup=folium.features.GeoJsonPopup(
        fields=['name', 'population', 'gdp', 'area'],
        aliases=['区名:', '人口:', 'GDP (亿元):', '面积 (平方公里):'],
        localize=True
    )
)
m.add_child(admin_info)

# 添加交通设施图层
transport_layer = folium.FeatureGroup(name='交通设施')

# 地铁站
subway_stations = [
    {'name': '洪山广场站', 'location': [30.5928, 114.3055], 'lines': [2, 4]},
    {'name': '中南路站', 'location': [30.5380, 114.3330], 'lines': [2, 8]},
    {'name': '街道口站', 'location': [30.5280, 114.3520], 'lines': [2]}
]

for station in subway_stations:
    # 创建HTML格式的弹出窗口
    lines_str = ', '.join([f'{line}号线' for line in station['lines']])
    html = f"""
    <div style="width:200px">
        <h4 style="margin-bottom:5px">{station['name']}</h4>
        <p><b>线路:</b> {lines_str}</p>
        <p><b>换乘数:</b> {len(station['lines'])}</p>
    </div>
    """
    
    # 创建地铁站标记
    folium.Marker(
        location=station['location'],
        icon=folium.Icon(icon='subway', prefix='fa', color='blue'),
        popup=folium.Popup(html),
        tooltip=f"{station['name']} ({lines_str})"
    ).add_to(transport_layer)

# 公交站
bus_stops = [
    {'name': '中南医院站', 'location': [30.5830, 114.3130], 'lines': [12, 542, 806]},
    {'name': '武汉大学站', 'location': [30.5928, 114.3070], 'lines': [592, 519, 552]}
]

for stop in bus_stops:
    lines_str = ', '.join([f'{line}路' for line in stop['lines']])
    folium.Marker(
        location=stop['location'],
        icon=folium.Icon(icon='bus', prefix='fa', color='green'),
        popup=f"{stop['name']}<br>线路: {lines_str}",
        tooltip=stop['name']
    ).add_to(transport_layer)

# 添加交通设施图层到地图
transport_layer.add_to(m)

# 添加热门地点图层
poi_layer = folium.FeatureGroup(name='热门地点')

# 添加热门地点
pois = [
    {'name': '武汉大学', 'location': [30.5928, 114.3055], 'type': 'education', 'rating': 5.0},
    {'name': '东湖风景区', 'location': [30.5800, 114.3600], 'type': 'scenic', 'rating': 4.8},
    {'name': '黄鹤楼', 'location': [30.5433, 114.3025], 'type': 'landmark', 'rating': 4.9},
    {'name': '武汉天地', 'location': [30.5867, 114.2819], 'type': 'shopping', 'rating': 4.6}
]

# 为不同类型选择不同图标
icon_map = {
    'education': {'icon': 'graduation-cap', 'color': 'blue'},
    'scenic': {'icon': 'tree', 'color': 'green'},
    'landmark': {'icon': 'monument', 'color': 'red'},
    'shopping': {'icon': 'shopping-cart', 'color': 'purple'}
}

for poi in pois:
    icon_info = icon_map.get(poi['type'], {'icon': 'info', 'color': 'gray'})
    html = f"""
    <div style="width:200px">
        <h4 style="margin-bottom:5px">{poi['name']}</h4>
        <p>类型: {poi['type']}</p>
        <p>评分: {"★" * int(poi['rating']) + "☆" * (5 - int(poi['rating']))}</p>
    </div>
    """
    
    folium.Marker(
        location=poi['location'],
        popup=folium.Popup(html),
        tooltip=poi['name'],
        icon=folium.Icon(icon=icon_info['icon'], prefix='fa', color=icon_info['color'])
    ).add_to(poi_layer)

# 添加热门地点图层到地图
poi_layer.add_to(m)

# 添加房价热力图 (模拟数据)
np.random.seed(42)
n_points = 500
housing_prices = []

# 生成以市中心为中心的房价分布(越靠近中心越贵)
center = np.array([30.5928, 114.3055])  # 市中心坐标
for _ in range(n_points):
    # 生成随机位置
    offset = np.random.normal(0, 0.03, 2)  # 标准差控制分布范围
    location = center + offset
    
    # 计算到中心的距离
    distance = np.sqrt(np.sum(offset**2))
    
    # 基于距离计算房价(距离越远价格越低)
    price = 30000 * np.exp(-5 * distance) + np.random.normal(0, 2000)
    
    housing_prices.append([location[0], location[1], max(8000, price)])

# 添加房价热力图
heat_layer = folium.FeatureGroup(name='房价热力图')
HeatMap(
    housing_prices,
    name='房价分布',
    radius=15,
    gradient={0.4: 'blue', 0.65: 'lime', 1: 'red'},
    min_opacity=0.5
).add_to(heat_layer)
heat_layer.add_to(m)

# 添加迷你地图和图层控制
from folium.plugins import MiniMap, Draw
minimap = MiniMap()
m.add_child(minimap)
folium.LayerControl(collapsed=False).add_to(m)

# 添加绘图工具
draw = Draw(
    export=True,
    position='topleft',
    draw_options={
        'polyline': True,
        'rectangle': True,
        'circle': True,
        'marker': True,
        'circlemarker': False
    }
)
m.add_child(draw)

# 添加地图标题
title_html = '''
    <div style="position: fixed; 
        top: 10px; left: 50%; transform: translateX(-50%);
        z-index: 9999; background-color: white; 
        padding: 10px; border-radius: 5px; box-shadow: 0 0 5px rgba(0,0,0,0.3);">
        <h3 style="margin:0;">武汉市城市数据分析面板</h3>
    </div>
'''
m.get_root().html.add_child(folium.Element(title_html))

m.save('wuhan_city_analysis.html')

9.2 环境监测与灾害管理系统

# 创建环境监测与灾害管理系统
m = folium.Map(
    location=[30.5928, 114.3055],
    zoom_start=11,
    tiles='CartoDB positron'
)

# 添加多种底图
folium.TileLayer('CartoDB dark_matter', name='暗色底图').add_to(m)
folium.TileLayer('Stamen Terrain', name='地形图').add_to(m)
folium.TileLayer('Stamen Watercolor', name='水彩风格').add_to(m)

# 模拟空气质量监测站点数据
air_quality_stations = [
    {'name': '监测站A', 'location': [30.5928, 114.3055], 'aqi': 75, 'pm25': 35, 'pm10': 65, 'status': 'good'},
    {'name': '监测站B', 'location': [30.6100, 114.2800], 'aqi': 110, 'pm25': 58, 'pm10': 95, 'status': 'moderate'},
    {'name': '监测站C', 'location': [30.5600, 114.3300], 'aqi': 145, 'pm25': 85, 'pm10': 120, 'status': 'unhealthy'},
    {'name': '监测站D', 'location': [30.5300, 114.2700], 'aqi': 55, 'pm25': 22, 'pm10': 48, 'status': 'good'},
    {'name': '监测站E', 'location': [30.6300, 114.3400], 'aqi': 165, 'pm25': 105, 'pm10': 145, 'status': 'very_unhealthy'}
]

# 创建空气质量图层
air_quality_layer = folium.FeatureGroup(name='空气质量监测站')

# 为不同空气质量状态定义颜色
status_colors = {
    'good': 'green',
    'moderate': 'yellow',
    'unhealthy': 'orange',
    'very_unhealthy': 'red',
    'hazardous': 'purple'
}

# 添加监测站点
for station in air_quality_stations:
    color = status_colors.get(station['status'], 'gray')
    
    html = f"""
    <div style="width:200px">
        <h4 style="margin-bottom:5px">{station['name']}</h4>
        <table style="width:100%">
            <tr><td>AQI:</td><td>{station['aqi']}</td></tr>
            <tr><td>PM2.5:</td><td>{station['pm25']} μg/m³</td></tr>
            <tr><td>PM10:</td><td>{station['pm10']} μg/m³</td></tr>
            <tr><td>状态:</td><td style="color:{color};font-weight:bold">{station['status'].replace('_', ' ').title()}</td></tr>
        </table>
    </div>
    """
    
    folium.CircleMarker(
        location=station['location'],
        radius=10,
        color=color,
        fill=True,
        fill_color=color,
        fill_opacity=0.7,
        popup=folium.Popup(html, max_width=200),
        tooltip=f"{station['name']} - AQI: {station['aqi']}"
    ).add_to(air_quality_layer)

# 添加空气质量图层到地图
air_quality_layer.add_to(m)

# 模拟洪水风险区域
flood_zones = {
    'high_risk': {
        'polygon': [
            [30.56, 114.30],
            [30.56, 114.33],
            [30.54, 114.33],
            [30.54, 114.30]
        ],
        'color': 'red',
        'name': '高风险区'
    },
    'medium_risk': {
        'polygon': [
            [30.60, 114.26],
            [30.60, 114.29],
            [30.58, 114.29],
            [30.58, 114.26]
        ],
        'color': 'orange',
        'name': '中风险区'
    },
    'low_risk': {
        'polygon': [
            [30.63, 114.31],
            [30.63, 114.34],
            [30.61, 114.34],
            [30.61, 114.31]
        ],
        'color': 'yellow',
        'name': '低风险区'
    }
}

# 创建洪水风险图层
flood_risk_layer = folium.FeatureGroup(name='洪水风险区域')

# 添加风险区域多边形
for risk_id, risk_data in flood_zones.items():
    folium.Polygon(
        locations=risk_data['polygon'],
        color=risk_data['color'],
        fill=True,
        fill_color=risk_data['color'],
        fill_opacity=0.4,
        weight=2,
        popup=risk_data['name']
    ).add_to(flood_risk_layer)

# 添加洪水风险图层到地图
flood_risk_layer.add_to(m)

# 添加避险场所
shelters = [
    {'name': '避险场所A', 'location': [30.5700, 114.3100], 'capacity': 2000, 'supplies': True},
    {'name': '避险场所B', 'location': [30.5950, 114.2850], 'capacity': 1500, 'supplies': True},
    {'name': '避险场所C', 'location': [30.6150, 114.3250], 'capacity': 3000, 'supplies': False}
]

# 创建避险场所图层
shelter_layer = folium.FeatureGroup(name='避险场所')

# 添加避险场所标记
for shelter in shelters:
    html = f"""
    <div style="width:200px">
        <h4 style="margin-bottom:5px">{shelter['name']}</h4>
        <table style="width:100%">
            <tr><td>容纳人数:</td><td>{shelter['capacity']}人</td></tr>
            <tr><td>应急物资:</td><td>{'充足' if shelter['supplies'] else '不足'}</td></tr>
        </table>
    </div>
    """
    
    folium.Marker(
        location=shelter['location'],
        popup=folium.Popup(html, max_width=200),
        tooltip=shelter['name'],
        icon=folium.Icon(color='green', icon='home', prefix='fa')
    ).add_to(shelter_layer)

# 添加避险场所图层到地图
shelter_layer.add_to(m)

# 添加实时降雨量热力图
np.random.seed(42)
rain_data = []
center = np.array([30.5928, 114.3055])  # 降雨中心

# 模拟降雨分布(从中心向外递减)
for _ in range(500):
    offset = np.random.normal(0, 0.05, 2)  # 标准差控制分布范围
    location = center + offset
    
    # 计算到中心的距离
    distance = np.sqrt(np.sum(offset**2))
    
    # 基于距离计算降雨量(越靠近中心降雨越大)
    intensity = 10 * np.exp(-5 * distance) + np.random.normal(0, 0.5)
    intensity = max(0, intensity)
    
    rain_data.append([location[0], location[1], intensity])

# 创建降雨量图层
rain_layer = folium.FeatureGroup(name='实时降雨量')

# 添加降雨热力图
HeatMap(
    rain_data,
    name='降雨分布',
    radius=12,
    gradient={0.4: '#a6cee3', 0.65: '#1f78b4', 1: '#081d58'},
    min_opacity=0.3
).add_to(rain_layer)

# 添加降雨图层到地图
rain_layer.add_to(m)

# 添加图层控制
folium.LayerControl(collapsed=False).add_to(m)

# 添加全屏控件
from folium.plugins import Fullscreen
Fullscreen().add_to(m)

# 添加测量工具
from folium.plugins import MeasureControl
m.add_child(MeasureControl(position='bottomleft', primary_length_unit='kilometers'))

# 添加比例尺
folium.plugins.FloatImage('https://upload.wikimedia.org/wikipedia/commons/thumb/b/b0/North_arrow_01.svg/1200px-North_arrow_01.svg.png', 
                         bottom=2, left=1, width='40px').add_to(m)

# 添加图例
legend_html = '''
<div style="position: fixed; 
    bottom: 50px; right: 50px; z-index: 1000; background-color: white; 
    padding: 10px; border-radius: 5px; box-shadow: 0 0 5px rgba(0,0,0,0.3);">
    <p><strong>空气质量</strong></p>
    <p><i class="fa fa-circle" style="color:green"></i> 良好</p>
    <p><i class="fa fa-circle" style="color:yellow"></i> 中等</p>
    <p><i class="fa fa-circle" style="color:orange"></i> 不健康</p>
    <p><i class="fa fa-circle" style="color:red"></i> 很不健康</p>
    <p><i class="fa fa-circle" style="color:purple"></i> 危险</p>
    <p><strong>洪水风险区域</strong></p>
    <p><i class="fa fa-square" style="color:red"></i> 高风险</p>
    <p><i class="fa fa-square" style="color:orange"></i> 中风险</p>
    <p><i class="fa fa-square" style="color:yellow"></i> 低风险</p>
</div>
'''
m.get_root().html.add_child(folium.Element(legend_html))

# 添加标题
title_html = '''
<div style="position: fixed; 
    top: 10px; left: 50%; transform: translateX(-50%);
    z-index:9999; background-color: white; 
    padding: 10px; border-radius: 5px; box-shadow: 0 0 5px rgba(0,0,0,0.3);">
    <h3 style="margin:0;">环境监测与灾害管理系统</h3>
</div>
'''
m.get_root().html.add_child(folium.Element(title_html))

m.save('disaster_management_system.html')

10. 总结与进阶资源

10.1 学习路径推荐

Folium是一个功能强大的Python地图可视化库,掌握它可以帮助你创建专业级的交互式地图应用。以下是推荐的学习路径:

  1. 基础入门

    • 掌握基本地图创建
    • 学习添加标记、线条和多边形
    • 了解弹出窗口和工具提示的使用
  2. 中级应用

    • 掌握分级统计图(Choropleth)的创建
    • 学习热力图和标记聚类的应用
    • 了解图层控制和自定义图标
  3. 高级功能

    • 掌握插件的使用(热力图、聚类、时间序列等)
    • 学习与其他库(Pandas、GeoPandas、Matplotlib)的集成
    • 了解性能优化和大数据处理技巧
  4. 实战应用

    • 完成综合项目,如城市分析面板、环境监测系统等
    • 将地图嵌入到Web应用中
    • 创建交互式地图仪表板

10.2 常见问题与解决方案

  1. 地图无法显示

    • 检查Folium版本是否最新
    • 确认所有依赖库是否正确安装
    • 检查HTML文件是否在支持JavaScript的环境中运行
  2. 自定义图标不显示

    • 确认图标名称是否正确
    • 检查图标前缀(prefix)是否正确设置
    • 尝试使用不同的图标库(‘fa’, 'glyphicon’等)
  3. GeoJSON数据加载问题

    • 确保GeoJSON格式正确
    • 检查坐标系统是否一致
    • 尝试使用更简化的GeoJSON数据
  4. 性能问题

    • 使用聚类或热力图代替大量单独标记
    • 减少不必要的弹出窗口内容
    • 考虑使用prefer_canvas=True选项提高渲染性能
  5. 移动设备兼容性

    • 添加适当的元标签和CSS样式
    • 使用更大的按钮和控件
    • 测试不同设备上的显示效果

10.3 进阶资源推荐

  1. 官方文档

  2. 教程和示例

  3. 相关库

    • GeoPandas:用于地理数据处理
    • Branca:Folium的依赖库,用于颜色映射
    • ipyleaflet:另一个基于Leaflet的交互式地图库
  4. 书籍

    • 《Python地理空间分析指南》
    • 《交互式数据可视化:用D3.js和Leaflet创建地图》

10.4 结语

Folium为Python开发者提供了一个强大的工具,让我们能够在不深入学习JavaScript的情况下创建精美的交互式地图。通过本文介绍的各种功能和技巧,你已经掌握了使用Folium进行地理数据可视化的核心能力。

从简单的点标记到复杂的分级统计图,从基础地图到综合的城市数据分析面板,Folium的灵活性和易用性使它成为地理数据可视化的首选工具之一。随着智慧城市、位置服务和空间分析的不断发展,这些技能将变得越来越重要。

希望本教程能帮助你开启地理数据可视化的探索之旅,创建出更多精彩的交互式地图应用!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Is code

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值