File size: 7,110 Bytes
61b4af1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import pandas as pd
import requests
import difflib
import logging
from io import StringIO
# from weather_collector import DistrictService # Not strictly needed if we copy overrides

# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

# Combined Manual Overrides (Original + 22 Recovered + 7 New Reported)
# Keys are Lowercase for easier matching
MANUAL_FIXES = {
    # Original from weather_collector.py
    'banka': {'lat': 24.88, 'lon': 86.92},
    'bargarh': {'lat': 21.33, 'lon': 83.62},
    'birbhum': {'lat': 23.91, 'lon': 87.52},
    'hamirpur': {'lat': 25.95, 'lon': 80.15}, 
    'kullu': {'lat': 31.95, 'lon': 77.10},
    'mandi': {'lat': 31.58, 'lon': 76.91},
    'maharajganj': {'lat': 27.14, 'lon': 83.56},
    'paschim bardhaman': {'lat': 23.68, 'lon': 86.98},
    'pratapgarh': {'lat': 25.93, 'lon': 81.60},
    'hooghly': {'lat': 22.90, 'lon': 88.39},
    'keonjhar': {'lat': 21.63, 'lon': 85.58},
    'khandwa': {'lat': 21.83, 'lon': 76.35},
    'khargone': {'lat': 21.83, 'lon': 75.61},
    'mewat': {'lat': 28.10, 'lon': 77.00},
    'nawanshahr': {'lat': 31.12, 'lon': 76.12},
    'badaun': {'lat': 28.03, 'lon': 79.13},
    'bulandshahar': {'lat': 28.40, 'lon': 77.85},
    'coochbehar': {'lat': 26.32, 'lon': 89.45},
    'davangere': {'lat': 14.46, 'lon': 75.92},
    'delhi': {'lat': 28.61, 'lon': 77.20},
    'janjgir': {'lat': 22.01, 'lon': 82.57},
    'kanpur': {'lat': 26.44, 'lon': 80.33},
    'khurda': {'lat': 20.18, 'lon': 85.62},
    'mumbai': {'lat': 19.07, 'lon': 72.87},
    'palakad': {'lat': 10.78, 'lon': 76.65},
    'sholapur': {'lat': 17.65, 'lon': 75.90},
    'the nilgiris': {'lat': 11.41, 'lon': 76.69},
    'thiruchirappalli': {'lat': 10.79, 'lon': 78.70},
    'thirunelveli': {'lat': 8.71, 'lon': 77.75},
    'thiruvannamalai': {'lat': 12.22, 'lon': 79.07},
    'thiruvarur': {'lat': 10.76, 'lon': 79.63},
    'villupuram': {'lat': 11.94, 'lon': 79.48},
    'amarawati': {'lat': 20.93, 'lon': 77.75},
    'ambedkarnagar': {'lat': 26.41, 'lon': 82.39},
    'anupur': {'lat': 23.10, 'lon': 81.69},
    'bangalore': {'lat': 12.97, 'lon': 77.59},
    
    # The 22 Manual Recovered
    "bhadradri kothagudem": {"lat": 17.55, "lon": 80.62},
    "chattrapati sambhajinagar": {"lat": 19.88, "lon": 75.32},
    "chhota udaipur": {"lat": 22.31, "lon": 74.01},
    "cuddapah": {"lat": 14.48, "lon": 78.82},
    "deedwana kuchaman": {"lat": 27.40, "lon": 74.58},
    "dharashiv(usmanabad)": {"lat": 18.19, "lon": 76.04},
    "east jaintia hills": {"lat": 25.36, "lon": 92.37},
    "gir somnath": {"lat": 20.91, "lon": 70.37},
    "gopalgang": {"lat": 26.47, "lon": 84.43},
    "jhunjhunu": {"lat": 28.13, "lon": 75.40},
    "kotputli- behror": {"lat": 27.70, "lon": 76.20},
    "madikeri(kodagu)": {"lat": 12.43, "lon": 75.75},
    "mansa": {"lat": 29.99, "lon": 75.38},
    "neem ka thana": {"lat": 27.74, "lon": 75.78},
    "nongpoh (r-bhoi)": {"lat": 25.87, "lon": 91.83},
    "purba bardhaman": {"lat": 23.26, "lon": 87.86},
    "south west garo hills": {"lat": 25.47, "lon": 89.93},
    "south west khasi hills": {"lat": 25.32, "lon": 91.29},
    "tsemenyu": {"lat": 26.05, "lon": 94.27},
    "tuticorin": {"lat": 8.76, "lon": 78.13},
    "unokoti": {"lat": 24.33, "lon": 92.00},
    "west chambaran": {"lat": 27.15, "lon": 84.35},

    # New Fixes for Reported 0.0s
    'bhojpur': {'lat': 25.47, 'lon': 84.54},
    'bilaspur': {'lat': 22.08, 'lon': 82.15}, # CG
    'banda': {'lat': 25.49, 'lon': 80.34},
    'fatehpur': {'lat': 25.92, 'lon': 80.81},
    'lalitpur': {'lat': 24.69, 'lon': 78.42},
    'vaishali': {'lat': 25.75, 'lon': 85.42},
    'hassan': {'lat': 13.01, 'lon': 76.10}
}

