oberbics commited on
Commit
5c72cc4
·
verified ·
1 Parent(s): 61f85b2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +90 -97
app.py CHANGED
@@ -8,133 +8,126 @@ from datetime import datetime
8
 
9
  warnings.filterwarnings("ignore")
10
 
11
- # Historical Basemap Configuration
12
- HISTORICAL_BASEMAPS = {
13
- "1700s": {
14
- "url": "https://map1.davidrumsey.com/tiles/rumsey/SDSC1790/{z}/{x}/{y}.png",
15
- "attr": "Rumsey 1794",
16
- "year_range": (1700, 1800),
17
- "default_zoom": 2
18
- },
19
- "1800s": {
20
  "url": "https://map1.davidrumsey.com/tiles/rumsey/SDSC1860/{z}/{x}/{y}.png",
21
- "attr": "Colton 1865",
22
- "year_range": (1801, 1900),
23
- "default_zoom": 3
24
  },
25
- "Early 1900s": {
26
  "url": "https://stamen-tiles.a.ssl.fastly.net/toner-lite/{z}/{x}/{y}.png",
27
- "attr": "Stamen 1915",
28
- "year_range": (1901, 1920),
29
- "default_zoom": 4
 
 
 
 
 
 
30
  }
31
  }
32
 
33
- class HistoricalGeocoder:
34
  def __init__(self):
35
- self.geolocator = Nominatim(user_agent="historical_mapper_v4")
36
- self.cache = {}
37
 
38
- def get_coords(self, location: str, year: int):
39
- """Get coordinates with simple historical adjustments"""
40
- cache_key = f"{location}_{year}"
41
- if cache_key in self.cache:
42
- return self.cache[cache_key]
43
-
44
  try:
45
- result = self.geolocator.geocode(location, timeout=10)
46
- if not result:
47
- return None
48
-
49
- lat, lon = result.latitude, result.longitude
50
-
51
- # Simple historical adjustments (example only)
52
- if year < 1800:
53
- # Older maps often had shifted coordinates
54
- lon += 0.2
55
- lat += 0.1
56
- elif year < 1900:
57
- lon += 0.1
58
-
59
- self.cache[cache_key] = (lat, lon)
60
- return (lat, lon)
61
-
62
- except Exception as e:
63
- print(f"Geocoding failed: {str(e)}")
64
  return None
65
 
66
- def create_historical_map(df, location_col, year):
67
- geocoder = HistoricalGeocoder()
68
-
69
- # Select appropriate basemap
70
- basemap = next(
71
- (bm for bm in HISTORICAL_BASEMAPS.values()
72
- if bm["year_range"][0] <= year <= bm["year_range"][1]),
73
- HISTORICAL_BASEMAPS["1800s"]
74
  )
75
 
76
- # Create map
77
- m = folium.Map(
78
- location=[40, 0],
79
- zoom_start=basemap["default_zoom"],
80
- tiles=basemap["url"],
81
- attr=basemap["attr"],
82
- control_scale=True
83
- )
84
 
85
- # Add all basemaps as layers
86
- for name, config in HISTORICAL_BASEMAPS.items():
87
- if config["url"] != basemap["url"]:
88
- folium.TileLayer(
89
- tiles=config["url"],
90
- attr=config["attr"],
91
- name=f"{name} ({config['year_range'][0]}-{config['year_range'][1]})",
92
- overlay=False
93
- ).add_to(m)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
 
95
  # Add markers
 
96
  coords = []
97
  for loc in df[location_col].dropna().unique():
98
- point = geocoder.get_coords(str(loc), year)
99
  if point:
100
  folium.Marker(
101
  location=point,
102
- popup=f"<b>{loc}</b><br><i>As of {year}</i>",
103
- icon=folium.Icon(
104
- color="red" if year < 1850 else "blue",
105
- icon="history" if year < 1850 else "info-sign",
106
- prefix="fa"
107
- )
108
  ).add_to(m)
109
  coords.append(point)
110
 
111
- # Add layer control and fit bounds
112
  folium.LayerControl().add_to(m)
113
  if coords:
114
  m.fit_bounds(coords)
115
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  return m._repr_html_()
117
 
118
- def process_file(file_obj, location_col, year):
119
  try:
120
- # Read file
121
  df = pd.read_excel(file_obj.name)
122
 
