File size: 5,726 Bytes
be096d1
6e9dc4d
 
99101f6
35509b3
ce5e315
ff0d30b
be096d1
99101f6
be096d1
ff0d30b
 
 
61f85b2
ff0d30b
 
 
35509b3
ff0d30b
61f85b2
ff0d30b
 
 
35509b3
ff0d30b
61f85b2
 
ff0d30b
 
35509b3
 
99101f6
ff0d30b
ce5e315
61f85b2
ce5e315
99101f6
61f85b2
 
ff0d30b
 
 
99101f6
df1519d
61f85b2
 
ff0d30b
 
61f85b2
 
 
 
 
 
 
 
 
ff0d30b
 
 
 
5f830c6
61f85b2
ff0d30b
6e9dc4d
61f85b2
ff0d30b
6e9dc4d
61f85b2
 
 
 
 
 
 
 
ff0d30b
61f85b2
 
 
 
 
ff0d30b
35509b3
61f85b2
ff0d30b
61f85b2
35509b3
 
 
61f85b2
35509b3
 
 
61f85b2
ff0d30b
61f85b2
 
ff0d30b
99101f6
ff0d30b
61f85b2
35509b3
61f85b2
 
35509b3
 
99101f6
ff0d30b
 
61f85b2
35509b3
ff0d30b
 
99101f6
ff0d30b
99101f6
61f85b2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35509b3
61f85b2
 
be096d1
35509b3
 
 
ff0d30b
61f85b2
35509b3
 
 
ff0d30b
61f85b2
ff0d30b
99101f6
35509b3
61f85b2
35509b3
61f85b2
ff0d30b
61f85b2
35509b3
61f85b2
ff0d30b
35509b3
 
 
ff0d30b
61f85b2
99101f6
61f85b2
 
ff0d30b
 
61f85b2
35509b3
61f85b2
35509b3
be096d1
39ee1aa
a4d0bd6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
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()