Spaces:
Sleeping
Sleeping
import streamlit as st | |
import geopandas as gpd | |
import pandas as pd | |
import plotly.graph_objects as go | |
from huggingface_hub import hf_hub_download | |
import matplotlib.pyplot as plt | |
import warnings | |
import io | |
from shapely.geometry import Polygon, MultiPolygon | |
# Page configuration | |
st.set_page_config(layout="wide", page_title="Geographic Data Visualization") | |
# Suppress warnings | |
warnings.filterwarnings('ignore') | |
def load_data(): | |
# Load world map data | |
world_gdf = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres')) | |
return world_gdf | |
def main(): | |
st.title("🗺️ Geographic Data Visualization") | |
# Load data | |
world_gdf = load_data() | |
# Country selection | |
selected_country = st.selectbox( | |
"Select a Country", | |
sorted(world_gdf['name'].unique()) | |
) | |
# Filter data for selected country | |
gdf = world_gdf[world_gdf['name'] == selected_country].copy() | |
# Legend settings | |
st.sidebar.header("Legend Settings") | |
legend_title = st.sidebar.text_input("Legend Title", "Regions") | |
legend_location = st.sidebar.selectbox("Legend Location", | |
["best", "upper right", "upper left", "lower left", "lower right"], | |
index=0) | |
legend_font_size = st.sidebar.slider("Legend Font Size", 8, 20, 12) | |
legend_font_weight = st.sidebar.selectbox("Legend Font Weight", ["Normal", "Bold"], index=0) | |
legend_columns = st.sidebar.slider("Legend Columns", 1, 3, 1) | |
legend_transparency = st.sidebar.slider("Legend Transparency", 0.0, 1.0, 0.1) | |
legend_bg_color = st.sidebar.color_picker("Legend Background Color", "#FFFFFF") | |
legend_border_color = st.sidebar.color_picker("Legend Border Color", "#000000") | |
legend_border_width = st.sidebar.slider("Legend Border Width", 0, 5, 1) | |
# Special handling for Pakistan | |
if selected_country == "Pakistan": | |
# Define administrative levels for Pakistan | |
admin_levels = {} | |
for i in range(6): | |
col_name = f"NAME_{i}" | |
if col_name in gdf.columns and not gdf[col_name].isna().all(): | |
level_name = f"Administrative Level {i}" | |
admin_levels[level_name] = col_name | |
admin_levels["Provinces and Territories"] = "NAME_1" | |
# Define Pakistan-specific regions | |
pakistan_regions = { | |
"Provinces": ["Punjab", "Sindh", "Khyber Pakhtunkhwa", "Balochistan"], | |
"Territories": ["Islamabad Capital Territory", "Gilgit-Baltistan", "Azad Kashmir"] | |
} | |
# Selection for Pakistan's administrative levels | |
selected_admin_levels = st.multiselect( | |
"Select Administrative Levels to Display", | |
list(admin_levels.keys()), | |
default=["Provinces and Territories"] | |
) | |
# Initialize selected areas and colors | |
selected_areas = [] | |
fill_colors = {} | |
border_colors = {} | |
# Handle selection for Pakistan's provinces and territories | |
if "Provinces and Territories" in selected_admin_levels: | |
col1, col2 = st.columns(2) | |
with col1: | |
selected_provinces = st.multiselect( | |
"Select Provinces", | |
pakistan_regions["Provinces"], | |
default=pakistan_regions["Provinces"] | |
) | |
with col2: | |
selected_territories = st.multiselect( | |
"Select Territories", | |
pakistan_regions["Territories"], | |
default=pakistan_regions["Territories"] | |
) | |
# Process selections | |
for area in selected_provinces + selected_territories: | |
selected_areas.append(area) | |
fill_colors[area] = st.color_picker(f"Fill color for {area}", "#3498db") | |
border_colors[area] = st.color_picker(f"Border color for {area}", "#2980b9") | |
# Create visualizations | |
st.subheader("2D Map Visualization") | |
fig_2d = create_2d_map(gdf, selected_areas, fill_colors, border_colors, | |
legend_title, legend_location, legend_font_size, | |
legend_bg_color, legend_border_color, legend_transparency, | |
legend_columns) | |
st.pyplot(fig_2d) | |
st.subheader("3D Map Visualization") | |
fig_3d = create_3d_map(gdf, selected_areas, fill_colors, border_colors, | |
legend_title, legend_font_size, legend_font_weight, | |
legend_bg_color, legend_border_color, legend_border_width, | |
legend_columns) | |
st.plotly_chart(fig_3d, use_container_width=True) | |
# Download buttons | |
col1, col2 = st.columns(2) | |
with col1: | |
if st.button("Download 2D Map"): | |
plt.savefig("map_2d.png", dpi=300, bbox_inches='tight') | |
with open("map_2d.png", "rb") as file: | |
st.download_button( | |
label="Click to Download 2D Map", | |
data=file, | |
file_name="map_2d.png", | |
mime="image/png" | |
) | |
with col2: | |
if st.button("Download 3D Map"): | |
html_buffer = io.StringIO() | |
fig_3d.write_html(html_buffer) | |
st.download_button( | |
label="Click to Download 3D Map", | |
data=html_buffer.getvalue(), | |
file_name="map_3d.html", | |
mime="text/html" | |
) | |
def create_2d_map(gdf, selected_areas, fill_colors, border_colors, | |
legend_title, legend_location, legend_font_size, | |
legend_bg_color, legend_border_color, legend_transparency, | |
legend_columns): | |
fig, ax = plt.subplots(figsize=(15, 10)) | |
# Plot base map | |
gdf.plot(ax=ax, color='#EEEEEE', alpha=0.3, edgecolor='#CCCCCC') | |
# Plot selected areas | |
for area in selected_areas: | |
area_gdf = gdf[gdf['NAME_1'] == area] | |
area_gdf.plot(ax=ax, color=fill_colors[area], alpha=0.5, | |
edgecolor=border_colors[area], linewidth=1) | |
# Configure legend | |
patches = [plt.Rectangle((0, 0), 1, 1, fc=fill_colors[area], | |
ec=border_colors[area], alpha=0.5) | |
for area in selected_areas] | |
ax.legend(patches, selected_areas, | |
title=legend_title, | |
loc=legend_location, | |
fontsize=legend_font_size, | |
title_fontsize=legend_font_size + 2, | |
frameon=True, | |
facecolor=legend_bg_color, | |
edgecolor=legend_border_color, | |
framealpha=1-legend_transparency, | |
ncol=legend_columns) | |
ax.set_xticks([]) | |
ax.set_yticks([]) | |
ax.set_frame_on(False) | |
return fig | |
def create_3d_map(gdf, selected_areas, fill_colors, border_colors, | |
legend_title, legend_font_size, legend_font_weight, | |
legend_bg_color, legend_border_color, legend_border_width, | |
legend_columns): | |
fig = go.Figure() | |
added_to_legend = set() | |
# Plot base map | |
for _, row in gdf.iterrows(): | |
if isinstance(row.geometry, MultiPolygon): | |
for poly in row.geometry.geoms: | |
x, y = poly.exterior.xy | |
fig.add_trace(go.Scatter3d( | |
x=list(x), y=list(y), z=[0]*len(x), | |
mode='lines', | |
line=dict(color='#CCCCCC', width=1), | |
showlegend=False | |
)) | |
else: | |
x, y = row.geometry.exterior.xy | |
fig.add_trace(go.Scatter3d( | |
x=list(x), y=list(y), z=[0]*len(x), | |
mode='lines', | |
line=dict(color='#CCCCCC', width=1), | |
showlegend=False | |
)) | |
# Plot selected areas | |
for area in selected_areas: | |
area_gdf = gdf[gdf['NAME_1'] == area] | |
for _, row in area_gdf.iterrows(): | |
if isinstance(row.geometry, MultiPolygon): | |
for poly in row.geometry.geoms: | |
x, y = poly.exterior.xy | |
if area not in added_to_legend: | |
fig.add_trace(go.Scatter3d( | |
x=list(x), y=list(y), z=[0]*len(x), | |
mode='lines+markers', | |
line=dict(color=border_colors[area], width=2), | |
marker=dict(size=1, color=fill_colors[area], opacity=0.5), | |
name=area, | |
showlegend=True | |
)) | |
added_to_legend.add(area) | |
else: | |
fig.add_trace(go.Scatter3d( | |
x=list(x), y=list(y), z=[0]*len(x), | |
mode='lines+markers', | |
line=dict(color=border_colors[area], width=2), | |
marker=dict(size=1, color=fill_colors[area], opacity=0.5), | |
name=area, | |
showlegend=False | |
)) | |
else: | |
x, y = row.geometry.exterior.xy | |
if area not in added_to_legend: | |
fig.add_trace(go.Scatter3d( | |
x=list(x), y=list(y), z=[0]*len(x), | |
mode='lines+markers', | |
line=dict(color=border_colors[area], width=2), | |
marker=dict(size=1, color=fill_colors[area], opacity=0.5), | |
name=area, | |
showlegend=True | |
)) | |
added_to_legend.add(area) | |
else: | |
fig.add_trace(go.Scatter3d( | |
x=list(x), y=list(y), z=[0]*len(x), | |
mode='lines+markers', | |
line=dict(color=border_colors[area], width=2), | |
marker=dict(size=1, color=fill_colors[area], opacity=0.5), | |
name=area, | |
showlegend=False | |
)) | |
# Update layout | |
fig.update_layout( | |
scene=dict( | |
aspectmode='data', | |
xaxis=dict(showgrid=False, zeroline=False, showticklabels=False), | |
yaxis=dict(showgrid=False, zeroline=False, showticklabels=False), | |
zaxis=dict(showgrid=False, zeroline=False, showticklabels=False, range=[-1, 1]) | |
), | |
margin=dict(t=30, b=0, l=0, r=0), | |
legend=dict( | |
font=dict( | |
size=legend_font_size, | |
weight=legend_font_weight.lower() | |
), | |
bgcolor=legend_bg_color, | |
bordercolor=legend_border_color, | |
borderwidth=legend_border_width, | |
orientation="h" if legend_columns > 1 else "v", | |
yanchor="bottom", | |
y=1.02, | |
xanchor="right", | |
x=1, | |
title=dict( | |
text=legend_title, | |
font=dict( | |
size=legend_font_size + 2 | |
) | |
), | |
itemsizing='constant' | |
), | |
showlegend=True, | |
height=800 | |
) | |
return fig | |
if __name__ == "__main__": | |
main() | |
# [Previous code remains the same until line 285 in the main() function] | |
# Add after the 3D map visualization section in main(): | |
# 3D Map Visualization (World Map Version) | |
st.subheader("🌎 3D Map Visualization (World Map)") | |
fig_world = create_3d_world_map(world_gdf, gdf, selected_areas, fill_colors, border_colors, | |
legend_title, legend_font_size, legend_font_weight, | |
legend_bg_color, legend_border_color, legend_border_width, | |
legend_columns) | |
st.plotly_chart(fig_world, use_container_width=True) | |
# Add download buttons for both 3D maps | |
col1, col2 = st.columns(2) | |
with col1: | |
if st.button("Download 3D Map (Current Version)"): | |
html_buffer = io.StringIO() | |
fig_3d.write_html(html_buffer) | |
st.download_button( | |
label="Click to Download 3D Map (Current Version)", | |
data=html_buffer.getvalue(), | |
file_name=f"{selected_country.lower()}_map_3d.html", | |
mime="text/html" | |
) | |
with col2: | |
if st.button("Download 3D World Map"): | |
html_buffer = io.StringIO() | |
fig_world.write_html(html_buffer) | |
st.download_button( | |
label="Click to Download 3D World Map", | |
data=html_buffer.getvalue(), | |
file_name=f"{selected_country.lower()}_world_map_3d.html", | |
mime="text/html" | |
) | |
# Add new function for world map visualization after create_3d_map(): | |
def create_3d_world_map(world_gdf, selected_gdf, selected_areas, fill_colors, border_colors, | |
legend_title, legend_font_size, legend_font_weight, | |
legend_bg_color, legend_border_color, legend_border_width, | |
legend_columns): | |
fig_world = go.Figure() | |
added_to_legend = set() | |
# Plot all countries in light gray | |
for _, row in world_gdf.iterrows(): | |
if isinstance(row.geometry, MultiPolygon): | |
for poly in row.geometry.geoms: | |
x, y = poly.exterior.xy | |
fig_world.add_trace(go.Scatter3d( | |
x=list(x), y=list(y), z=[0]*len(x), | |
mode='lines', | |
line=dict(color='#CCCCCC', width=1), | |
showlegend=False | |
)) | |
else: | |
x, y = row.geometry.exterior.xy | |
fig_world.add_trace(go.Scatter3d( | |
x=list(x), y=list(y), z=[0]*len(x), | |
mode='lines', | |
line=dict(color='#CCCCCC', width=1), | |
showlegend=False | |
)) | |
# Plot selected areas with their colors | |
for area in selected_areas: | |
area_gdf = selected_gdf[selected_gdf['NAME_1'] == area] | |
for _, row in area_gdf.iterrows(): | |
if isinstance(row.geometry, MultiPolygon): | |
for poly in row.geometry.geoms: | |
x, y = poly.exterior.xy | |
if area not in added_to_legend: | |
fig_world.add_trace(go.Scatter3d( | |
x=list(x), y=list(y), z=[0]*len(x), | |
mode='lines+markers', | |
line=dict(color=border_colors[area], width=2), | |
marker=dict(size=1, color=fill_colors[area], opacity=0.5), | |
name=area, | |
showlegend=True | |
)) | |
added_to_legend.add(area) | |
else: | |
fig_world.add_trace(go.Scatter3d( | |
x=list(x), y=list(y), z=[0]*len(x), | |
mode='lines+markers', | |
line=dict(color=border_colors[area], width=2), | |
marker=dict(size=1, color=fill_colors[area], opacity=0.5), | |
name=area, | |
showlegend=False | |
)) | |
fig_world.add_trace(go.Mesh3d( | |
x=list(x), y=list(y), z=[0]*len(x), | |
color=fill_colors[area], | |
opacity=0.3, | |
showlegend=False | |
)) | |
else: | |
x, y = row.geometry.exterior.xy | |
if area not in added_to_legend: | |
fig_world.add_trace(go.Scatter3d( | |
x=list(x), y=list(y), z=[0]*len(x), | |
mode='lines+markers', | |
line=dict(color=border_colors[area], width=2), | |
marker=dict(size=1, color=fill_colors[area], opacity=0.5), | |
name=area, | |
showlegend=True | |
)) | |
added_to_legend.add(area) | |
else: | |
fig_world.add_trace(go.Scatter3d( | |
x=list(x), y=list(y), z=[0]*len(x), | |
mode='lines+markers', | |
line=dict(color=border_colors[area], width=2), | |
marker=dict(size=1, color=fill_colors[area], opacity=0.5), | |
name=area, | |
showlegend=False | |
)) | |
fig_world.add_trace(go.Mesh3d( | |
x=list(x), y=list(y), z=[0]*len(x), | |
color=fill_colors[area], | |
opacity=0.3, | |
showlegend=False | |
)) | |
# Update layout | |
fig_world.update_layout( | |
scene=dict( | |
aspectmode='data', | |
xaxis=dict(showgrid=False, zeroline=False, showticklabels=False), | |
yaxis=dict(showgrid=False, zeroline=False, showticklabels=False), | |
zaxis=dict(showgrid=False, zeroline=False, showticklabels=False, range=[-1, 1]) | |
), | |
margin=dict(t=30, b=0, l=0, r=0), | |
legend=dict( | |
font=dict( | |
size=legend_font_size, | |
weight=legend_font_weight.lower() | |
), | |
bgcolor=legend_bg_color, | |
bordercolor=legend_border_color, | |
borderwidth=legend_border_width, | |
orientation="h" if legend_columns > 1 else "v", | |
yanchor="bottom", | |
y=1.02, | |
xanchor="right", | |
x=1, | |
title=dict( | |
text=legend_title, | |
font=dict( | |
size=legend_font_size + 2 | |
) | |
), | |
itemsizing='constant', | |
traceorder='normal' if legend_columns == 1 else 'grouped' | |
), | |
showlegend=True, | |
height=800 | |
) | |
return fig_world | |
# Wrap the main execution in a try-except block | |
if __name__ == "__main__": | |
try: | |
main() | |
except Exception as e: | |
st.error(f"⚠️ Error: {e}") |