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}")