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.
- quantaroute_geocoding/__init__.py +3 -1
- quantaroute_geocoding/cli.py +138 -2
- quantaroute_geocoding/client.py +112 -1
- quantaroute_geocoding/location_lookup.py +291 -0
- quantaroute_geocoding/offline.py +2 -2
- {quantaroute_geocoding-1.0.1.dist-info → quantaroute_geocoding-1.0.3.dist-info}/METADATA +161 -10
- quantaroute_geocoding-1.0.3.dist-info/RECORD +13 -0
- quantaroute_geocoding-1.0.1.dist-info/RECORD +0 -12
- {quantaroute_geocoding-1.0.1.dist-info → quantaroute_geocoding-1.0.3.dist-info}/WHEEL +0 -0
- {quantaroute_geocoding-1.0.1.dist-info → quantaroute_geocoding-1.0.3.dist-info}/entry_points.txt +0 -0
- {quantaroute_geocoding-1.0.1.dist-info → quantaroute_geocoding-1.0.3.dist-info}/licenses/LICENSE +0 -0
- {quantaroute_geocoding-1.0.1.dist-info → quantaroute_geocoding-1.0.3.dist-info}/top_level.txt +0 -0
|
@@ -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.
|
|
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",
|
quantaroute_geocoding/cli.py
CHANGED
|
@@ -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="
|
|
17
|
+
@click.version_option(version="2.0.0")
|
|
17
18
|
def main():
|
|
18
|
-
"""QuantaRoute Geocoding CLI -
|
|
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()
|
quantaroute_geocoding/client.py
CHANGED
|
@@ -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/
|
|
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', [])
|
quantaroute_geocoding/offline.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quantaroute-geocoding
|
|
3
|
-
Version: 1.0.
|
|
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
|
|
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
|
-
###
|
|
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
|
|
174
|
+
The package includes a **revolutionary** CLI with Location Lookup capabilities:
|
|
145
175
|
|
|
146
|
-
###
|
|
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
|
|
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,,
|
|
File without changes
|
{quantaroute_geocoding-1.0.1.dist-info → quantaroute_geocoding-1.0.3.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{quantaroute_geocoding-1.0.1.dist-info → quantaroute_geocoding-1.0.3.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
{quantaroute_geocoding-1.0.1.dist-info → quantaroute_geocoding-1.0.3.dist-info}/top_level.txt
RENAMED
|
File without changes
|