def generate_all_coordinates():
    # 1. Download Master List for fuzzy matching
    url = "https://raw.githubusercontent.com/recurze/IndianCities/master/india_places.csv"
    logger.info(f"Downloading master list from {url}...")
    try:
        response = requests.get(url)
        response.raise_for_status()
        master_df = pd.read_csv(StringIO(response.text))
        master_df.columns = [c.lower() for c in master_df.columns]
        master_df['latitude'] = pd.to_numeric(master_df['latitude'], errors='coerce')
        master_df['longitude'] = pd.to_numeric(master_df['longitude'], errors='coerce')
        master_df = master_df.dropna(subset=['latitude', 'longitude'])
        
        place_map = {}
        def add_to_map(name, row):
            if not isinstance(name, str): return
            n = name.strip().lower()
            if n not in place_map:
                place_map[n] = {'lat': row['latitude'], 'lon': row['longitude']}
        for _, row in master_df.iterrows():
            if 'district' in row: add_to_map(row['district'], row)
            if 'city' in row: add_to_map(row['city'], row)
        
        possible_names = list(place_map.keys())
        logger.info(f"Loaded {len(place_map)} names for fuzzy matching.")
    except Exception as e:
        logger.error(f"Failed to load master list: {e}")
        return

    # 2. Load Targets
    districts_file = 'districts.txt'
    with open(districts_file, 'r') as f:
        lines = [l.strip() for l in f if l.strip()]
        if lines and lines[0].lower() == 'district_name':
            targets = lines[1:]
        else:
            targets = lines
    
    logger.info(f"Loaded {len(targets)} target districts.")
    
    final_list = []
    
    for district in targets:
        d_lower = district.lower().strip()
        lat, lon = None, None
        
        # A. Manual Fixes
        if d_lower in MANUAL_FIXES:
            coords = MANUAL_FIXES[d_lower]
            lat, lon = coords['lat'], coords['lon']
            # logger.info(f"Manual override: {district}")
            
        # B. Exact Match in Place Map
        elif d_lower in place_map:
            lat = place_map[d_lower]['lat']
            lon = place_map[d_lower]['lon']
            
        # C. Fuzzy Match
        else:
            matches = difflib.get_close_matches(d_lower, possible_names, n=1, cutoff=0.6)
            if matches:
                 best = matches[0]
                 lat = place_map[best]['lat']
                 lon = place_map[best]['lon']
                 logger.info(f"Fuzzy match: {district} -> {best}")
        
        if lat is not None and lon is not None:
             if lat == 0.0 and lon == 0.0:
                 logger.warning(f"Found 0.0, 0.0 for {district}. Please verify.")
             final_list.append({'district': district, 'lat': lat, 'lon': lon})
        else:
            logger.error(f"FAILED to find coordinates for: {district}")

    # 4. Save
    out_df = pd.DataFrame(final_list)
    out_file = 'alldistrictsCoordinates.csv'
    out_df.to_csv(out_file, index=False)
    logger.info(f"Saved {len(final_list)} coordinates to {out_file}")
    
    if len(final_list) == len(targets):
        logger.info("SUCCESS: 100% Coverage.")
    else:
        logger.warning(f"WARNING: Coverage {len(final_list)}/{len(targets)}")

if __name__ == "__main__":
    generate_all_coordinates()