forever-sheikh commited on
Commit
028dda8
·
verified ·
1 Parent(s): 4eebef5

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -246
app.py DELETED
@@ -1,246 +0,0 @@
1
- import gradio as gr
2
- import googlemaps
3
- import osmnx as ox
4
- import geopandas as gpd
5
- import pandas as pd
6
- import requests
7
- import zipfile
8
- import os
9
- import glob
10
- import shutil
11
- from shapely.geometry import Point, Polygon
12
-
13
- # ==========================================
14
- # AUTHENTICATION
15
- # ==========================================
16
- try:
17
- API_KEY = os.environ.get("GOOGLE_API_KEY")
18
- except:
19
- API_KEY = None
20
-
21
- # ==========================================
22
- # HELPER FUNCTIONS
23
- # ==========================================
24
- def load_geodata_to_polygon(file_obj):
25
- extract_path = "temp_extract"
26
- if os.path.exists(extract_path):
27
- shutil.rmtree(extract_path)
28
- os.makedirs(extract_path)
29
-
30
- target_kml = None
31
- try:
32
- if file_obj.name.lower().endswith('.kmz'):
33
- with zipfile.ZipFile(file_obj.name, 'r') as zip_ref:
34
- zip_ref.extractall(extract_path)
35
- kml_files = glob.glob(extract_path + "/**/*.kml", recursive=True)
36
- if kml_files:
37
- target_kml = kml_files[0]
38
- elif file_obj.name.lower().endswith('.kml'):
39
- target_kml = file_obj.name
40
-
41
- if target_kml:
42
- return gpd.read_file(target_kml).union_all()
43
- except:
44
- return None
45
- return None
46
-
47
- def get_roof_area(lat, lng, api_key):
48
- base_url = "https://solar.googleapis.com/v1/buildingInsights:findClosest"
49
- params = {
50
- "location.latitude": lat,
51
- "location.longitude": lng,
52
- "requiredQuality": "HIGH",
53
- "key": api_key
54
- }
55
- try:
56
- resp = requests.get(base_url, params=params)
57
- data = resp.json()
58
- if 'error' in data: return None
59
- return data.get('solarPotential', {}).get('wholeRoofStats', {}).get('areaMeters2', None)
60
- except:
61
- return None
62
-
63
- def get_osm_physics(lat, lng):
64
- try:
65
- tags = {'building': True}
66
- gdf = ox.features.features_from_point((lat, lng), tags, dist=60)
67
- if not gdf.empty:
68
- gdf_proj = gdf.to_crs(epsg=3857)
69
- gdf_proj['area_m2'] = gdf_proj.geometry.area
70
- best = gdf_proj.sort_values(by='area_m2', ascending=False).iloc[0]
71
-
72
- floors = None
73
- if 'building:levels' in best and pd.notna(best['building:levels']):
74
- try: floors = int(float(str(best['building:levels']).split(';')[0]))
75
- except: pass
76
-
77
- height = None
78
- if 'height' in best and pd.notna(best['height']):
79
- try: height = float(str(best['height']).replace('m','').strip())
80
- except: pass
81
- return height, floors
82
- except:
83
- pass
84
- return None, None
85
-
86
- # DATA CONSTANTS
87
- BRAND_FLOORS = {
88
- "Macy's": 2, "JCPenney": 2, "Nordstrom": 2, "Sears": 2, "IKEA": 2,
89
- "Target": 1, "Walmart": 1, "Costco": 1, "Home Depot": 1, "Lowe's": 1,
90
- "Barnes & Noble": 1, "Dick's Sporting Goods": 1, "Kohl's": 1
91
- }
92
-
93
- BRAND_AVG_AREA = {
94
- "IKEA": 28000, "Walmart": 15000, "Costco": 14000, "Sam's Club": 13000,
95
- "Meijer": 18000, "Target": 12000, "Home Depot": 10000, "Lowe's": 10000,
96
- "Kroger": 6000, "Safeway": 5000, "Whole Foods": 4000, "Macy's": 16000,
97
- "JCPenney": 10000, "Sears": 12000, "Kohl's": 8000, "Dick's Sporting Goods": 4500,
98
- "T.J. Maxx": 2800, "Marshalls": 2800, "Ross Dress for Less": 2800,
99
- "Old Navy": 1400, "Barnes & Noble": 2500, "Best Buy": 3500, "Staples": 2000,
100
- "Office Depot": 2000, "PetSmart": 1800, "Petco": 1400, "Trader Joe's": 1200,
101
- "Aldi": 1500, "Lidl": 1500, "Ace Hardware": 800, "DSW Designer Shoe Warehouse": 2000
102
- }
103
-
104
- SEARCH_LIST = [
105
- "Walmart", "Target", "Kmart", "Sears", "Kohl's", "Macy's", "JCPenney",
106
- "T.J. Maxx", "Marshalls", "HomeGoods", "HomeSense", "Ross", "Ross Dress for Less", "Burlington",
107
- "Dick's Sporting Goods", "Albertsons", "Safeway", "Home Depot", "Lowe's",
108
- "Best Buy", "IKEA", "Bob's Discount Furniture", "Raymour & Flanigan",
109
- "Barnes & Noble", "Office Depot", "OfficeMax", "Staples", "Lowe",
110
- "PetSmart", "Petco", "Kroger", "Meijer", "Costco", "BJ's Wholesale Club",
111
- "Sam's Club", "Whole Foods", "ShopRite", "Stop & Shop", "Trader Joe's",
112
- "Lidl", "Aldi", "DSW Designer Shoe Warehouse", "Old Navy", "Ace Hardware", "Michaels",
113
- "Hobby Lobby", "Trader Joes"
114
- ]
115
-
116
- # ==========================================
117
- # MAIN LOGIC WITH GENERATOR (YIELD)
118
- # ==========================================
119
- def process_data(file_obj):
120
- # This allows real-time updates to the UI
121
- if not API_KEY:
122
- yield "❌ API Key not found! Set GOOGLE_API_KEY in Secrets.", None
123
- return
124
-
125
- if file_obj is None:
126
- yield "❌ Please upload a file.", None
127
- return
128
-
129
- yield "📂 Loading Polygon...", None
130
- polygon = load_geodata_to_polygon(file_obj)
131
-
132
- if not polygon:
133
- yield "❌ Failed to read KML/KMZ file.", None
134
- return
135
-
136
- # --- NEW: SAFE AREA CHECK ---
137
- try:
138
- # We use a simple estimate first to avoid crashing on projection
139
- # Converting to EPSG:6933 (Equal Area)
140
- gs = gpd.GeoSeries([polygon], crs="EPSG:4326")
141
- gs_proj = gs.to_crs(epsg=6933)
142
- area_sq_meters = gs_proj.area.iloc[0]
143
-
144
- limit_sq_meters = 250_000_000
145
-
146
- if area_sq_meters > limit_sq_meters:
147
- yield f"⚠️ AREA TOO LARGE: {area_sq_meters:,.0f} sq m (Limit: {limit_sq_meters:,.0f}). Upload a smaller file.", None
148
- return
149
-
150
- yield f"✅ Area: {area_sq_meters:,.0f} sq m. Starting scan...", None
151
-
152
- except Exception as e:
153
- print(f"⚠️ Area Check Warning: {e}")
154
- yield "⚠️ Could not verify area size. Proceeding anyway...", None
155
- # ---------------------------
156
-
157
- gmaps = googlemaps.Client(key=API_KEY)
158
- results = []
159
- seen_ids = set()
160
- total_brands = len(SEARCH_LIST)
161
-
162
- # LOOP THROUGH BRANDS
163
- for i, brand in enumerate(SEARCH_LIST):
164
- # Update the UI status message
165
- yield f"🔍 Scanning Brand {i+1}/{total_brands}: {brand}...", None
166
-
167
- try:
168
- places = gmaps.places_nearby(
169
- location=(polygon.centroid.y, polygon.centroid.x),
170
- radius=50000,
171
- keyword=brand
172
- )
173
-
174
- for p in places.get('results', []):
175
- pid = p.get('place_id')
176
- if pid in seen_ids: continue
177
-
178
- name = p.get('name')
179
- if any(x in name for x in ["ATM", "Redbox", "Kiosk", "Coinme", "Gas", "Fuel"]): continue
180
-
181
- lat = p['geometry']['location']['lat']
182
- lng = p['geometry']['location']['lng']
183
-
184
- if not polygon.contains(Point(lng, lat)): continue
185
- seen_ids.add(pid)
186
-
187
- # Get Data
188
- roof_area = get_roof_area(lat, lng, API_KEY)
189
- height, floors = get_osm_physics(lat, lng)
190
-
191
- # Logic
192
- source_note = "SolarAPI"
193
- if roof_area is None:
194
- roof_area = BRAND_AVG_AREA.get(brand, 2500.0)
195
- source_note = "Brand_Avg (Missing)"
196
- else:
197
- if roof_area > 30000 and brand not in ["IKEA", "Costco", "Meijer", "Sam's Club", "Walmart"]:
198
- roof_area = BRAND_AVG_AREA.get(brand, 2500.0)
199
- source_note = "Brand_Avg (Mall detected)"
200
- elif roof_area < 500 and brand not in ["Ace Hardware", "Trader Joe's", "GameStop"]:
201
- roof_area = BRAND_AVG_AREA.get(brand, 2500.0)
202
- source_note = "Brand_Avg (Too Small detected)"
203
-
204
- if floors is None: floors = BRAND_FLOORS.get(brand, 1)
205
- if height is None: height = floors * 6.0
206
-
207
- results.append({
208
- 'Name': name, 'Brand': brand, 'Latitude': lat, 'Longitude': lng,
209
- 'Height_m': round(height, 2), 'Num_Floors': int(floors),
210
- 'Area_sqm': round(roof_area, 2), 'Data_Source': source_note
211
- })
212
- except Exception as e:
213
- # THIS IS IMPORTANT: It prints the error to the logs if something goes wrong
214
- print(f"❌ Error scanning {brand}: {str(e)}")
215
- pass
216
-
217
- # FINISHED
218
- if not results:
219
- yield "❌ No stores found in this area.", None
220
- return
221
-
222
- # Export
223
- df = pd.DataFrame(results)
224
- bad_keywords = ['Mobile', 'Salon', 'Floral', 'Bakery', 'Pharmacy', 'Optical', 'Geek Squad', 'Photo']
225
- df = df[~df['Name'].str.contains('|'.join(bad_keywords), case=False, na=False)]
226
-
227
- output_path = "Building_Inventory.csv"
228
- df.to_csv(output_path, index=False)
229
-
230
- yield f"✅ Success! Found {len(df)} stores.", output_path
231
-
232
- # ==========================================
233
- # GRADIO INTERFACE
234
- # ==========================================
235
- iface = gr.Interface(
236
- fn=process_data,
237
- inputs=gr.File(label="Upload Polygon (Limit: 250,000,000 sq m)"),
238
- outputs=[
239
- gr.Textbox(label="Status Log"),
240
- gr.File(label="Download CSV")
241
- ],
242
- title="🏙️ Commercial Building Inventory Generator",
243
- description="Upload a KMZ file. The tool will scan for over 45 Big Box brands, check Solar API for area, and OpenStreetMap for height/floors."
244
- )
245
-
246
- iface.launch()