Spaces:
Sleeping
Sleeping
File size: 7,144 Bytes
0640b0e |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
import numpy as np
import folium
import matplotlib.pyplot as plt
from folium.raster_layers import ImageOverlay
import geopandas as gpd
from shapely.geometry import Point
from scipy.ndimage import gaussian_filter
from matplotlib.colors import LinearSegmentedColormap
import pandas as pd
from sklearn.ensemble import RandomForestRegressor
from scipy.ndimage import gaussian_filter
from matplotlib.colors import LinearSegmentedColormap
from branca.element import Template, MacroElement
# ==== 1. Dữ liệu ban đầu ====
# for idx, pre in enumerate(timelamp):
def plot_map(filtered_df):
idx = 'pre'
vn_mainland = gpd.read_file("map/gadm41_VNM_1.json")
hoang_sa = gpd.read_file("map/gadm36_XSP_0.json")
truong_sa = gpd.read_file("map/gadm36_XPI_0.json")
# Gộp lại thành một GeoDataFrame
vn_full = gpd.GeoDataFrame(pd.concat([vn_mainland, hoang_sa, truong_sa], ignore_index=True), crs='EPSG:4326')
m = folium.Map(location=[14.0583, 108.2772], zoom_start=6, tiles='CartoDB Positron')
X = filtered_df[['Kinh độ', 'Vĩ độ']]
y = filtered_df['AQI_PM2.5']
model = RandomForestRegressor(n_estimators=100, random_state=42)
model.fit(X, y)
# === 2. Tạo lưới để dự đoán ===
lat_min, lat_max = X['Kinh độ'].min()-1, X['Kinh độ'].max()+1
lon_min, lon_max = X['Vĩ độ'].min()-1, X['Vĩ độ'].max()+1
grid_lat = np.linspace(lat_min, lat_max, 300)
grid_lon = np.linspace(lon_min, lon_max, 300)
grid_lon_mesh, grid_lat_mesh = np.meshgrid(grid_lon, grid_lat)
grid_points = np.c_[grid_lat_mesh.ravel(), grid_lon_mesh.ravel()]
grid_intensity = model.predict(grid_points).reshape(grid_lat_mesh.shape)
grid_intensity = gaussian_filter(grid_intensity, sigma=2)
vn_union = vn_full.union_all() # Nối tất cả vùng lại thành 1 polygon
# Tạo mask: điểm nào ngoài biên VN thì đặt NaN
mask = np.full(grid_intensity.shape, False)
for i in range(grid_lat_mesh.shape[0]):
for j in range(grid_lat_mesh.shape[1]):
point = Point(grid_lon_mesh[i, j], grid_lat_mesh[i, j])
if not vn_union.contains(point):
mask[i, j] = True
grid_intensity_masked = np.ma.array(grid_intensity, mask=mask)
plt.figure(figsize=(6, 6))
# plt.imshow(grid_intensity_masked, extent=(lon_min, lon_max, lat_min, lat_max),
# origin='lower', cmap='hot', alpha=0.6)
# Danh sách màu chuẩn AQI theo thứ tự tăng dần
aqi_colors = [
'#00e400', # Green (0–50)
'#ffff00', # Yellow (51–100)
'#ff7e00', # Orange (101–150)
'#ff0000', # Red (151–200)
'#8f3f97', # Purple (201–300)
'#7e0023' # Maroon (301–500)
]
# Tạo colormap nội suy từ danh sách trên
aqi_cmap = LinearSegmentedColormap.from_list("aqi_smooth", aqi_colors, N=100)
plt.imshow(
grid_intensity_masked,
extent=(lon_min, lon_max, lat_min, lat_max),
origin='lower',
cmap=aqi_cmap, # Hoặc 'plasma', 'turbo', 'jet', 'YlOrRd'
alpha=0.75,
vmin=0,
vmax=400
)
plt.axis('off')
plt.savefig(f"images/heatmap_overlay {idx}.png", bbox_inches='tight', pad_inches=0, transparent=True)
plt.close()
# ==== 4. Tạo bản đồ và chèn ảnh ====
# Overlay ảnh lên đúng vùng địa lý
ImageOverlay(
image=f"images/heatmap_overlay {idx}.png",
bounds=[[lat_min, lon_min], [lat_max, lon_max]],
opacity=0.6,
interactive=True,
cross_origin=False
).add_to(m)
# Đọc và thêm biên giới VN nếu có
try:
folium.GeoJson(
vn_full,
name='Biên giới',
style_function=lambda x: {
'fillColor': 'none',
'color': 'black',
'weight': 1,
'dashArray': '5, 5'
}
).add_to(m)
except:
print("Không tìm thấy tệp GeoJSON biên giới.")
def get_folium_color(aqi):
if aqi <= 50:
return 'green'
elif aqi <= 100:
return 'beige' # gần giống yellow
elif aqi <= 150:
return 'orange'
elif aqi <= 200:
return 'red'
elif aqi <= 300:
return 'purple'
else:
return 'darkred'
for name, lat, lon, aqi_value in zip(filtered_df["Tên"], filtered_df["Kinh độ"], filtered_df["Vĩ độ"], filtered_df["AQI_PM2.5"]):
# aqi_value = intensity_values[list(coordinates.keys()).index(name)]
fill_color = (
'green' if aqi_value <= 50 else
'yellow' if aqi_value <= 100 else
'orange' if aqi_value <= 150 else
'red' if aqi_value <= 200 else
'purple' if aqi_value <= 300 else
'maroon'
)
folium.Marker(
location=(lat, lon),
popup=f"{name}: AQI PM2.5 = {aqi_value:.1f}",
icon=folium.Icon(color=get_folium_color(aqi_value), icon="info-sign") # icon bạn có thể đổi thành 'cloud', 'leaf', 'home'...
).add_to(m)
folium.CircleMarker(
location=(lat, lon),
radius=6,
popup=f"{name}: pm2.5 {aqi_value}",
color='black',
weight=0.5,
fill=True,
fill_color=fill_color,
fill_opacity=0.8
).add_to(m)
folium.Marker(
location=[16.5053, 111.9537],
icon=folium.DivIcon(
html='<div style="font-size: 12px; color: black; font-weight: bold;">Hoàng Sa</div>'
)
).add_to(m)
folium.Marker(
location=[9.9342, 114.3302],
icon=folium.DivIcon(
html='<div style="font-size: 12px; color: black; font-weight: bold;">Trường Sa</div>'
)
).add_to(m)
folium.LayerControl().add_to(m)
# m.save(f"heatmap_image_overlay{idx}.html")
print("✅ Đã tạo bản đồ heatmap với overlay hình ảnh: heatmap_image_overlay.html")
colorbar_template = """
{% macro html(this, kwargs) %}
<div style="
position: fixed;
bottom: 50px;
left: 50px;
width: 200px;
height: 20px;
z-index:9999;
background: linear-gradient(to right, green, yellow, orange, red, purple, maroon);
border: 1px solid black;
text-align: center;
font-size: 12px;
color: black;">
0 50 100 150 200 300 500+
</div>
{% endmacro %}
"""
colorbar = MacroElement()
colorbar._template = Template(colorbar_template)
m.get_root().add_child(colorbar)
m.save(f"heatmap_image_overlaycolorbar.html")
m
return m |