Spaces:
Runtime error
Runtime error
| import gradio as gr | |
| import pandas as pd | |
| import folium | |
| from geopy.geocoders import Nominatim | |
| from geopy.extra.rate_limiter import RateLimiter | |
| import tempfile | |
| from typing import Optional, Tuple | |
| import warnings | |
| # Suppress warnings | |
| warnings.filterwarnings("ignore") | |
| # Historical Tile Providers | |
| HISTORICAL_TILES = { | |
| "David Rumsey (1790)": { | |
| "url": "https://map1.davidrumsey.com/tiles/rumsey/SDSC1790/{z}/{x}/{y}.png", | |
| "attr": "David Rumsey Map Collection", | |
| "min_year": 1700, | |
| "max_year": 1800 | |
| }, | |
| "David Rumsey (1860)": { | |
| "url": "https://map1.davidrumsey.com/tiles/rumsey/SDSC1860/{z}/{x}/{y}.png", | |
| "attr": "David Rumsey Map Collection", | |
| "min_year": 1801, | |
| "max_year": 1900 | |
| }, | |
| "Stamen (1915)": { | |
| "url": "https://stamen-tiles.a.ssl.fastly.net/toner-lite/{z}/{x}/{y}.png", | |
| "attr": "Stamen Maps", | |
| "min_year": 1901, | |
| "max_year": 1920 | |
| }, | |
| "OpenHistoricalMap": { | |
| "url": "https://tile.openhistoricalmap.org/{z}/{x}/{y}.png", | |
| "attr": "OpenHistoricalMap", | |
| "min_year": 1700, | |
| "max_year": 2023 | |
| } | |
| } | |
| class Geocoder: | |
| def __init__(self): | |
| self.geolocator = Nominatim(user_agent="historical_mapper", timeout=10) | |
| self.geocode = RateLimiter(self.geolocator.geocode, min_delay_seconds=1) | |
| self.cache = {} | |
| def get_coords(self, location: str) -> Optional[Tuple[float, float]]: | |
| if not location or pd.isna(location): | |
| return None | |
| if location in self.cache: | |
| return self.cache[location] | |
| try: | |
| result = self.geocode(location) | |
| if result: | |
| coords = (result.latitude, result.longitude) | |
| self.cache[location] = coords | |
| return coords | |
| except Exception as e: | |
| print(f"Geocoding failed for '{location}': {str(e)}") | |
| self.cache[location] = None | |
| return None | |
| def get_tile_layer(year: int): | |
| """Select the most appropriate tile layer for the given year""" | |
| for name, config in HISTORICAL_TILES.items(): | |
| if config["min_year"] <= year <= config["max_year"]: | |
| return folium.TileLayer( | |
| tiles=config["url"], | |
| attr=config["attr"], | |
| name=name, | |
| overlay=False | |
| ) | |
| return folium.TileLayer("OpenStreetMap") | |
| def create_historical_map(df: pd.DataFrame, location_col: str, year: int = 1900) -> str: | |
| geocoder = Geocoder() | |
| # Create map with historical base layer | |
| base_layer = get_tile_layer(year) | |
| m = folium.Map(location=[40, -10], zoom_start=2, control_scale=True) | |
| base_layer.add_to(m) | |
| # Add all other historical layers as options | |
| for name, config in HISTORICAL_TILES.items(): | |
| if config["url"] != base_layer.tiles: | |
| folium.TileLayer( | |
| tiles=config["url"], | |
| attr=config["attr"], | |
| name=f"{name} ({config['min_year']}-{config['max_year']})", | |
| overlay=False | |
| ).add_to(m) | |
| # Add markers with historical styling | |
| coords_list = [] | |
| for loc in df[location_col].dropna().unique(): | |
| coords = geocoder.get_coords(str(loc)) | |
| if coords: | |
| folium.Marker( | |
| location=coords, | |
| popup=f"<b>{loc}</b><br>Year: {year}", | |
| icon=folium.Icon( | |
| color="red", | |
| icon="info-sign", | |
| prefix="fa" | |
| ) | |
| ).add_to(m) | |
| coords_list.append(coords) | |
| # Add layer control and fit bounds | |
| folium.LayerControl().add_to(m) | |
| if coords_list: | |
| m.fit_bounds(coords_list) | |
| return m._repr_html_() | |
| def process_file(file_obj, location_col, year): | |
| try: | |
| # Read input file | |
| df = pd.read_excel(file_obj.name) | |
| # Validate column exists | |
| if location_col not in df.columns: | |
| return None, f"Column '{location_col}' not found", None | |
| # Create historical map | |
| map_html = create_historical_map(df, location_col, year) | |
| # Save processed data | |
| with tempfile.NamedTemporaryFile(suffix=".xlsx", delete=False) as tmp: | |
| df.to_excel(tmp.name, index=False) | |
| processed_path = tmp.name | |
| # Generate stats | |
| stats = ( | |
| f"Total locations: {len(df)}\n" | |
| f"Unique places: {df[location_col].nunique()}\n" | |
| f"Map year: {year}" | |
| ) | |
| return ( | |
| f"<div style='width:100%; height:70vh'>{map_html}</div>", | |
| stats, | |
| processed_path | |
| ) | |
| except Exception as e: | |
| return None, f"Error: {str(e)}", None | |
| # Gradio Interface | |
| with gr.Blocks(title="Historical Map Explorer", theme=gr.themes.Soft()) as app: | |
| gr.Markdown("# Historical Location Mapper") | |
| with gr.Row(): | |
| with gr.Column(): | |
| file_input = gr.File( | |
| label="Upload Excel File", | |
| file_types=[".xlsx", ".xls"], | |
| type="filepath" | |
| ) | |
| location_col = gr.Textbox( | |
| label="Location Column Name", | |
| value="locations", | |
| placeholder="Enter exact column name with locations" | |
| ) | |
| year = gr.Slider( | |
| minimum=1700, | |
| maximum=2023, | |
| value=1900, | |
| step=1, | |
| label="Map Year" | |
| ) | |
| map_btn = gr.Button("Generate Historical Map", variant="primary") | |
| with gr.Column(): | |
| map_display = gr.HTML( | |
| label="Historical Map", | |
| value="<div style='text-align:center;padding:20px;'>" | |
| "Map will appear here after processing</div>" | |
| ) | |
| stats_output = gr.Textbox(label="Statistics") | |
| download_output = gr.File(label="Download Processed Data") | |
| map_btn.click( | |
| process_file, | |
| inputs=[file_input, location_col, year], | |
| outputs=[map_display, stats_output, download_output] | |
| ) | |
| if __name__ == "__main__": | |
| app.launch() |