1- import pgeocode
2- import numpy as np
3- import pandas as pd
1+ from geopy .geocoders import Nominatim
2+ from geopy .exc import GeocoderTimedOut , GeocoderServiceError
3+
4+ from app .core .logging import get_logger
5+
6+ logger = get_logger (__name__ )
7+
8+ _GEOLOCATOR = Nominatim (user_agent = "fireform" , timeout = 10 )
9+
410
511class ZipCodeAPI :
612 def __init__ (self ):
713 pass
814
9- def get_postal_code (self , country : str , postal_code : str ):
10- res = pgeocode .Nominatim (country ).query_postal_code (postal_code )
11- if isinstance (res , pd .Series ):
12- res = res .replace ({np .nan : None }).to_dict ()
13- elif isinstance (res , pd .DataFrame ):
14- res = res .replace ({np .nan : None }).to_dict (orient = 'records' )
15- return res
16-
17- def get_location (self , country : str , city : str ):
18- res = pgeocode .Nominatim (country ).query_location (city )
19- if isinstance (res , pd .Series ):
20- res = res .replace ({np .nan : None }).to_dict ()
21- elif isinstance (res , pd .DataFrame ):
22- res = res .replace ({np .nan : None }).to_dict (orient = 'records' )
23- return res
15+ def lookup_address (self , address : str ) -> list [dict ]:
16+ """
17+ Look up an address string via OpenStreetMap Nominatim and return a list
18+ of matching locations with their postal codes, coordinates, and names.
19+
20+ Args:
21+ address: Full address string, e.g. "1600 Amphitheatre Parkway, Mountain View, CA, US"
22+
23+ Returns:
24+ A list of dicts, each containing:
25+ - postal_code (str | None)
26+ - place_name (str)
27+ - latitude (float)
28+ - longitude (float)
29+ - display_name (str)
30+ - country (str | None)
31+ - state (str | None)
32+ - county (str | None)
33+ """
34+ try :
35+ locations = _GEOLOCATOR .geocode (
36+ address ,
37+ exactly_one = False ,
38+ addressdetails = True ,
39+ limit = 10 ,
40+ )
41+ except GeocoderTimedOut :
42+ logger .error ("Nominatim geocoding timed out for address: %s" , address )
43+ raise TimeoutError (f"Geocoding service timed out for address: '{ address } '" )
44+ except GeocoderServiceError as exc :
45+ logger .error ("Nominatim geocoding service error: %s" , exc )
46+ raise RuntimeError (f"Geocoding service error: { exc } " )
47+
48+ if not locations :
49+ logger .info ("No results found for address: %s" , address )
50+ return []
51+
52+ results = []
53+ for loc in locations :
54+ addr = loc .raw .get ("address" , {})
55+ results .append ({
56+ "postal_code" : addr .get ("postcode" ),
57+ "place_name" : addr .get ("city" )
58+ or addr .get ("town" )
59+ or addr .get ("village" )
60+ or addr .get ("hamlet" )
61+ or addr .get ("municipality" )
62+ or "" ,
63+ "latitude" : loc .latitude ,
64+ "longitude" : loc .longitude ,
65+ "display_name" : loc .address ,
66+ "country" : addr .get ("country" ),
67+ "state" : addr .get ("state" ),
68+ "county" : addr .get ("county" ),
69+ })
70+ logger .debug ("Geocoded result: %s" , results [- 1 ])
71+
72+ return results
0 commit comments