quantaroute-geocoding 1.0.1__py3-none-any.whl → 1.0.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of quantaroute-geocoding might be problematic. Click here for more details.

@@ -5,11 +5,12 @@ A Python library for geocoding addresses to DigiPin codes with both online API
5
5
  and offline processing capabilities.
6
6
  """
7
7
 
8
- __version__ = "1.0.1"
8
+ __version__ = "1.0.3"
9
9
  __author__ = "QuantaRoute"
10
10
  __email__ = "support@quantaroute.com"
11
11
 
12
12
  from .client import QuantaRouteClient
13
+ from .location_lookup import LocationLookupClient
13
14
  from .offline import OfflineProcessor
14
15
  from .csv_processor import CSVProcessor
15
16
  from .exceptions import (
@@ -22,6 +23,7 @@ from .exceptions import (
22
23
 
23
24
  __all__ = [
24
25
  "QuantaRouteClient",
26
+ "LocationLookupClient",
25
27
  "OfflineProcessor",
26
28
  "CSVProcessor",
27
29
  "QuantaRouteError",
@@ -8,14 +8,15 @@ from typing import Optional
8
8
 
9
9
  from .csv_processor import CSVProcessor
10
10
  from .client import QuantaRouteClient
11
+ from .location_lookup import LocationLookupClient
11
12
  from .offline import OfflineProcessor
12
13
  from .exceptions import QuantaRouteError
13
14
 
14
15
 
15
16
  @click.group()
16
- @click.version_option(version="1.0.0")
17
+ @click.version_option(version="2.0.0")
17
18
  def main():
18
- """QuantaRoute Geocoding CLI - Process addresses and coordinates with DigiPin"""
19
+ """QuantaRoute Geocoding CLI - Revolutionary location intelligence with DigiPin and administrative boundaries"""
19
20
  pass
20
21
 
21
22
 
@@ -277,5 +278,140 @@ def single_digipin_to_coords(digipin_code: str, api_key: Optional[str], offline:
277
278
  click.echo(f"Unexpected error: {str(e)}", err=True)
278
279
 
279
280
 
281
+ # 🚀 REVOLUTIONARY LOCATION LOOKUP COMMANDS
282
+
283
+ @main.command()
284
+ @click.argument('latitude', type=float)
285
+ @click.argument('longitude', type=float)
286
+ @click.option('--api-key', envvar='QUANTAROUTE_API_KEY', required=True, help='QuantaRoute API key')
287
+ def location_lookup(latitude: float, longitude: float, api_key: str):
288
+ """🚀 REVOLUTIONARY: Get administrative boundaries from coordinates"""
289
+
290
+ try:
291
+ client = LocationLookupClient(api_key)
292
+ result = client.lookup_coordinates(latitude, longitude)
293
+
294
+ click.echo("🚀 REVOLUTIONARY LOCATION LOOKUP RESULT")
295
+ click.echo("=" * 50)
296
+ click.echo(f"📍 Coordinates: {latitude}, {longitude}")
297
+ click.echo(f"📮 Pincode: {result.get('pincode', 'N/A')}")
298
+ click.echo(f"🏢 Office Name: {result.get('office_name', 'N/A')}")
299
+ click.echo(f"🏛️ Division: {result.get('division', 'N/A')}")
300
+ click.echo(f"🌍 Region: {result.get('region', 'N/A')}")
301
+ click.echo(f"⭕ Circle: {result.get('circle', 'N/A')}")
302
+ click.echo(f"🗺️ DigiPin: {result.get('digipin', 'N/A')}")
303
+
304
+ if result.get('cached'):
305
+ click.echo(f"⚡ Response Time: {result.get('response_time_ms', 0)}ms (cached)")
306
+ else:
307
+ click.echo(f"🔍 Response Time: {result.get('response_time_ms', 0)}ms (database)")
308
+
309
+ click.echo("\n✨ This precision is not available from government services!")
310
+
311
+ except QuantaRouteError as e:
312
+ click.echo(f"❌ Error: {str(e)}", err=True)
313
+ except Exception as e:
314
+ click.echo(f"💥 Unexpected error: {str(e)}", err=True)
315
+
316
+
317
+ @main.command()
318
+ @click.argument('digipin_code')
319
+ @click.option('--api-key', envvar='QUANTAROUTE_API_KEY', required=True, help='QuantaRoute API key')
320
+ def location_from_digipin(digipin_code: str, api_key: str):
321
+ """🚀 REVOLUTIONARY: Get administrative boundaries from DigiPin"""
322
+
323
+ try:
324
+ client = LocationLookupClient(api_key)
325
+ result = client.lookup_digipin(digipin_code)
326
+
327
+ click.echo("🚀 REVOLUTIONARY LOCATION LOOKUP FROM DIGIPIN")
328
+ click.echo("=" * 55)
329
+ click.echo(f"🔢 DigiPin: {digipin_code}")
330
+ click.echo(f"📮 Pincode: {result.get('pincode', 'N/A')}")
331
+ click.echo(f"🏢 Office Name: {result.get('office_name', 'N/A')}")
332
+ click.echo(f"🏛️ Division: {result.get('division', 'N/A')}")
333
+ click.echo(f"🌍 Region: {result.get('region', 'N/A')}")
334
+ click.echo(f"⭕ Circle: {result.get('circle', 'N/A')}")
335
+
336
+ coords = result.get('coordinates', {})
337
+ if coords:
338
+ click.echo(f"📍 Coordinates: {coords.get('latitude', 'N/A')}, {coords.get('longitude', 'N/A')}")
339
+
340
+ if result.get('cached'):
341
+ click.echo(f"⚡ Response Time: {result.get('response_time_ms', 0)}ms (cached)")
342
+ else:
343
+ click.echo(f"🔍 Response Time: {result.get('response_time_ms', 0)}ms (database)")
344
+
345
+ click.echo("\n✨ Administrative boundary precision that governments don't provide!")
346
+
347
+ except QuantaRouteError as e:
348
+ click.echo(f"❌ Error: {str(e)}", err=True)
349
+ except Exception as e:
350
+ click.echo(f"💥 Unexpected error: {str(e)}", err=True)
351
+
352
+
353
+ @main.command()
354
+ @click.option('--api-key', envvar='QUANTAROUTE_API_KEY', required=True, help='QuantaRoute API key')
355
+ def location_stats(api_key: str):
356
+ """📊 Get live statistics about the revolutionary Location Lookup service"""
357
+
358
+ try:
359
+ client = LocationLookupClient(api_key)
360
+ stats = client.get_statistics()
361
+
362
+ click.echo("📊 REVOLUTIONARY LOCATION LOOKUP STATISTICS")
363
+ click.echo("=" * 50)
364
+ click.echo(f"🗺️ Total Boundaries: {stats.get('total_boundaries', 'N/A'):,}")
365
+ click.echo(f"🏛️ Total States: {stats.get('total_states', 'N/A')}")
366
+ click.echo(f"📮 Total Divisions: {stats.get('total_divisions', 'N/A')}")
367
+ click.echo(f"⚡ Cache Size: {stats.get('cache_size', 'N/A')}")
368
+
369
+ performance = stats.get('performance_metrics', {})
370
+ if performance:
371
+ click.echo(f"\n⚡ PERFORMANCE METRICS:")
372
+ click.echo(f" Average Response Time: {performance.get('avg_response_time_ms', 'N/A')}ms")
373
+ click.echo(f" Cache Hit Rate: {performance.get('cache_hit_rate', 'N/A')}%")
374
+ click.echo(f" Total Requests: {performance.get('total_requests', 'N/A'):,}")
375
+
376
+ click.echo("\n🚀 Revolutionary service with 36,000+ postal boundaries!")
377
+ click.echo("✨ Precision that even government services don't provide!")
378
+
379
+ except QuantaRouteError as e:
380
+ click.echo(f"❌ Error: {str(e)}", err=True)
381
+ except Exception as e:
382
+ click.echo(f"💥 Unexpected error: {str(e)}", err=True)
383
+
384
+
385
+ @main.command()
386
+ @click.argument('input_file', type=click.Path(exists=True))
387
+ @click.argument('output_file', type=click.Path())
388
+ @click.option('--api-key', envvar='QUANTAROUTE_API_KEY', required=True, help='QuantaRoute API key')
389
+ @click.option('--latitude-column', default='latitude', help='Name of latitude column')
390
+ @click.option('--longitude-column', default='longitude', help='Name of longitude column')
391
+ @click.option('--batch-size', default=50, help='Batch size for API requests')
392
+ def location_lookup_csv(
393
+ input_file: str,
394
+ output_file: str,
395
+ api_key: str,
396
+ latitude_column: str,
397
+ longitude_column: str,
398
+ batch_size: int
399
+ ):
400
+ """🚀 REVOLUTIONARY: Batch location lookup from CSV coordinates"""
401
+
402
+ try:
403
+ # This would need to be implemented in csv_processor.py
404
+ click.echo("🚀 REVOLUTIONARY BATCH LOCATION LOOKUP")
405
+ click.echo("=" * 45)
406
+ click.echo(f"📁 Input: {input_file}")
407
+ click.echo(f"📁 Output: {output_file}")
408
+ click.echo(f"📊 Batch Size: {batch_size}")
409
+ click.echo("\n⚠️ CSV Location Lookup processing coming soon!")
410
+ click.echo("✨ Will process thousands of coordinates to administrative boundaries!")
411
+
412
+ except Exception as e:
413
+ click.echo(f"💥 Unexpected error: {str(e)}", err=True)
414
+
415
+
280
416
  if __name__ == '__main__':
281
417
  main()
@@ -39,7 +39,7 @@ class QuantaRouteClient:
39
39
  self.session = requests.Session()
40
40
  self.session.headers.update({
41
41
  'x-api-key': api_key,
42
- 'User-Agent': 'quantaroute-geocoding-python/1.0.0',
42
+ 'User-Agent': 'quantaroute-geocoding-python/2.0.0',
43
43
  'Content-Type': 'application/json'
44
44
  })
45
45
 
@@ -310,3 +310,114 @@ class QuantaRouteClient:
310
310
 
311
311
  response = self._make_request('DELETE', f'/v1/digipin/webhooks/{webhook_id}')
312
312
  return response
313
+
314
+ # 🚀 REVOLUTIONARY LOCATION LOOKUP METHODS
315
+
316
+ def lookup_location_from_coordinates(self, latitude: float, longitude: float) -> Dict:
317
+ """
318
+ 🚀 REVOLUTIONARY: Get administrative boundaries from coordinates
319
+
320
+ This is a revolutionary service that provides administrative boundary lookup
321
+ with precision that even government services don't offer.
322
+
323
+ Args:
324
+ latitude: Latitude coordinate
325
+ longitude: Longitude coordinate
326
+
327
+ Returns:
328
+ Dict containing administrative boundary information:
329
+ - pincode: 6-digit postal code
330
+ - office_name: Post office name
331
+ - division: Postal division
332
+ - region: Postal region
333
+ - circle: Postal circle
334
+ - state: State name
335
+ - coordinates: Input coordinates
336
+ - digipin: DigiPin code for the location
337
+ - cached: Whether result was from cache
338
+ - response_time_ms: Response time in milliseconds
339
+ """
340
+ if not isinstance(latitude, (int, float)) or not isinstance(longitude, (int, float)):
341
+ raise ValidationError("Latitude and longitude must be numbers")
342
+
343
+ if not (-90 <= latitude <= 90):
344
+ raise ValidationError("Latitude must be between -90 and 90")
345
+
346
+ if not (-180 <= longitude <= 180):
347
+ raise ValidationError("Longitude must be between -180 and 180")
348
+
349
+ data = {
350
+ 'latitude': float(latitude),
351
+ 'longitude': float(longitude)
352
+ }
353
+
354
+ response = self._make_request('POST', '/v1/location/lookup', data)
355
+ return response.get('data', {})
356
+
357
+ def lookup_location_from_digipin(self, digipin: str) -> Dict:
358
+ """
359
+ 🚀 REVOLUTIONARY: Get administrative boundaries from DigiPin
360
+
361
+ Args:
362
+ digipin: DigiPin code (format: XXX-XXX-XXXX)
363
+
364
+ Returns:
365
+ Dict containing administrative boundary information
366
+ """
367
+ if not digipin or not digipin.strip():
368
+ raise ValidationError("DigiPin is required")
369
+
370
+ # Use the LocationLookupClient for this operation
371
+ from .location_lookup import LocationLookupClient
372
+ location_client = LocationLookupClient(
373
+ api_key=self.api_key,
374
+ base_url=self.base_url,
375
+ timeout=self.timeout,
376
+ max_retries=self.max_retries
377
+ )
378
+
379
+ return location_client.lookup_digipin(digipin)
380
+
381
+ def batch_location_lookup(self, locations: List[Dict]) -> Dict:
382
+ """
383
+ 🚀 REVOLUTIONARY: Batch lookup for multiple locations
384
+
385
+ Args:
386
+ locations: List of location dictionaries, each containing either:
387
+ - {'latitude': float, 'longitude': float}
388
+ - {'digipin': str}
389
+
390
+ Returns:
391
+ Dict containing batch processing results with administrative boundaries
392
+ """
393
+ if not locations or not isinstance(locations, list):
394
+ raise ValidationError("Locations must be a non-empty list")
395
+
396
+ if len(locations) > 100:
397
+ raise ValidationError("Maximum 100 locations allowed per batch")
398
+
399
+ # Use the LocationLookupClient for this operation
400
+ from .location_lookup import LocationLookupClient
401
+ location_client = LocationLookupClient(
402
+ api_key=self.api_key,
403
+ base_url=self.base_url,
404
+ timeout=self.timeout,
405
+ max_retries=self.max_retries
406
+ )
407
+
408
+ return location_client.batch_lookup(locations)
409
+
410
+ def get_location_statistics(self) -> Dict:
411
+ """
412
+ 📊 Get live statistics about the revolutionary Location Lookup service
413
+
414
+ Returns:
415
+ Dict containing:
416
+ - total_boundaries: Total number of postal boundaries (36,000+)
417
+ - total_states: Number of states covered
418
+ - total_divisions: Number of postal divisions
419
+ - cache_size: Current cache size
420
+ - performance_metrics: Response time statistics
421
+ """
422
+ response = self._make_request('GET', '/v1/location/stats')
423
+ return response.get('data', {})
@@ -0,0 +1,291 @@
1
+ """
2
+ Revolutionary Location Lookup Client
3
+
4
+ Provides administrative boundary lookup capabilities - a service that even
5
+ the government doesn't provide at this level of precision and accessibility.
6
+ """
7
+
8
+ import requests
9
+ import time
10
+ from typing import Dict, List, Optional, Union
11
+ from .exceptions import APIError, RateLimitError, AuthenticationError, ValidationError
12
+
13
+
14
+ class LocationLookupClient:
15
+ """
16
+ Revolutionary Location Lookup Client
17
+
18
+ Get administrative boundaries (state, division, locality, pincode) from
19
+ coordinates or DigiPin codes. Access to 36,000+ postal boundaries across India.
20
+ """
21
+
22
+ def __init__(
23
+ self,
24
+ api_key: str,
25
+ base_url: str = "https://api.quantaroute.com",
26
+ timeout: int = 30,
27
+ max_retries: int = 3
28
+ ):
29
+ """
30
+ Initialize the Location Lookup client
31
+
32
+ Args:
33
+ api_key: Your QuantaRoute API key
34
+ base_url: Base URL for the API (default: https://api.quantaroute.com)
35
+ timeout: Request timeout in seconds (default: 30)
36
+ max_retries: Maximum number of retries for failed requests (default: 3)
37
+ """
38
+ self.api_key = api_key
39
+ self.base_url = base_url.rstrip('/')
40
+ self.timeout = timeout
41
+ self.max_retries = max_retries
42
+ self.session = requests.Session()
43
+ self.session.headers.update({
44
+ 'x-api-key': api_key,
45
+ 'User-Agent': 'quantaroute-geocoding-python/2.0.0',
46
+ 'Content-Type': 'application/json'
47
+ })
48
+
49
+ def _make_request(
50
+ self,
51
+ method: str,
52
+ endpoint: str,
53
+ data: Optional[Dict] = None,
54
+ params: Optional[Dict] = None
55
+ ) -> Dict:
56
+ """Make HTTP request with retry logic"""
57
+ url = f"{self.base_url}{endpoint}"
58
+
59
+ for attempt in range(self.max_retries + 1):
60
+ try:
61
+ if method.upper() == 'GET':
62
+ response = self.session.get(url, params=params, timeout=self.timeout)
63
+ else:
64
+ response = self.session.post(url, json=data, params=params, timeout=self.timeout)
65
+
66
+ # Handle rate limiting
67
+ if response.status_code == 429:
68
+ retry_after = int(response.headers.get('Retry-After', 60))
69
+ if attempt < self.max_retries:
70
+ time.sleep(retry_after)
71
+ continue
72
+ raise RateLimitError(
73
+ "Rate limit exceeded",
74
+ retry_after=retry_after
75
+ )
76
+
77
+ # Handle authentication errors
78
+ if response.status_code == 401:
79
+ raise AuthenticationError()
80
+
81
+ # Handle other client/server errors
82
+ if not response.ok:
83
+ try:
84
+ error_data = response.json()
85
+ message = error_data.get('message', f'HTTP {response.status_code}')
86
+ error_code = error_data.get('code')
87
+ except:
88
+ message = f'HTTP {response.status_code}: {response.text}'
89
+ error_code = None
90
+
91
+ raise APIError(message, response.status_code, error_code)
92
+
93
+ return response.json()
94
+
95
+ except requests.exceptions.RequestException as e:
96
+ if attempt < self.max_retries:
97
+ time.sleep(2 ** attempt) # Exponential backoff
98
+ continue
99
+ raise APIError(f"Request failed: {str(e)}")
100
+
101
+ raise APIError("Max retries exceeded")
102
+
103
+ def lookup_coordinates(self, latitude: float, longitude: float) -> Dict:
104
+ """
105
+ 🚀 REVOLUTIONARY: Get administrative boundaries from coordinates
106
+
107
+ Args:
108
+ latitude: Latitude coordinate
109
+ longitude: Longitude coordinate
110
+
111
+ Returns:
112
+ Dict containing administrative boundary information:
113
+ - pincode: 6-digit postal code
114
+ - office_name: Post office name
115
+ - division: Postal division
116
+ - region: Postal region
117
+ - circle: Postal circle
118
+ - state: State name
119
+ - coordinates: Input coordinates
120
+ - digipin: DigiPin code for the location
121
+ - cached: Whether result was from cache
122
+ - response_time_ms: Response time in milliseconds
123
+ """
124
+ if not isinstance(latitude, (int, float)) or not isinstance(longitude, (int, float)):
125
+ raise ValidationError("Latitude and longitude must be numbers")
126
+
127
+ if not (-90 <= latitude <= 90):
128
+ raise ValidationError("Latitude must be between -90 and 90")
129
+
130
+ if not (-180 <= longitude <= 180):
131
+ raise ValidationError("Longitude must be between -180 and 180")
132
+
133
+ data = {
134
+ 'latitude': float(latitude),
135
+ 'longitude': float(longitude)
136
+ }
137
+
138
+ response = self._make_request('POST', '/v1/location/lookup', data)
139
+ return response.get('data', {})
140
+
141
+ def lookup_digipin(self, digipin: str) -> Dict:
142
+ """
143
+ 🚀 REVOLUTIONARY: Get administrative boundaries from DigiPin
144
+
145
+ Args:
146
+ digipin: DigiPin code (format: XXX-XXX-XXXX)
147
+
148
+ Returns:
149
+ Dict containing administrative boundary information
150
+ """
151
+ if not digipin or not digipin.strip():
152
+ raise ValidationError("DigiPin is required")
153
+
154
+ # Convert DigiPin to coordinates first, then lookup
155
+ from .offline import OfflineProcessor
156
+ processor = OfflineProcessor()
157
+
158
+ try:
159
+ coords_result = processor.digipin_to_coordinates(digipin)
160
+ if coords_result and 'coordinates' in coords_result:
161
+ lat, lng = coords_result['coordinates']
162
+ return self.lookup_coordinates(lat, lng)
163
+ else:
164
+ raise ValidationError("Invalid DigiPin format")
165
+ except Exception as e:
166
+ raise ValidationError(f"DigiPin conversion failed: {str(e)}")
167
+
168
+ def batch_lookup(self, locations: List[Dict]) -> Dict:
169
+ """
170
+ 🚀 REVOLUTIONARY: Batch lookup for multiple locations
171
+
172
+ Args:
173
+ locations: List of location dictionaries, each containing either:
174
+ - {'latitude': float, 'longitude': float}
175
+ - {'digipin': str}
176
+
177
+ Returns:
178
+ Dict containing batch processing results with administrative boundaries
179
+ """
180
+ if not locations or not isinstance(locations, list):
181
+ raise ValidationError("Locations must be a non-empty list")
182
+
183
+ if len(locations) > 100:
184
+ raise ValidationError("Maximum 100 locations allowed per batch")
185
+
186
+ # Validate and normalize locations
187
+ normalized_locations = []
188
+ for i, loc in enumerate(locations):
189
+ if not isinstance(loc, dict):
190
+ raise ValidationError(f"Location {i+1} must be a dictionary")
191
+
192
+ if 'latitude' in loc and 'longitude' in loc:
193
+ # Coordinate-based lookup
194
+ if not isinstance(loc['latitude'], (int, float)) or not isinstance(loc['longitude'], (int, float)):
195
+ raise ValidationError(f"Location {i+1}: latitude and longitude must be numbers")
196
+ normalized_locations.append({
197
+ 'latitude': float(loc['latitude']),
198
+ 'longitude': float(loc['longitude'])
199
+ })
200
+ elif 'digipin' in loc:
201
+ # DigiPin-based lookup - convert to coordinates
202
+ from .offline import OfflineProcessor
203
+ processor = OfflineProcessor()
204
+ try:
205
+ coords_result = processor.digipin_to_coordinates(loc['digipin'])
206
+ if coords_result and 'coordinates' in coords_result:
207
+ lat, lng = coords_result['coordinates']
208
+ normalized_locations.append({
209
+ 'latitude': lat,
210
+ 'longitude': lng,
211
+ 'original_digipin': loc['digipin']
212
+ })
213
+ else:
214
+ raise ValidationError(f"Location {i+1}: Invalid DigiPin format")
215
+ except Exception as e:
216
+ raise ValidationError(f"Location {i+1}: DigiPin conversion failed: {str(e)}")
217
+ else:
218
+ raise ValidationError(f"Location {i+1} must contain either 'latitude'+'longitude' or 'digipin'")
219
+
220
+ data = {'locations': normalized_locations}
221
+ response = self._make_request('POST', '/v1/location/batch-lookup', data)
222
+ return response.get('data', {})
223
+
224
+ def get_statistics(self) -> Dict:
225
+ """
226
+ 📊 Get live statistics about the Location Lookup service
227
+
228
+ Returns:
229
+ Dict containing:
230
+ - total_boundaries: Total number of postal boundaries
231
+ - total_states: Number of states covered
232
+ - total_divisions: Number of postal divisions
233
+ - cache_size: Current cache size
234
+ - performance_metrics: Response time statistics
235
+ """
236
+ response = self._make_request('GET', '/v1/location/stats')
237
+ return response.get('data', {})
238
+
239
+ def get_coverage_info(self) -> Dict:
240
+ """
241
+ 🌍 Get coverage information about the Location Lookup service
242
+
243
+ Returns:
244
+ Dict containing coverage details and service capabilities
245
+ """
246
+ response = self._make_request('GET', '/v1/location')
247
+ return response
248
+
249
+ def find_nearby_boundaries(
250
+ self,
251
+ latitude: float,
252
+ longitude: float,
253
+ radius_km: float = 5.0,
254
+ limit: int = 10
255
+ ) -> List[Dict]:
256
+ """
257
+ 🎯 Find nearby postal boundaries (experimental feature)
258
+
259
+ Args:
260
+ latitude: Center latitude
261
+ longitude: Center longitude
262
+ radius_km: Search radius in kilometers (default: 5.0)
263
+ limit: Maximum number of results (default: 10, max: 50)
264
+
265
+ Returns:
266
+ List of nearby postal boundaries with distances
267
+ """
268
+ if not isinstance(latitude, (int, float)) or not isinstance(longitude, (int, float)):
269
+ raise ValidationError("Latitude and longitude must be numbers")
270
+
271
+ if not (-90 <= latitude <= 90):
272
+ raise ValidationError("Latitude must be between -90 and 90")
273
+
274
+ if not (-180 <= longitude <= 180):
275
+ raise ValidationError("Longitude must be between -180 and 180")
276
+
277
+ if radius_km <= 0 or radius_km > 100:
278
+ raise ValidationError("Radius must be between 0 and 100 km")
279
+
280
+ if limit > 50:
281
+ limit = 50
282
+
283
+ params = {
284
+ 'lat': latitude,
285
+ 'lng': longitude,
286
+ 'radius': radius_km,
287
+ 'limit': limit
288
+ }
289
+
290
+ response = self._make_request('GET', '/v1/location/nearby', params=params)
291
+ return response.get('data', [])
@@ -88,8 +88,8 @@ class OfflineProcessor:
88
88
  return {
89
89
  'digipin': digipin_code.strip(),
90
90
  'coordinates': {
91
- 'latitude': result.latitude,
92
- 'longitude': result.longitude
91
+ 'latitude': result[0],
92
+ 'longitude': result[1]
93
93
  },
94
94
  'source': 'offline'
95
95
  }
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quantaroute-geocoding
3
- Version: 1.0.1
4
- Summary: Python SDK for QuantaRoute Geocoding API with offline DigiPin processing
3
+ Version: 1.0.3
4
+ Summary: Revolutionary Python SDK for QuantaRoute Geocoding API with Location Lookup and offline DigiPin processing
5
5
  Home-page: https://github.com/quantaroute/quantaroute-geocoding-python
6
6
  Author: QuantaRoute
7
7
  Author-email: QuantaRoute <support@quantaroute.com>
@@ -10,7 +10,7 @@ Project-URL: Homepage, https://quantaroute.com
10
10
  Project-URL: Documentation, https://api.quantaroute.com/v1/digipin/docs
11
11
  Project-URL: Repository, https://github.com/quantaroute/quantaroute-geocoding-python
12
12
  Project-URL: Bug Tracker, https://github.com/quantaroute/quantaroute-geocoding-python/issues
13
- Keywords: geocoding,digipin,gis,location,india,address,coordinates
13
+ Keywords: geocoding,digipin,gis,location,india,address,coordinates,administrative-boundaries,pincode,postal-lookup,location-intelligence
14
14
  Classifier: Development Status :: 4 - Beta
15
15
  Classifier: Intended Audience :: Developers
16
16
  Classifier: License :: OSI Approved :: MIT License
@@ -44,10 +44,18 @@ Dynamic: requires-python
44
44
 
45
45
  # QuantaRoute Geocoding Python SDK
46
46
 
47
- A comprehensive Python library for geocoding addresses to DigiPin codes with both online API and offline processing capabilities.
47
+ A **revolutionary** Python library for geocoding addresses to DigiPin codes with **groundbreaking Location Lookup API** and offline processing capabilities.
48
48
 
49
- ## Features
49
+ ## 🚀 Revolutionary Features
50
50
 
51
+ ### 🎯 **NEW: Location Lookup API** - *Service that even government doesn't provide!*
52
+ - 🗺️ **Administrative Boundary Lookup**: Get state, division, locality, pincode from coordinates
53
+ - 📍 **36,000+ Postal Boundaries**: Complete coverage across India
54
+ - ⚡ **Sub-100ms Response**: Cached responses with database fallback
55
+ - 🎯 **Government-Level Precision**: Accuracy that official services don't offer
56
+ - 🔄 **Batch Processing**: Up to 100 locations per request
57
+
58
+ ### 🌟 **Core Features**
51
59
  - 🌐 **Online API Integration**: Full access to QuantaRoute Geocoding API
52
60
  - 🔌 **Offline Processing**: Process coordinates ↔ DigiPin without internet
53
61
  - 📊 **CSV Bulk Processing**: Handle large datasets efficiently
@@ -70,14 +78,36 @@ pip install digipin
70
78
 
71
79
  ## Quick Start
72
80
 
73
- ### Online API Usage
81
+ ### 🚀 **NEW: Revolutionary Location Lookup API**
74
82
 
75
83
  ```python
76
- from quantaroute_geocoding import QuantaRouteClient
84
+ from quantaroute_geocoding import QuantaRouteClient, LocationLookupClient
77
85
 
78
86
  # Initialize client
79
87
  client = QuantaRouteClient(api_key="your-api-key")
80
88
 
89
+ # 🚀 REVOLUTIONARY: Get administrative boundaries from coordinates
90
+ result = client.lookup_location_from_coordinates(28.6139, 77.2090)
91
+ print(f"Pincode: {result['pincode']}") # 110001
92
+ print(f"Office: {result['office_name']}") # New Delhi GPO
93
+ print(f"Division: {result['division']}") # New Delhi GPO
94
+ print(f"Circle: {result['circle']}") # Delhi
95
+ print(f"DigiPin: {result['digipin']}") # 39J-438-TJC7
96
+ print(f"Response Time: {result['response_time_ms']}ms") # <100ms
97
+
98
+ # 🚀 REVOLUTIONARY: Get boundaries from DigiPin
99
+ result = client.lookup_location_from_digipin("39J-438-TJC7")
100
+ print(f"Administrative Details: {result}")
101
+
102
+ # 📊 Get live statistics (36,000+ boundaries)
103
+ stats = client.get_location_statistics()
104
+ print(f"Total Boundaries: {stats['total_boundaries']:,}")
105
+ print(f"Total States: {stats['total_states']}")
106
+ ```
107
+
108
+ ### 🌟 **Traditional Geocoding API**
109
+
110
+ ```python
81
111
  # Geocode an address
82
112
  result = client.geocode("India Gate, New Delhi, India")
83
113
  print(f"DigiPin: {result['digipin']}")
@@ -141,9 +171,25 @@ result = processor_offline.process_coordinates_to_digipin_csv(
141
171
 
142
172
  ## Command Line Interface
143
173
 
144
- The package includes a powerful CLI for batch processing:
174
+ The package includes a **revolutionary** CLI with Location Lookup capabilities:
145
175
 
146
- ### Geocode addresses from CSV
176
+ ### 🚀 **NEW: Revolutionary Location Lookup Commands**
177
+
178
+ ```bash
179
+ # Get administrative boundaries from coordinates
180
+ quantaroute-geocode location-lookup 28.6139 77.2090 --api-key your-key
181
+
182
+ # Get boundaries from DigiPin
183
+ quantaroute-geocode location-from-digipin "39J-438-TJC7" --api-key your-key
184
+
185
+ # Get live statistics (36,000+ boundaries)
186
+ quantaroute-geocode location-stats --api-key your-key
187
+
188
+ # Batch location lookup from CSV (coming soon)
189
+ quantaroute-geocode location-lookup-csv coordinates.csv boundaries.csv --api-key your-key
190
+ ```
191
+
192
+ ### 🌟 **Traditional Geocoding Commands**
147
193
 
148
194
  ```bash
149
195
  # Using API
@@ -215,6 +261,77 @@ digipin
215
261
  39J-49J-4867
216
262
  ```
217
263
 
264
+ ## 🚀 Revolutionary Location Lookup API
265
+
266
+ ### Dedicated Location Lookup Client
267
+
268
+ ```python
269
+ from quantaroute_geocoding import LocationLookupClient
270
+
271
+ # Initialize dedicated location client
272
+ location_client = LocationLookupClient(api_key="your-api-key")
273
+
274
+ # Single coordinate lookup
275
+ result = location_client.lookup_coordinates(28.6139, 77.2090)
276
+ print(f"📮 Pincode: {result['pincode']}")
277
+ print(f"🏢 Office: {result['office_name']}")
278
+ print(f"🏛️ Division: {result['division']}")
279
+ print(f"⚡ Response Time: {result['response_time_ms']}ms")
280
+
281
+ # DigiPin to boundaries
282
+ result = location_client.lookup_digipin("39J-438-TJC7")
283
+ print(f"Administrative boundaries: {result}")
284
+
285
+ # Batch processing (up to 100 locations)
286
+ locations = [
287
+ {"latitude": 28.6139, "longitude": 77.2090},
288
+ {"latitude": 19.0760, "longitude": 72.8777},
289
+ {"digipin": "39J-438-TJC7"}
290
+ ]
291
+ results = location_client.batch_lookup(locations)
292
+ print(f"Processed {len(results['results'])} locations")
293
+
294
+ # Live statistics
295
+ stats = location_client.get_statistics()
296
+ print(f"🗺️ Total Boundaries: {stats['total_boundaries']:,}")
297
+ print(f"⚡ Cache Size: {stats['cache_size']}")
298
+
299
+ # Coverage information
300
+ coverage = location_client.get_coverage_info()
301
+ print(f"Service capabilities: {coverage}")
302
+ ```
303
+
304
+ ### Location Lookup Output Format
305
+
306
+ ```json
307
+ {
308
+ "pincode": "110001",
309
+ "office_name": "New Delhi GPO",
310
+ "division": "New Delhi GPO",
311
+ "region": "",
312
+ "circle": "Delhi",
313
+ "coordinates": {
314
+ "latitude": 28.6139,
315
+ "longitude": 77.2090
316
+ },
317
+ "digipin": "39J-438-TJC7",
318
+ "cached": true,
319
+ "response_time_ms": 45
320
+ }
321
+ ```
322
+
323
+ ### Why This is Revolutionary
324
+
325
+ 🎯 **Government-Level Precision**: Access to administrative boundaries that even government APIs don't provide at this level of detail and accessibility.
326
+
327
+ 📍 **36,000+ Boundaries**: Complete coverage of Indian postal boundaries with sub-district level precision.
328
+
329
+ ⚡ **Performance**: Sub-100ms cached responses, <500ms database queries.
330
+
331
+ 🔄 **Batch Processing**: Process up to 100 locations in a single API call.
332
+
333
+ ✨ **Unique Value**: The only service providing this level of administrative boundary lookup precision for India.
334
+
218
335
  ## Advanced Features
219
336
 
220
337
  ### Webhook Management
@@ -332,17 +449,51 @@ except APIError as e:
332
449
 
333
450
  ## API Limits
334
451
 
452
+ ### Traditional Geocoding API
453
+
335
454
  | Tier | Requests/Minute | Monthly Limit | Batch Size |
336
455
  |------|----------------|---------------|------------|
337
456
  | Free | 10 | 1,000 | 50 |
338
457
  | Paid | 100 | 10,000 | 100 |
339
458
  | Enterprise | 1,000 | Unlimited | 100 |
340
459
 
460
+ ### 🚀 Revolutionary Location Lookup API
461
+
462
+ | Tier | Requests/Minute | Monthly Limit | Batch Size | Boundaries |
463
+ |------|----------------|---------------|------------|------------|
464
+ | Free | 20 | 2,000 | 50 | 36,000+ |
465
+ | Paid | 200 | 20,000 | 100 | 36,000+ |
466
+ | Enterprise | 2,000 | Unlimited | 100 | 36,000+ |
467
+
468
+ **Performance Guarantees:**
469
+ - ⚡ Cached responses: <100ms
470
+ - 🔍 Database queries: <500ms
471
+ - 📊 Batch processing: <50ms per location
472
+ - 🎯 99.9% uptime SLA (Enterprise)
473
+
341
474
  ## Support
342
475
 
343
476
  - 📧 Email: support@quantaroute.com
344
477
  - 🌐 Website: https://quantaroute.com
345
- - 📖 API Documentation: https://api.quantaroute.com/v1/digipin/docs
478
+ - 📖 Traditional API Docs: https://api.quantaroute.com/v1/digipin/docs
479
+ - 🚀 **NEW: Location Lookup API**: https://api.quantaroute.com/v1/location
480
+ - 📊 **Live Statistics**: https://api.quantaroute.com/v1/location/stats
481
+
482
+ ### 🚀 What Makes This Revolutionary?
483
+
484
+ **QuantaRoute's Location Lookup API is the first and only service to provide:**
485
+
486
+ ✨ **Government-Level Precision**: Administrative boundary data that even government APIs don't provide at this level of detail and accessibility.
487
+
488
+ 📍 **Complete Coverage**: 36,000+ postal boundaries across India with sub-district precision.
489
+
490
+ ⚡ **Blazing Performance**: Sub-100ms cached responses, guaranteed <500ms database queries.
491
+
492
+ 🎯 **Unique Value Proposition**: The only service providing this level of administrative boundary lookup precision for India.
493
+
494
+ 🔄 **Developer-Friendly**: Simple APIs, comprehensive SDKs, and excellent documentation.
495
+
496
+ **Ready to revolutionize your location intelligence applications?**
346
497
 
347
498
  ## License
348
499
 
@@ -0,0 +1,13 @@
1
+ quantaroute_geocoding/__init__.py,sha256=-C2ycuij-JrzHAYWxnbD5Qm8mLv7EZXLYt0I5pfhg_Y,766
2
+ quantaroute_geocoding/cli.py,sha256=2vJRef_4dP-4iCs3BWmNDACxMV628X4q2pos4S5UfIY,16835
3
+ quantaroute_geocoding/client.py,sha256=47sirSJMek9dGeybm6-62RoLx1SBfYlJuqDqZfgQcF8,14935
4
+ quantaroute_geocoding/csv_processor.py,sha256=tmbtE1bQzi2TnKgxOGIQ-H6MWirh-cuG4RRErcCdfco,16096
5
+ quantaroute_geocoding/exceptions.py,sha256=rAkj8K5-AUf31kV8UlmNxQ5IN0m4CIL2qnI6ALas5Ys,1173
6
+ quantaroute_geocoding/location_lookup.py,sha256=vDPmpCHFp5WDfzFYXGB71BErZCr6AEgib8n6P-ugXDE,11409
7
+ quantaroute_geocoding/offline.py,sha256=HymAwPNp01oHifQrXZ5Qj8EVn7EwoeqxjczJxbMNuXE,9711
8
+ quantaroute_geocoding-1.0.3.dist-info/licenses/LICENSE,sha256=QY7Uoe-MPRTjfyyDM3HOnqtuSqbzyPLATiriEaQ9u90,1068
9
+ quantaroute_geocoding-1.0.3.dist-info/METADATA,sha256=eYq5ESJf_K5PcHaAenF_X0f9PQaH-79ROCGE8b791Lw,15299
10
+ quantaroute_geocoding-1.0.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
+ quantaroute_geocoding-1.0.3.dist-info/entry_points.txt,sha256=cniijgPLz_Pb6Fiti6aOxaJp1BB_bv60ZOL_oitGCx8,71
12
+ quantaroute_geocoding-1.0.3.dist-info/top_level.txt,sha256=_cyyFLwSH1wYB1HNs_LcZIDQRo1CnvoEsdnKz-cBCr4,22
13
+ quantaroute_geocoding-1.0.3.dist-info/RECORD,,
@@ -1,12 +0,0 @@
1
- quantaroute_geocoding/__init__.py,sha256=zINRaVM0id2VGMQPR2UyJPK5s1_XtE3WbDlJb35ZEho,688
2
- quantaroute_geocoding/cli.py,sha256=kfVoKtBNC_jeAeqMeKRN5kxpwNEcPhnekTyLwZJFRw0,10626
3
- quantaroute_geocoding/client.py,sha256=y6aAr5Ka1nlu2epw6rXxiJ8WQpOTAO7O-ef2Ww6EYtY,10644
4
- quantaroute_geocoding/csv_processor.py,sha256=tmbtE1bQzi2TnKgxOGIQ-H6MWirh-cuG4RRErcCdfco,16096
5
- quantaroute_geocoding/exceptions.py,sha256=rAkj8K5-AUf31kV8UlmNxQ5IN0m4CIL2qnI6ALas5Ys,1173
6
- quantaroute_geocoding/offline.py,sha256=fidFlfvL6fuM1RFv4d3lSfkvSWYoUU1fNS0oU9B7U9U,9724
7
- quantaroute_geocoding-1.0.1.dist-info/licenses/LICENSE,sha256=QY7Uoe-MPRTjfyyDM3HOnqtuSqbzyPLATiriEaQ9u90,1068
8
- quantaroute_geocoding-1.0.1.dist-info/METADATA,sha256=CjUdQJ6j1OxvqFEAXqSgG1xoj2EIaZu0QwB1ceCu-nI,9545
9
- quantaroute_geocoding-1.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
10
- quantaroute_geocoding-1.0.1.dist-info/entry_points.txt,sha256=cniijgPLz_Pb6Fiti6aOxaJp1BB_bv60ZOL_oitGCx8,71
11
- quantaroute_geocoding-1.0.1.dist-info/top_level.txt,sha256=_cyyFLwSH1wYB1HNs_LcZIDQRo1CnvoEsdnKz-cBCr4,22
12
- quantaroute_geocoding-1.0.1.dist-info/RECORD,,