HistorySpace / app.py
oberbics's picture
Update app.py
61f85b2 verified
raw
history blame
5.73 kB
import gradio as gr
import pandas as pd
import folium
from geopy.geocoders import Nominatim
import tempfile
import warnings
from datetime import datetime
warnings.filterwarnings("ignore")
# Historical Basemap Configuration
HISTORICAL_BASEMAPS = {
"1700s": {
"url": "https://map1.davidrumsey.com/tiles/rumsey/SDSC1790/{z}/{x}/{y}.png",
"attr": "Rumsey 1794",
"year_range": (1700, 1800),
"default_zoom": 2
},
"1800s": {
"url": "https://map1.davidrumsey.com/tiles/rumsey/SDSC1860/{z}/{x}/{y}.png",
"attr": "Colton 1865",
"year_range": (1801, 1900),
"default_zoom": 3
},
"Early 1900s": {
"url": "https://stamen-tiles.a.ssl.fastly.net/toner-lite/{z}/{x}/{y}.png",
"attr": "Stamen 1915",
"year_range": (1901, 1920),
"default_zoom": 4
}
}
class HistoricalGeocoder:
def __init__(self):
self.geolocator = Nominatim(user_agent="historical_mapper_v4")
self.cache = {}
def get_coords(self, location: str, year: int):
"""Get coordinates with simple historical adjustments"""
cache_key = f"{location}_{year}"
if cache_key in self.cache:
return self.cache[cache_key]
try:
result = self.geolocator.geocode(location, timeout=10)
if not result:
return None
lat, lon = result.latitude, result.longitude
# Simple historical adjustments (example only)
if year < 1800:
# Older maps often had shifted coordinates
lon += 0.2
lat += 0.1
elif year < 1900:
lon += 0.1
self.cache[cache_key] = (lat, lon)
return (lat, lon)
except Exception as e:
print(f"Geocoding failed: {str(e)}")
return None
def create_historical_map(df, location_col, year):
geocoder = HistoricalGeocoder()
# Select appropriate basemap
basemap = next(
(bm for bm in HISTORICAL_BASEMAPS.values()
if bm["year_range"][0] <= year <= bm["year_range"][1]),
HISTORICAL_BASEMAPS["1800s"]
)
# Create map
m = folium.Map(
location=[40, 0],
zoom_start=basemap["default_zoom"],
tiles=basemap["url"],
attr=basemap["attr"],
control_scale=True
)
# Add all basemaps as layers
for name, config in HISTORICAL_BASEMAPS.items():
if config["url"] != basemap["url"]:
folium.TileLayer(
tiles=config["url"],
attr=config["attr"],
name=f"{name} ({config['year_range'][0]}-{config['year_range'][1]})",
overlay=False
).add_to(m)
# Add markers
coords = []
for loc in df[location_col].dropna().unique():
point = geocoder.get_coords(str(loc), year)
if point:
folium.Marker(
location=point,
popup=f"<b>{loc}</b><br><i>As of {year}</i>",
icon=folium.Icon(
color="red" if year < 1850 else "blue",
icon="history" if year < 1850 else "info-sign",
prefix="fa"
)
).add_to(m)
coords.append(point)
# Add layer control and fit bounds
folium.LayerControl().add_to(m)
if coords:
m.fit_bounds(coords)
return m._repr_html_()
def process_file(file_obj, location_col, year):
try:
# Read file
df = pd.read_excel(file_obj.name)
# Validate column
if location_col not in df.columns:
return None, f"Column '{location_col}' not found"
# Create 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
return (
f"<div style='width:100%; height:70vh'>{map_html}</div>",
f"Map Year: {year}\nLocations: {len(df)}",
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="1. Upload Excel File",
file_types=[".xlsx", ".xls"],
type="filepath"
)
location_col = gr.Textbox(
label="2. Location Column Name",
value="location",
interactive=True
)
year = gr.Slider(
label="3. Select Map Year",
minimum=1700,
maximum=1920,
value=1850,
step=1
)
btn = gr.Button("Generate Map", variant="primary")
with gr.Column():
map_display = gr.HTML(
label="Historical Map",
value="<div style='text-align:center;padding:2em;color:gray'>"
"Map will appear here</div>"
)
stats = gr.Textbox(label="Map Info")
download = gr.File(label="Download Processed Data", visible=True)
btn.click(
process_file,
inputs=[file_input, location_col, year],
outputs=[map_display, stats, download]
)
if __name__ == "__main__":
app.launch()