123
- # Validate column
124
  if location_col not in df.columns:
125
- return None, f"Column '{location_col}' not found"
126
 
127
- # Create map
128
- map_html = create_historical_map(df, location_col, year)
129
 
130
- # Save processed data
131
  with tempfile.NamedTemporaryFile(suffix=".xlsx", delete=False) as tmp:
132
  df.to_excel(tmp.name, index=False)
133
  processed_path = tmp.name
134
 
 
135
  return (
136
- f"<div style='width:100%; height:70vh'>{map_html}</div>",
137
- f"Map Year: {year}\nLocations: {len(df)}",
138
  processed_path
139
  )
140
 
@@ -142,26 +135,26 @@ def process_file(file_obj, location_col, year):
142
  return None, f"Error: {str(e)}", None
143
 
144
  # Gradio Interface
145
- with gr.Blocks(title="Historical Map Explorer", theme=gr.themes.Soft()) as app:
146
- gr.Markdown("# Historical Location Mapper")
147
 
148
  with gr.Row():
149
  with gr.Column():
150
  file_input = gr.File(
151
  label="1. Upload Excel File",
152
- file_types=[".xlsx", ".xls"],
153
  type="filepath"
154
  )
155
  location_col = gr.Textbox(
156
  label="2. Location Column Name",
157
  value="location",
158
- interactive=True
159
  )
160
  year = gr.Slider(
161
- label="3. Select Map Year",
162
  minimum=1700,
163
  maximum=1920,
164
- value=1850,
165
  step=1
166
  )
167
  btn = gr.Button("Generate Map", variant="primary")
@@ -169,14 +162,14 @@ with gr.Blocks(title="Historical Map Explorer", theme=gr.themes.Soft()) as app:
169
  with gr.Column():
170
  map_display = gr.HTML(
171
  label="Historical Map",
172
- value="<div style='text-align:center;padding:2em;color:gray'>"
173
- "Map will appear here</div>"
174
  )
175
- stats = gr.Textbox(label="Map Info")
176
- download = gr.File(label="Download Processed Data", visible=True)
177
 
178
  btn.click(
179
- process_file,
180
  inputs=[file_input, location_col, year],
181
  outputs=[map_display, stats, download]
182
  )
 
8
 
9
  warnings.filterwarnings("ignore")
10
 
11
+ # Reliable Historical Tile Providers with fallbacks
12
+ HISTORICAL_TILES = {
13
+ "Colton 1865": {
 
 
 
 
 
 
14
  "url": "https://map1.davidrumsey.com/tiles/rumsey/SDSC1860/{z}/{x}/{y}.png",
15
+ "attr": "David Rumsey Map Collection",
16
+ "fallback": "https://stamen-tiles.a.ssl.fastly.net/toner-lite/{z}/{x}/{y}.png",
17
+ "years": (1800, 1900)
18
  },
19
+ "Stamen 1915": {
20
  "url": "https://stamen-tiles.a.ssl.fastly.net/toner-lite/{z}/{x}/{y}.png",
21
+ "attr": "Stamen Maps",
22
+ "fallback": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
23
+ "years": (1901, 1920)
24
+ },
25
+ "OpenStreetMap": {
26
+ "url": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
27
+ "attr": "OpenStreetMap",
28
+ "fallback": None,
29
+ "years": (1700, 2023)
30
  }
31
  }
32
 
33
+ class SafeGeocoder:
34
  def __init__(self):
35
+ self.geolocator = Nominatim(user_agent="historical_mapper_v6", timeout=10)
 
36
 
37
+ def get_coords(self, location: str):
 
 
 
 
 
38
  try:
39
+ result = self.geolocator.geocode(location)
40
+ return (result.latitude, result.longitude) if result else None
41
+ except:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  return None
43
 
44
+ def create_reliable_map(df, location_col, year):
45
+ # Select appropriate tile configuration
46
+ tile_config = next(
47
+ (t for t in HISTORICAL_TILES.values()
48
+ if t["years"][0] <= year <= t["years"][1]),
49
+ HISTORICAL_TILES["OpenStreetMap"]
 
 
50
  )
51
 
52
+ # Initialize map with fallback support
53
+ m = folium.Map(location=[51.5, -0.1], zoom_start=5, control_scale=True)
 
 
 
 
 
 
54
 
