Spaces:
Build error
Build error
| import gradio as gr | |
| import geopandas as gpd | |
| import os | |
| import shutil | |
| import zipfile | |
| from shapely.geometry import mapping | |
| import folium | |
| import plotly.express as px | |
| import tempfile | |
| import logging | |
| from pathlib import Path | |
| # Set up logging | |
| logging.basicConfig( | |
| level=logging.INFO, | |
| format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' | |
| ) | |
| logger = logging.getLogger(__name__) | |
| class GeoPulseApp: | |
| def __init__(self): | |
| # Create a temporary directory for processing files | |
| self.temp_dir = tempfile.mkdtemp(prefix="geopulse_") | |
| logger.info(f"Created temporary directory at {self.temp_dir}") | |
| def cleanup(self): | |
| # Clean up temporary directory after use | |
| if self.temp_dir and os.path.exists(self.temp_dir): | |
| try: | |
| shutil.rmtree(self.temp_dir) | |
| logger.info(f"Cleaned up temporary directory: {self.temp_dir}") | |
| except Exception as e: | |
| logger.error(f"Error cleaning up: {e}") | |
| def process_files(self, file_obj, view_type: str) -> str: | |
| """Process the uploaded file and create visualization.""" | |
| try: | |
| if file_obj is None: | |
| raise gr.Error("Please upload a ZIP file or Shapefile") | |
| # Save the uploaded file to the temporary directory | |
| file_path = os.path.join(self.temp_dir, file_obj.name) | |
| with open(file_path, "wb") as f: | |
| f.write(file_obj.read()) # Correct method to handle Gradio file object | |
| # Check file extension | |
| if file_path.lower().endswith('.zip'): | |
| # Extract the shapefile from the ZIP | |
| with zipfile.ZipFile(file_path, 'r') as zip_ref: | |
| zip_ref.extractall(self.temp_dir) | |
| # Find the .shp file | |
| shp_files = list(Path(self.temp_dir).rglob("*.shp")) | |
| if not shp_files: | |
| raise gr.Error("No shapefile (.shp) found in the ZIP file") | |
| shapefile_path = str(shp_files[0]) | |
| elif file_path.lower().endswith('.shp'): | |
| shapefile_path = file_path # Use directly if it's a .shp file | |
| else: | |
| raise gr.Error("Please upload a valid ZIP file or Shapefile (*.shp)") | |
| # Load the shapefile | |
| gdf = gpd.read_file(shapefile_path) | |
| if gdf.empty: | |
| raise gr.Error("The shapefile contains no data") | |
| # Convert to WGS84 if needed | |
| if gdf.crs is None: | |
| gdf.set_crs("EPSG:4326", inplace=True) | |
| elif gdf.crs != "EPSG:4326": | |
| gdf = gdf.to_crs("EPSG:4326") | |
| # Create visualization based on the selected view type | |
| if view_type == "2D": | |
| return self.create_2d_map(gdf) | |
| elif view_type == "3D": | |
| return self.create_3d_map(gdf) | |
| else: | |
| raise gr.Error("Please select either 2D or 3D view") | |
| except zipfile.BadZipFile: | |
| raise gr.Error("The file is not a valid ZIP file") | |
| except Exception as e: | |
| logger.error(f"Error processing file: {e}") | |
| raise gr.Error(f"Error processing file: {str(e)}") | |
| finally: | |
| self.cleanup() | |
| def create_2d_map(self, gdf: gpd.GeoDataFrame) -> str: | |
| """Create 2D map visualization.""" | |
| try: | |
| # Re-project to a projected CRS for accurate centroid calculation | |
| gdf_projected = gdf.to_crs(epsg=3857) | |
| center = [gdf_projected.geometry.centroid.y.mean(), gdf_projected.geometry.centroid.x.mean()] | |
| m = folium.Map(location=center, zoom_start=4) | |
| folium.GeoJson( | |
| gdf, | |
| style_function=lambda x: { | |
| 'fillColor': '#ffff00', | |
| 'color': '#000000', | |
| 'weight': 1, | |
| 'fillOpacity': 0.5 | |
| } | |
| ).add_to(m) | |
| # Save map to temporary file | |
| map_path = os.path.join(self.temp_dir, "map_2d.html") | |
| m.save(map_path) | |
| return map_path # Return the path for download link | |
| except Exception as e: | |
| raise gr.Error(f"Error creating 2D map: {str(e)}") | |
| def create_3d_map(self, gdf: gpd.GeoDataFrame) -> str: | |
| """Create 3D map visualization.""" | |
| try: | |
| # Sample if too large | |
| if len(gdf) > 1000: | |
| gdf = gdf.sample(n=1000) | |
| # Calculate centroids in a projected CRS for accuracy | |
| gdf_projected = gdf.to_crs(epsg=3857) | |
| gdf['lon'] = gdf_projected.geometry.centroid.x | |
| gdf['lat'] = gdf_projected.geometry.centroid.y | |
| gdf['area'] = gdf.geometry.area | |
| fig = px.scatter_3d( | |
| gdf, | |
| x='lon', | |
| y='lat', | |
| z='area', | |
| title="3D GIS Visualization" | |
| ) | |
| # Save to temporary file | |
| map_path = os.path.join(self.temp_dir, "map_3d.html") | |
| fig.write_html(map_path) | |
| return map_path # Return the path for download link | |
| except Exception as e: | |
| raise gr.Error(f"Error creating 3D map: {str(e)}") | |
| def main(): | |
| app = GeoPulseApp() | |
| with gr.Blocks(title="GeoPulse: GIS Data Visualizer") as interface: | |
| gr.Markdown("# GeoPulse: GIS Data Visualizer") | |
| with gr.Row(): | |
| with gr.Column(): | |
| # File input component for uploading ZIP files | |
| file_input = gr.File( | |
| label="Upload Shapefile ZIP or Shapefile", | |
| file_types=[".zip", ".shp"], # Accept zip or shapefiles | |
| ) | |
| view_type = gr.Radio( | |
| choices=["2D", "3D"], | |
| value="2D", | |
| label="View Type" | |
| ) | |
| process_btn = gr.Button("Process and Visualize") | |
| with gr.Column(): | |
| # Output area for the visualization | |
| output = gr.HTML(label="Visualization") | |
| # Trigger processing when the button is clicked | |
| process_btn.click( | |
| fn=app.process_files, | |
| inputs=[file_input, view_type], | |
| outputs=output | |
| ) | |
| # Instructions for the user | |
| gr.Markdown(""" | |
| ### Instructions: | |
| 1. Prepare a ZIP file containing your shapefile (.shp, .shx, and .dbf files) | |
| 2. Click 'Upload Shapefile ZIP or Shapefile' and select your file | |
| 3. Choose between 2D or 3D visualization | |
| 4. Click 'Process and Visualize' | |
| 5. After processing, a download link for the map will appear below | |
| """) | |
| # Launch the interface | |
| interface.launch(server_name="0.0.0.0", server_port=7860) | |
| if __name__ == "__main__": | |
| main() | |