| # /// script | |
| # requires-python = ">=3.12" | |
| # dependencies = [ | |
| # "pyobjc-core", | |
| # "pyobjc-framework-CoreLocation", | |
| # "click" | |
| # ] | |
| # /// | |
| """Basic geocoding using CoreLocation on macOS.""" | |
| import click | |
| import objc | |
| from CoreLocation import CLGeocoder | |
| from Foundation import NSRunLoop, NSDate | |
| def forward_geocode(address: str) -> list[dict]: | |
| with objc.autorelease_pool(): | |
| geocoder = CLGeocoder.alloc().init() | |
| results = {"placemarks": [], "error": None} | |
| completed = False | |
| def completion(placemarks, error): | |
| nonlocal completed | |
| if error: | |
| results["error"] = error.localizedDescription() | |
| elif placemarks: | |
| results["placemarks"] = placemarks | |
| completed = True | |
| geocoder.geocodeAddressString_completionHandler_(address, completion) | |
| while not completed: | |
| NSRunLoop.currentRunLoop().runMode_beforeDate_( | |
| "NSDefaultRunLoopMode", | |
| NSDate.dateWithTimeIntervalSinceNow_(0.1) | |
| ) | |
| if results["error"]: | |
| raise Exception(f"Geocoding error: {results['error']}") | |
| return [{ | |
| "latitude": pm.location().coordinate().latitude, | |
| "longitude": pm.location().coordinate().longitude, | |
| "name": pm.name(), | |
| "locality": pm.locality(), | |
| "country": pm.country() | |
| } for pm in results["placemarks"]] | |
| @click.command() | |
| @click.argument('address') | |
| def main(address): | |
| try: | |
| locations = forward_geocode(address) | |
| for loc in locations: | |
| click.echo("\nLocation found:") | |
| for key, value in loc.items(): | |
| if value: | |
| click.echo(f"{key}: {value}") | |
| except Exception as e: | |
| click.echo(f"Error: {e}", err=True) | |
| raise click.Abort() | |
| if __name__ == "__main__": | |
| main() |