55
+ # Try primary tile layer
56
+ primary_layer = folium.TileLayer(
57
+ tiles=tile_config["url"],
58
+ attr=tile_config["attr"],
59
+ name=f"Primary ({year})",
60
+ control=False
61
+ ).add_to(m)
62
+
63
+ # Add fallback layer
64
+ if tile_config["fallback"]:
65
+ folium.TileLayer(
66
+ tiles=tile_config["fallback"],
67
+ attr=f"{tile_config['attr']} (Fallback)",
68
+ name="Fallback Tiles",
69
+ control=False
70
+ ).add_to(m)
71
+
72
+ # Add OpenStreetMap as backup
73
+ osm_layer = folium.TileLayer(
74
+ tiles=HISTORICAL_TILES["OpenStreetMap"]["url"],
75
+ attr=HISTORICAL_TILES["OpenStreetMap"]["attr"],
76
+ name="OpenStreetMap",
77
+ control=True
78
+ ).add_to(m)
79
 
80
  # Add markers
81
+ geocoder = SafeGeocoder()
82
  coords = []
83
  for loc in df[location_col].dropna().unique():
84
+ point = geocoder.get_coords(str(loc))
85
  if point:
86
  folium.Marker(
87
  location=point,
88
+ popup=f"<b>{loc}</b><br><i>Historical View ({year})</i>",
89
+ icon=folium.Icon(color="blue")
 
 
 
 
90
  ).add_to(m)
91
  coords.append(point)
92
 
93
+ # Layer control and bounds
94
  folium.LayerControl().add_to(m)
95
  if coords:
96
  m.fit_bounds(coords)
97
 
98
+ # Force tile reload as workaround for gray tiles
99
+ m.get_root().html.add_child(folium.Element("""
100
+ <script>
101
+ setTimeout(function() {
102
+ const tiles = document.querySelectorAll('.leaflet-tile-loaded');
103
+ tiles.forEach(tile => {
104
+ const src = tile.src;
105
+ tile.src = '';
106
+ tile.src = src;
107
+ });
108
+ }, 1000);
109
+ </script>
110
+ """))
111
+
112
  return m._repr_html_()
113
 
114
+ def process_data(file_obj, location_col, year):
115
  try:
 
116
  df = pd.read_excel(file_obj.name)
117
 
 
118
  if location_col not in df.columns:
119
+ return None, f"Column '{location_col}' not found", None
120
 
121
+ map_html = create_reliable_map(df, location_col, year)
 
122
 
 
123
  with tempfile.NamedTemporaryFile(suffix=".xlsx", delete=False) as tmp:
124
  df.to_excel(tmp.name, index=False)
125
  processed_path = tmp.name
126
 
127
+ stats = f"Displaying {len(df)} locations from {year}"
128
  return (
129
+ f"<div style='width:100%; height:70vh; border:1px solid #ddd'>{map_html}</div>",
130
+ stats,
131
  processed_path
132
  )
133
 
 
135
  return None, f"Error: {str(e)}", None
136
 
137
  # Gradio Interface
138
+ with gr.Blocks(title="Reliable Historical Maps", theme=gr.themes.Soft()) as app:
139
+ gr.Markdown("# Historical Map Viewer")
140
 
141
  with gr.Row():
142
  with gr.Column():
143
  file_input = gr.File(
144
  label="1. Upload Excel File",
145
+ file_types=[".xlsx"],
146
  type="filepath"
147
  )
148
  location_col = gr.Textbox(
149
  label="2. Location Column Name",
150
  value="location",
151
+ placeholder="e.g., 'city' or 'address'"
152
  )
153
  year = gr.Slider(
154
+ label="3. Map Year",
155
  minimum=1700,
156
  maximum=1920,
157
+ value=1865,
158
  step=1
159
  )
160
  btn = gr.Button("Generate Map", variant="primary")
 
162
  with gr.Column():
163
  map_display = gr.HTML(
164
  label="Historical Map",
165
+ value="<div style='text-align:center;padding:2em;color:gray;border:1px solid #ddd'>"
166
+ "Map will appear here after generation</div>"
167
  )
168
+ stats = gr.Textbox(label="Map Information")
169
+ download = gr.File(label="Download Processed Data")
170
 
171
  btn.click(
172
+ process_data,
173
  inputs=[file_input, location_col, year],
174
  outputs=[map_display, stats, download]
175
  )