### Complete Geocoding Application Example Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/quick-reference.md This example demonstrates how to set up a geocoder with caching and fallback providers, perform forward geocoding to get coordinates from an address, and reverse geocoding to get an address from coordinates. It includes error handling and logging setup. ```go package main import ( "fmt" "log" "os" "time" geo "github.com/codingsince1985/geo-golang" "github.com/codingsince1985/geo-golang/cached" "github.com/codingsince1985/geo-golang/chained" "github.com/codingsince1985/geo-golang/google" "github.com/codingsince1985/geo-golang/openstreetmap" "github.com/patrickmn/go-cache" ) func main() { // Setup logging geo.ErrLogger = log.New(os.Stderr, "[ERR] ", log.Lshortfile) // Create geocoder with caching and fallback c := cache.New(24*time.Hour, 48*time.Hour) geocoder := cached.Geocoder( chained.Geocoder( google.Geocoder(os.Getenv("GOOGLE_API_KEY")), openstreetmap.Geocoder(), ), c, ) // Forward geocoding location, err := geocoder.Geocode("Times Square, New York") if err != nil { log.Fatalf("Error: %v", err) } if location == nil { log.Fatal("Address not found") } fmt.Printf("Location: %.6f, %.6f\n", location.Lat, location.Lng) // Reverse geocoding address, err := geocoder.ReverseGeocode(location.Lat, location.Lng) if err != nil { log.Fatalf("Error: %v", err) } if address != nil { fmt.Printf("Address: %s\n", address.FormattedAddress) } } ``` -------------------------------- ### Complete Application Example Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/DOCUMENTATION-INDEX.md Presents a more comprehensive example, likely integrating multiple features like configuration, caching, and error handling into a functional application. This serves as a reference for building real-world applications. ```go package main import ( "context" "fmt" "log" "net/http" "os" "time" "github.com/codingsince1985/geo-golang" "github.com/codingsince1985/geo-golang/cache" "github.com/codingsince1985/geo-golang/google" "github.com/codingsince1985/geo-golang/nominatim" ) func main() { // --- Configuration --- apiKey := os.Getenv("GOOGLE_API_KEY") if apiKey == "" { log.Fatal("GOOGLE_API_KEY environment variable not set.") } // --- Geocoder Setup --- // Primary geocoder googleGeocoder, err := google.GeocoderWithKey(apiKey) if err != nil { log.Fatalf("Failed to create Google geocoder: %v", err) } // Secondary geocoder (for fallback) nominatimGeocoder, err := nominatim.GeocoderWithUserAgent("geo-golang-app-example") if err != nil { log.Fatalf("Failed to create Nominatim geocoder: %v", err) } // Cache provider with 1 hour expiration cacheProvider := cache.New("1h") // Combine geocoders with caching and fallback // The order matters: Google is primary, Nominatim is fallback. // Cache is applied on top of the fallback chain. geocoder := geo.NewCache(geo.NewFallback(googleGeocoder, nominatimGeocoder), cacheProvider) // --- Geocoding Request --- addressToGeocode := "1 Infinite Loop, Cupertino, CA" // Use a context with a timeout for the request ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() fmt.Printf("Geocoding address: %s\n", addressToGeocode) location, err := geocoder.GeocodeWithContext(ctx, addressToGeocode) // --- Error Handling --- if err != nil { log.Fatalf("Geocoding failed: %v", err) } if location == nil { log.Fatalf("Geocoding returned no location and no error.") } fmt.Printf("Successfully geocoded address.\n") fmt.Printf("Latitude: %f, Longitude: %f\n", location.Lat, location.Lng) // --- Reverse Geocoding Request --- fmt.Printf("Reverse geocoding location: %f, %f\n", location.Lat, location.Lng) reverseAddress, err := geocoder.ReverseGeocode(location.Lat, location.Lng) if err != nil { log.Fatalf("Reverse geocoding failed: %v", err) } fmt.Printf("Reverse geocoded address: %s\n", reverseAddress) // --- Example of serving geocoding via HTTP --- http.HandleFunc("/geocode", func(w http.ResponseWriter, r *http.Request) { addr := r.URL.Query().Get("address") if addr == "" { http.Error(w, "Missing 'address' query parameter", http.StatusBadRequest) return } // Use a short timeout for HTTP requests reqCtx, reqCancel := context.WithTimeout(r.Context(), 5*time.Second) defer reqCancel() loc, err := geocoder.GeocodeWithContext(reqCtx, addr) if err != nil { http.Error(w, fmt.Sprintf("Geocoding failed: %v", err), http.StatusInternalServerError) return } fmt.Fprintf(w, "Address: %s\nLocation: %f, %f\n", addr, loc.Lat, loc.Lng) }) fmt.Println("Starting HTTP server on :8080...") log.Fatal(http.ListenAndServe(":8080", nil)) } ``` -------------------------------- ### Cache Creation Examples Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/quick-reference.md Create a new cache instance with different expiration policies. Use `0, 0` for no expiration. ```go import "github.com/patrickmn/go-cache" import "time" // No expiration (items never expire) c := cache.New(0, 0) // 5 minute expiration c := cache.New(5*time.Minute, 10*time.Minute) // 24 hour expiration (recommended) c := cache.New(24*time.Hour, 48*time.Hour) ``` -------------------------------- ### Geocode and Reverse Geocode Example Source: https://github.com/codingsince1985/geo-golang/blob/master/README.md Demonstrates how to geocode an address to get coordinates and then perform a reverse geocode to retrieve the address details. Handles cases where results might be nil. ```Go func tryFRData(geocoder geo.Geocoder) { location, _ := geocoder.Geocode(addrFR) if location != nil { fmt.Printf("%s location is (%.6f, %.6f)\n", addrFR, location.Lat, location.Lng) } else { fmt.Println("got location") } address, _ := geocoder.ReverseGeocode(latFR, lngFR) if address != nil { fmt.Printf("Address of (%.6f,%.6f) is %s\n", latFR, lngFR, address.FormattedAddress) fmt.Printf("Detailed address: %#v\n", address) } else { fmt.Println("got address") } fmt.Print("\n") } ``` -------------------------------- ### Common Pattern: Basic Usage Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/quick-reference.md A straightforward example demonstrating how to initialize a geocoder and perform a forward geocoding lookup. ```go geocoder := google.Geocoder(os.Getenv("GOOGLE_API_KEY")) location, _ := geocoder.Geocode("London") if location != nil { fmt.Printf("%.6f, %.6f\n", location.Lat, location.Lng) } ``` -------------------------------- ### DebugLogger Configuration Example Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/api-reference/core.md Example of how to assign a custom logger to the package-level DebugLogger. By default, it logs to io.Discard. ```go import ( "log" "os" geo "github.com/codingsince1985/geo-golang" ) func main() { geo.DebugLogger = log.New(os.Stdout, "[Geo][Debug]", log.LstdFlags) // Now debug messages are logged to stdout } ``` -------------------------------- ### IP Geolocation Example Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/api-reference/special-geocoders.md A complete Go program demonstrating how to initialize the IP2Geo geocoder and retrieve the location for a given IP address. ```go package main import ( "fmt" "os" "github.com/codingsince1985/geo-golang/ip2geo" ) func main() { geocoder := ip2geo.Geocoder(os.Getenv("IP2GEO_API_KEY")) // Get location for an IP address location, err := geocoder.Geocode("1.1.1.1") // Cloudflare DNS if err != nil { fmt.Printf("Error: %v\n", err) return } if location != nil { fmt.Printf("1.1.1.1 location: %.6f, %.6f\n", location.Lat, location.Lng) } } ``` -------------------------------- ### Caching Example Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/DOCUMENTATION-INDEX.md Demonstrates how to implement caching for geocoding results to reduce API calls and improve performance. Configure cache expiration as needed. ```go package main import ( "fmt" "log" "github.com/codingsince1985/geo-golang" "github.com/codingsince1985/geo-golang/cache" "github.com/codingsince1985/geo-golang/google" ) func main() { // Create a new cache with a 1-hour expiration cacheProvider := cache.New("1h") // Wrap the Google Maps geocoder with the cache geocoder, err := google.GeocoderWithKey("YOUR_API_KEY") if err != nil { log.Fatalf("Failed to create geocoder: %v", err) } cachedGeocoder := geo.NewCache(geocoder, cacheProvider) address := "Eiffel Tower, Paris, France" // First call: will hit the actual geocoder and cache the result location1, err := cachedGeocoder.Geocode(address) if err != nil { log.Fatalf("Failed to geocode address '%s': %v", address, err) } fmt.Printf("First call - Location: %f, %f\n", location1.Lat, location1.Lng) // Second call: should hit the cache, returning the result much faster location2, err := cachedGeocoder.Geocode(address) if err != nil { log.Fatalf("Failed to geocode address '%s' (cached): %v", address, err) } fmt.Printf("Second call - Location: %f, %f\n", location2.Lat, location2.Lng) // You can also use ReverseGeocode with the cached geocoder reverseAddress, err := cachedGeocoder.ReverseGeocode(location1.Lat, location1.Lng) if err != nil { log.Fatalf("Failed to reverse geocode location %f, %f (cached): %v", location1.Lat, location1.Lng, err) } fmt.Printf("Reverse Geocoded Address (cached): %s\n", reverseAddress) } ``` -------------------------------- ### GeocodeURL Example Implementation Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/api-reference/core.md An example implementation of the GeocodeURL method for constructing a Google Maps geocoding URL. Ensure the address parameter is URL-encoded. ```go func (b baseURL) GeocodeURL(address string) string { return string(b) + "address=" + address } ``` -------------------------------- ### Error Handling Quick Reference Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/quick-reference.md Provides examples for checking specific errors like timeouts, general errors, and cases where an address or coordinates are not found. ```go // Check for timeout if err == geo.ErrTimeout { // Retry after delay } // Check for any error if err != nil { // Handle error } // Check for not found if location == nil && err == nil { // Address/coordinates not found (not an error) } // Check network error (Go 1.16+) var netErr net.Error if errors.As(err, &netErr) { if netErr.Timeout() { // Timeout } } ``` -------------------------------- ### Basic Geocoding Example Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/DOCUMENTATION-INDEX.md Demonstrates the fundamental usage of the geocoder to find the location of an address. Ensure you have configured your geocoder with an API key if required by the provider. ```go package main import ( "fmt" "log" "github.com/codingsince1985/geo-golang" "github.com/codingsince1985/geo-golang/google" ) func main() { // Use Google Maps provider, replace "YOUR_API_KEY" with your actual key // For other providers, see their respective packages in this repo. // For example, Nominatim (OpenStreetMap) does not require an API key: // geocoder, err := nominatim.GeocoderWith andKey("YOUR_USER_AGENT") geocoder, err := google.GeocoderWithKey("YOUR_API_KEY") if err != nil { log.Fatalf("Failed to create geocoder: %v", err) } address := "1600 Amphitheatre Parkway, Mountain View, CA" // Geocode the address location, err := geocoder.Geocode("1600 Amphitheatre Parkway, Mountain View, CA") if err != nil { log.Fatalf("Failed to geocode address '%s': %v", address, err) } fmt.Printf("Address: %s\n", address) fmt.Printf("Location: %f, %f\n", location.Lat, location.Lng) // Reverse geocode the location reverseAddress, err := geocoder.ReverseGeocode(location.Lat, location.Lng) if err != nil { log.Fatalf("Failed to reverse geocode location %f, %f: %v", location.Lat, location.Lng, err) } fmt.Printf("Reverse Geocoded Address: %s\n", reverseAddress) } ``` -------------------------------- ### IP2Geo Geocoder Geocode Example Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/api-reference/special-geocoders.md Demonstrates geocoding an IP address using the IP2Geo geocoder. Standard addresses will not work with this geocoder. ```go // Interpret "address" parameter as IP address location, _ := geocoder.Geocode("8.8.8.8") // Google's DNS fmt.Printf("8.8.8.8 is in: %.6f, %.6f\n", location.Lat, location.Lng) ``` ```go // Standard addresses don't work location, _ := geocoder.Geocode("London") // Won't work ``` -------------------------------- ### Context Deadline Exceeded Example Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/errors.md Shows how to create a context with a timeout and notes that HTTP geocoders may require a wrapper for custom timeouts. ```go ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) def cancel() // Note: HTTPGeocoder doesn't accept custom context directly // Create your own wrapper if you need custom timeouts: type CustomGeocoder struct { underlying geo.Geocoder timeout time.Duration } ``` -------------------------------- ### HTTPGeocoder ReverseGeocode Method Example Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/api-reference/core.md Demonstrates calling the ReverseGeocode method on an HTTPGeocoder. This example shows how to get address details for specific coordinates. The default timeout is 8 seconds. ```go geocoder := google.Geocoder(apiKey) address, err := geocoder.ReverseGeocode(48.8566, 2.3522) // Paris, France if address != nil { fmt.Println(address.FormattedAddress) } ``` -------------------------------- ### ErrLogger Configuration Example Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/api-reference/core.md Example of how to assign a custom logger to the package-level ErrLogger. By default, it logs to io.Discard. ```go import ( "log" "os" geo "github.com/codingsince1985/geo-golang" ) func main() { geo.ErrLogger = log.New(os.Stderr, "[Geo][Err]", log.LstdFlags) // Now error messages are logged to stderr } ``` -------------------------------- ### Reverse Geocoding with geocod.io Source: https://github.com/codingsince1985/geo-golang/blob/master/README.md Attempts to get an address for a given location using the geocod.io provider. Note that this example resulted in a nil address. ```go Melbourne VIC location is (28.079357, -80.623618) got address ``` -------------------------------- ### Fallback Example Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/DOCUMENTATION-INDEX.md Demonstrates how to use multiple geocoders in a fallback chain. If the primary geocoder fails, the request is automatically sent to the secondary geocoder. ```go package main import ( "fmt" "log" "github.com/codingsince1985/geo-golang" "github.com/codingsince1985/geo-golang/google" "github.com/codingsince1985/geo-golang/nominatim" ) func main() { // Primary geocoder (e.g., Google Maps) googleGeocoder, err := google.GeocoderWithKey("YOUR_API_KEY") if err != nil { log.Fatalf("Failed to create Google geocoder: %v", err) } // Secondary geocoder (e.g., Nominatim - requires a User-Agent) nominatimGeocoder, err := nominatim.GeocoderWithUserAgent("geo-golang-example") if err != nil { log.Fatalf("Failed to create Nominatim geocoder: %v", err) } // Create a fallback geocoder: if googleGeocoder fails, nominatimGeocoder will be used fallbackGeocoder := geo.NewFallback(googleGeocoder, nominatimGeocoder) address := "Times Square, New York" location, err := fallbackGeocoder.Geocode(address) if err != nil { log.Fatalf("Failed to geocode address '%s' using fallback: %v", address, err) } fmt.Printf("Address: %s\n", address) fmt.Printf("Location: %f, %f\n", location.Lat, location.Lng) // Reverse geocoding also works with fallback reverseAddress, err := fallbackGeocoder.ReverseGeocode(location.Lat, location.Lng) if err != nil { log.Fatalf("Failed to reverse geocode location %f, %f using fallback: %v", location.Lat, location.Lng, err) } fmt.Printf("Reverse Geocoded Address: %s\n", reverseAddress) } ``` -------------------------------- ### Reverse Geocode Example with Nil Check Source: https://github.com/codingsince1985/geo-golang/blob/master/README.md Shows how to perform a reverse geocode operation and explicitly check if the returned address is nil before attempting to use it. ```Go func tryOnlyFRData(geocoder geo.Geocoder) { location, _ := geocoder.Geocode(addrFR) if location != nil { fmt.Printf("%s location is (%.6f, %.6f)\n", addrFR, location.Lat, location.Lng) } else { fmt.Println("got location") } address, _ := geocoder.ReverseGeocode(latFR, lngFR) if address != nil { fmt.Printf("Address of (%.6f,%.6f) is %s\n", latFR, lngFR, address.FormattedAddress) fmt.Printf("Detailed address: %#v\n", address) } else { fmt.Println("got address") } fmt.Print("\n") } ``` -------------------------------- ### Get Google API Key from Environment Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/configuration.md Demonstrates how to retrieve a Google API key from environment variables. Ensure the GOOGLE_API_KEY environment variable is set before running. ```go package main import ( "os" "github.com/codingsince1985/geo-golang/google" ) func main() { apiKey := os.Getenv("GOOGLE_API_KEY") if apiKey == "" panic("GOOGLE_API_KEY not set") } geocoder := google.Geocoder(apiKey) // Use geocoder... } ``` -------------------------------- ### ReverseGeocodeURL Example Implementation Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/api-reference/core.md An example implementation of the ReverseGeocodeURL method for constructing a Google Maps reverse geocoding URL. It formats latitude and longitude coordinates. ```go func (b baseURL) ReverseGeocodeURL(l geo.Location) string { return string(b) + fmt.Sprintf("result_type=street_address&latlng=%f,%f", l.Lat, l.Lng) } ``` -------------------------------- ### Standard Library Logger Compatibility Example Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/types.md Shows how to assign a standard library logger to the package's `ErrLogger` field, enabling structured logging with file and line information. ```go import "log" geo.ErrLogger = log.New(os.Stderr, "[GEO] ", log.Lshortfile) ``` -------------------------------- ### Geocode Address Example Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/api-reference/core.md Demonstrates how to use the Geocode method to convert an address string into geographical coordinates. Ensure the GOOGLE_API_KEY environment variable is set. ```go package main import ( "fmt" "os" "github.com/codingsince1985/geo-golang" "github.com/codingsince1985/geo-golang/google" ) func main() { geocoder := google.Geocoder(os.Getenv("GOOGLE_API_KEY")) location, err := geocoder.Geocode("Melbourne VIC") if err != nil { fmt.Printf("Error: %v\n", err) return } if location != nil { fmt.Printf("Latitude: %.6f, Longitude: %.6f\n", location.Lat, location.Lng) } } ``` -------------------------------- ### Google Maps Geocoder Example Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/api-reference/providers.md Demonstrates how to initialize and use the Google Maps Geocoder to find a location by address and perform reverse geocoding. Requires a Google Maps API key. ```go package main import ( "fmt" "os" "github.com/codingsince1985/geo-golang/google" ) func main() { geocoder := google.Geocoder(os.Getenv("GOOGLE_API_KEY")) location, _ := geocoder.Geocode("Melbourne VIC") fmt.Printf("Melbourne is at %.6f, %.6f\n", location.Lat, location.Lng) address, _ := geocoder.ReverseGeocode(-37.813611, 144.963056) fmt.Println(address.FormattedAddress) } ``` -------------------------------- ### Error Handling Example Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/DOCUMENTATION-INDEX.md Illustrates how to handle potential errors during geocoding, including specific error types like timeouts. It demonstrates checking for nil results and non-nil errors. ```go package main import ( "context" "errors" "fmt" "log" "time" "github.com/codingsince1985/geo-golang" "github.com/codingsince1985/geo-golang/google" ) func main() { geocoder, err := google.GeocoderWithKey("YOUR_API_KEY") if err != nil { log.Fatalf("Failed to create geocoder: %v", err) } address := "Invalid Address That Will Cause An Error" // Example with a context that has a timeout ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() location, err := geocoder.GeocodeWithContext(ctx, address) // Check if an error occurred if err != nil { // Distinguish between a timeout error and other errors if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, geo.ErrTimeout) { fmt.Printf("Geocoding timed out for address '%s': %v\n", address, err) // Implement retry logic or fallback here } else { fmt.Printf("Failed to geocode address '%s': %v\n", address, err) // Handle other geocoding errors } return // Exit if there was an error } // Check if a result was found (location might be nil even if err is nil for some providers/cases) if location == nil { fmt.Printf("No location found for address '%s', but no error was returned.\n", address) return } fmt.Printf("Address: %s\n", address) fmt.Printf("Location: %f, %f\n", location.Lat, location.Lng) } ``` -------------------------------- ### Retry Logic Example Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/DOCUMENTATION-INDEX.md Shows how to implement retry logic for geocoding requests, particularly useful for transient network errors or rate limiting. It uses a simple exponential backoff strategy. ```go package main import ( "fmt" "log" "time" "github.com/codingsince1985/geo-golang" "github.com/codingsince1985/geo-golang/google" ) func main() { geocoder, err := google.GeocoderWithKey("YOUR_API_KEY") if err != nil { log.Fatalf("Failed to create geocoder: %v", err) } // Wrap the geocoder with retry logic // Max 3 retries, initial delay 100ms, backoff factor 2 (exponential) retryGeocoder := geo.NewRetry(geocoder, 3, 100*time.Millisecond, 2.0) address := "Golden Gate Bridge, San Francisco" location, err := retryGeocoder.Geocode(address) if err != nil { log.Fatalf("Failed to geocode address '%s' after retries: %v", address, err) } fmt.Printf("Address: %s\n", address) fmt.Printf("Location: %f, %f\n", location.Lat, location.Lng) } ``` -------------------------------- ### HERE Maps Geocoder Example Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/api-reference/providers.md Initializes the HERE Geocoder API provider with application ID, code, and an optional search radius. Used for geocoding addresses. ```go geocoder := here.Geocoder( os.Getenv("HERE_APP_ID"), os.Getenv("HERE_APP_CODE"), 50, // 50 meter radius ) location, _ := geocoder.Geocode("Champs Elysees Paris") ``` -------------------------------- ### Reverse Geocode Coordinates Example Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/api-reference/core.md Shows how to use the ReverseGeocode method to find an address associated with given latitude and longitude. Requires the GOOGLE_API_KEY environment variable. ```go package main import ( "fmt" "github.com/codingsince1985/geo-golang/google" "os" ) func main() { geocoder := google.Geocoder(os.Getenv("GOOGLE_API_KEY")) address, err := geocoder.ReverseGeocode(-37.813611, 144.963056) if err != nil { fmt.Printf("Error: %v\n", err) return } if address != nil { fmt.Printf("Address: %s\n", address.FormattedAddress) fmt.Printf("City: %s, Country: %s\n", address.City, address.Country) } } ``` -------------------------------- ### Custom Unmarshaler Implementation Example Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/types.md Demonstrates how to implement a custom ResponseUnmarshaler for specific unmarshaling logic. This can be assigned to the HTTPGeocoder's ResponseUnmarshaler field. ```go type CustomUnmarshaler struct{} func (c *CustomUnmarshaler) Unmarshal(data []byte, v any) error { // Custom unmarshaling logic return nil } geocoder.HTTPGeocoder.ResponseUnmarshaler = &CustomUnmarshaler{} ``` -------------------------------- ### HTTP Status Code Error Examples Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/errors.md Illustrates common HTTP status code errors (4xx, 5xx) and their potential error messages from providers. ```go location, err := geocoder.Geocode("test") // 401 Unauthorized: "REQUEST_DENIED" (Google) or similar // 429 Rate Limited: Depends on provider // 503 Service Unavailable: Network error or provider error ``` -------------------------------- ### General Error Handling Logic Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/errors.md A comprehensive example of how to handle both actual errors (e.g., network issues, authentication failures) and 'not found' results in a geocoding operation. ```Go location, err := geocoder.Geocode(address) if err != nil { // Error occurred - request failed log.Printf("Failed to geocode: %v", err) return fmt.Errorf("geocoding error: %w", err) } if location == nil { // Not found - request succeeded but no result log.Printf("Address not found: %s", address) return fmt.Errorf("address not found: %s", address) } // Success - location found fmt.Printf("Found at %.6f, %.6f\n", location.Lat, location.Lng) ``` -------------------------------- ### HTTPGeocoder Geocode Method Example Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/api-reference/core.md Illustrates calling the Geocode method on an HTTPGeocoder instance. Typically, provider-specific constructors like google.Geocoder are used, which return an HTTPGeocoder. ```go // Most geocoding providers use HTTPGeocoder internally // You typically use provider-specific constructors instead geocoder := google.Geocoder(apiKey) // Returns HTTPGeocoder location, err := geocoder.Geocode("Paris France") ``` -------------------------------- ### Access Address Components Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/types.md Example of receiving an Address object from reverse geocoding and accessing its individual components like City, State, and CountryCode. ```go // Reverse geocoding returns Address address, err := geocoder.ReverseGeocode(40.7128, -74.0060) // New York City if address != nil { fmt.Println(address.FormattedAddress) // "1 Times Square, New York, NY 10036, USA" fmt.Println(address.City) // "New York" fmt.Println(address.State) // "New York" fmt.Println(address.CountryCode) // "US" } ``` -------------------------------- ### French API Gouv Geocode Example Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/api-reference/special-geocoders.md Shows how to use the French API Gouv geocoder for French addresses and highlights its limitations with non-French addresses. ```go // Works well for French addresses location, _ := geocoder.Geocode("9 Rue de l'Église, 75001 Paris") // Returns accurate coordinates // Works poorly for non-French addresses location, _ := geocoder.Geocode("Big Ben London") // May return incorrect results or nil ``` -------------------------------- ### Implement Custom Logger for StdLogger Interface Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/configuration.md Provides an example of a custom logger implementation that satisfies the `StdLogger` interface by embedding a `slog.Logger`. This allows integration with Go's structured logging (slog). ```Go type StructuredLogger struct { logger *slog.Logger } func (s *StructuredLogger) Printf(format string, v ...interface{}) { s.logger.Error(fmt.Sprintf(format, v...)) } geo.ErrLogger = &StructuredLogger{logger: slog.Default()} ``` -------------------------------- ### Bing Maps Geocoder Example Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/api-reference/providers.md Demonstrates initializing and using the Bing Maps Geocoder for reverse geocoding. Requires a Bing Maps API key. ```go geocoder := bing.Geocoder(os.Getenv("BING_API_KEY")) address, _ := geocoder.ReverseGeocode(-37.813611, 144.963056) fmt.Println(address.FormattedAddress) ``` -------------------------------- ### Testing Geocoding Service Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/DOCUMENTATION-INDEX.md Provides an example of how to test your geocoding implementation, likely by using a mock geocoder or a test server. This ensures your application logic works correctly without relying on external services. ```go package main import ( "fmt" "testing" "github.com/codingsince1985/geo-golang" ) // MockGeocoder is a mock implementation of the Geocoder interface for testing. type MockGeocoder struct { MockLocation *geo.Location MockAddress string MockErr error } // Geocode implements the Geocoder interface for MockGeocoder. func (m *MockGeocoder) Geocode(address string) (*geo.Location, error) { if m.MockErr != nil { return nil, m.MockErr } if m.MockLocation != nil { return m.MockLocation, nil } return nil, fmt.Errorf("no mock location or error configured") } // ReverseGeocode implements the Geocoder interface for MockGeocoder. func (m *MockGeocoder) ReverseGeocode(lat, lng float64) (string, error) { if m.MockErr != nil { return "", m.MockErr } if m.MockAddress != "" { return m.MockAddress, nil } return "", fmt.Errorf("no mock address or error configured") } func TestGeocoding(t *testing.T) { // Example test case testAddress := "1600 Amphitheatre Parkway, Mountain View, CA" expectedLocation := &geo.Location{Lat: 37.4220, Lng: -122.0841} mock := &MockGeocoder{ MockLocation: expectedLocation, MockAddress: "Googleplex", } location, err := mock.Geocode(testAddress) if err != nil { t .Errorf("Geocode failed: %v", err) } if location.Lat != expectedLocation.Lat || location.Lng != expectedLocation.Lng { .Errorf("Expected location %v, got %v", expectedLocation, location) } reverseAddr, err := mock.ReverseGeocode(location.Lat, location.Lng) if err != nil { .Errorf("ReverseGeocode failed: %v", err) } if reverseAddr != "Googleplex" { .Errorf("Expected reverse address 'Googleplex', got '%s'", reverseAddr) } } ``` -------------------------------- ### Reverse Geocoding with TomTom Source: https://github.com/codingsince1985/geo-golang/blob/master/README.md Retrieves address details for a location using the TomTom provider. The example shows the formatted address and specific components like street and postcode. ```go Address of (-37.813611,144.963056) is Doyles Road, Elaine, West Central Victoria, Victoria, 3334 Detailed address: &geo.Address{FormattedAddress:"Doyles Road, Elaine, West Central Victoria, Victoria, 3334", Street:"Doyles Road", HouseNumber:"", Suburb:"", Postcode:"3334", State:"Victoria", StateDistrict:"", County:"", Country:"Australia", CountryCode:"AU", City:"Elaine"} ``` -------------------------------- ### OpenStreetMap Nominatim Geocoder with Custom URL Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/api-reference/providers.md Initializes a geocoder using a custom Nominatim instance by providing its base URL. Useful for private Nominatim installations. ```go // Use a private Nominatim instance geocoder := openstreetmap.GeocoderWithURL("https://nominatim.example.com/") ``` -------------------------------- ### HTTPGeocoder Template Implementation Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/api-reference/special-geocoders.md Extend the HTTPGeocoder type to create custom geocoding providers. This example shows the basic structure for defining a geocoder with a custom response parser and URL builder. ```go package myprovider import "github.com/codingsince1985/geo-golang" type baseURL string type geocodeResponse struct { Results []struct { Lat float64 Lng float64 } } func (g *geocodeResponse) Location() (*geo.Location, error) { if len(g.Results) == 0 { return nil, nil } return &geo.Location{ Lat: g.Results[0].Lat, Lng: g.Results[0].Lng, }, } } func (g *geocodeResponse) Address() (*geo.Address, error) { // Parse address... return nil, nil } func (b baseURL) GeocodeURL(address string) string { return string(b) + "?q=" + address } func (b baseURL) ReverseGeocodeURL(l geo.Location) string { // Build reverse URL... return "" } func Geocoder(apiKey string) geo.Geocoder { return geo.HTTPGeocoder{ EndpointBuilder: baseURL("https://api.example.com/"), ResponseParserFactory: func() geo.ResponseParser { return &geocodeResponse{} }, } } ``` -------------------------------- ### Initialize and Use Geocoding Services in Go Source: https://github.com/codingsince1985/geo-golang/blob/master/README.md This snippet demonstrates how to initialize and use various geocoding services like Google, Mapquest, OpenCage, HERE, Bing, Baidu, Amap, Mapbox, OpenStreetMap, PickPoint, LocationIQ, ArcGIS, geocod.io, Mapzen, TomTom, Yandex, and FrenchAPIGouv. It also shows how to set up a chained geocoder for fallback. ```Go package main import ( "fmt" "os" "github.com/codingsince1985/geo-golang" "github.com/codingsince1985/geo-golang/arcgis" "github.com/codingsince1985/geo-golang/baidu" "github.com/codingsince1985/geo-golang/bing" "github.com/codingsince1985/geo-golang/chained" "github.com/codingsince1985/geo-golang/frenchapigouv" "github.com/codingsince1985/geo-golang/geocod" "github.com/codingsince1985/geo-golang/google" "github.com/codingsince1985/geo-golang/here" "github.com/codingsince1985/geo-golang/locationiq" "github.com/codingsince1985/geo-golang/mapbox" "github.com/codingsince1985/geo-golang/mapquest/nominatim" "github.com/codingsince1985/geo-golang/mapquest/open" "github.com/codingsince1985/geo-golang/mapzen" "github.com/codingsince1985/geo-golang/opencage" "github.com/codingsince1985/geo-golang/openstreetmap" "github.com/codingsince1985/geo-golang/pickpoint" "github.com/codingsince1985/geo-golang/tomtom" "github.com/codingsince1985/geo-golang/yandex" ) const ( addr = "Melbourne VIC" lat, lng = -37.813611, 144.963056 radius = 50 zoom = 18 addrFR = "Champs de Mars Paris" latFR, lngFR = 48.854395, 2.304770 ) func main() { ExampleGeocoder() } // ExampleGeocoder demonstrates the different geocoding services func ExampleGeocoder() { fmt.Println("Google Geocoding API") try(google.Geocoder(os.Getenv("GOOGLE_API_KEY"))) fmt.Println("Mapquest Nominatim") try(nominatim.Geocoder(os.Getenv("MAPQUEST_NOMINATIM_KEY"))) fmt.Println("Mapquest Open streetmaps") try(open.Geocoder(os.Getenv("MAPQUEST_OPEN_KEY"))) fmt.Println("OpenCage Data") try(opencage.Geocoder(os.Getenv("OPENCAGE_API_KEY"))) fmt.Println("HERE API") try(here.Geocoder(os.Getenv("HERE_APP_ID"), os.Getenv("HERE_APP_CODE"), radius)) fmt.Println("Bing Geocoding API") try(bing.Geocoder(os.Getenv("BING_API_KEY"))) fmt.Println("Baidu Geocoding API") try(baidu.Geocoder(os.Getenv("BAIDU_API_KEY"))) fmt.Println("Amap Geocoding API") try(amap.Geocoder(os.Getenv("AMAP_APP_KEY"))) fmt.Println("Mapbox API") try(mapbox.Geocoder(os.Getenv("MAPBOX_API_KEY"))) fmt.Println("OpenStreetMap") try(openstreetmap.Geocoder()) fmt.Println("PickPoint") try(pickpoint.Geocoder(os.Getenv("PICKPOINT_API_KEY"))) fmt.Println("LocationIQ") try(locationiq.Geocoder(os.Getenv("LOCATIONIQ_API_KEY"), zoom)) fmt.Println("ArcGIS") try(arcgis.Geocoder(os.Getenv("ARCGIS_TOKEN"))) fmt.Println("geocod.io") try(geocod.Geocoder(os.Getenv("GEOCOD_API_KEY"))) fmt.Println("Mapzen") try(mapzen.Geocoder(os.Getenv("MAPZEN_API_KEY"))) fmt.Println("TomTom") try(tomtom.Geocoder(os.Getenv("TOMTOM_API_KEY"))) fmt.Println("Yandex") try(yandex.Geocoder(os.Getenv("YANDEX_API_KEY"))) // To use only with french locations or addresses, // or values ​​could be estimated and will be false fmt.Println("FrenchAPIGouv") tryOnlyFRData(frenchapigouv.Geocoder()) // Chained geocoder will fallback to subsequent geocoders fmt.Println("ChainedAPI[OpenStreetmap -> Google]") try(chained.Geocoder( openstreetmap.Geocoder(), google.Geocoder(os.Getenv("GOOGLE_API_KEY")), )) } func try(geocoder geo.Geocoder) { location, _ := geocoder.Geocode(addr) if location != nil { fmt.Printf("%s location is (%.6f, %.6f)\n", addr, location.Lat, location.Lng) } else { ``` -------------------------------- ### Create a New Cache Instance Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/configuration.md Initializes a new cache using the 'go-cache' library. Configure default expiration and cleanup intervals based on your needs. ```go import "github.com/patrickmn/go-cache" // Cache with 5 minute default expiration, cleanup every 10 minutes c := cache.New(5*time.Minute, 10*time.Minute) ``` -------------------------------- ### Create and Use Location Instance Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/types.md Demonstrates creating a Location instance and using it in reverse geocoding and map data structures. ```go // Create a location loc := geo.Location{Lat: 51.5074, Lng: -0.1278} // London // Use in reverse geocoding geocoder := google.Geocoder(apiKey) address, err := geocoder.ReverseGeocode(loc.Lat, loc.Lng) // Use in maps coordinates := make(map[string]geo.Location) coordinates["London"] = geo.Location{Lat: 51.5074, Lng: -0.1278} coordinates["Paris"] = geo.Location{Lat: 48.8566, Lng: 2.3522} ``` -------------------------------- ### Create a Test Geocoder Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/quick-reference.md Set up a mock geocoder for testing purposes using predefined address-to-location mappings. ```go func testGeocoder() geo.Geocoder { return data.Geocoder( data.AddressToLocation{ geo.Address{FormattedAddress: "London"}: geo.Location{Lat: 51.5074, Lng: -0.1278}, }, nil, ) } ``` -------------------------------- ### Imports for Major Providers Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/quick-reference.md Demonstrates importing the core package along with several major geocoding provider packages. ```go import ( geo "github.com/codingsince1985/geo-golang" "github.com/codingsince1985/geo-golang/google" "github.com/codingsince1985/geo-golang/openstreetmap" "github.com/codingsince1985/geo-golang/bing" "github.com/codingsince1985/geo-golang/here" "github.com/codingsince1985/geo-golang/mapbox" // ... etc ) ``` -------------------------------- ### HTTP Status Code Error Mitigation (Rate Limiting) Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/errors.md Provides an example of how to mitigate rate limiting errors by implementing a backoff strategy. ```go if err != nil && strings.Contains(err.Error(), "OVER_QUERY_LIMIT") { // Implement backoff time.Sleep(time.Second) return retry() } ``` -------------------------------- ### Basic Geocoding with Google Provider Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/README.md Demonstrates how to geocode an address using the Google Maps provider. Ensure the GOOGLE_API_KEY environment variable is set. ```go package main import ( "github.com/codingsince1985/geo-golang/google" "os" ) func main() { geocoder := google.Geocoder(os.Getenv("GOOGLE_API_KEY")) location, err := geocoder.Geocode("London") if err != nil { panic(err) } if location != nil { println(location.Lat, location.Lng) } } ``` -------------------------------- ### Configuration: Enable Logging Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/quick-reference.md Configure logging for errors and debug messages. Use `io.Discard` to disable logging. ```go // Enable error logging import "log" geocoder.ErrLogger = log.New(os.Stderr, "[ERR]", log.Lshortfile) // Enable debug logging (see API responses) geocoder.DebugLogger = log.New(os.Stderr, "[DBG]", log.Lshortfile) // Disable logging (default) geocoder.ErrLogger = log.New(io.Discard, "", 0) ``` -------------------------------- ### Create Geocoder with Wrappers (Test Data) Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/quick-reference.md Use a geocoder that serves predefined test data for addresses and locations. ```go import ( "github.com/patrickmn/go-cache" "time" ) // Test data geocoder := data.Geocoder(addressToLoc, locToAddr) ``` -------------------------------- ### Handling Google Maps Geocoding Errors Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/errors.md Shows an example of a geocoding request to Google Maps that is expected to fail, resulting in a provider-specific error message. ```Go location, err := geocoder.Geocode("xyz123invalid") // err: "geocoding error: ZERO_RESULTS" (treated as no result, location is nil) // OR // err: "geocoding error: REQUEST_DENIED" (actual error) ``` -------------------------------- ### IP2Geo API Response Structure Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/api-reference/special-geocoders.md Example JSON structure of a successful response from the IP2Geo API, detailing the geographical and network information for an IP address. ```json { "success": true, "code": 0, "message": "Request successful", "data": { "ip": "8.8.8.8", "type": "IPv4", "continent": { "name": "North America", "code": "NA", "country": { "name": "United States", "code": "US", "phone_code": "+1", "capital": "Washington", "city": { "name": "Mountain View", "latitude": 37.386, "longitude": -122.085, "postal_code": "94040", "timezone": { "name": "America/Los_Angeles" } } } }, "asn": { "number": 15169, "name": "Google LLC" } } } ``` -------------------------------- ### Load API Key from Environment Variable Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/configuration.md Loads an API key from an environment variable using the 'os' package. Includes error handling if the variable is not set. ```go import "os" apiKey := os.Getenv("GOOGLE_API_KEY") if apiKey == "" log.Fatal("GOOGLE_API_KEY not set") } ``` -------------------------------- ### Standard Provider Constructor Source: https://github.com/codingsince1985/geo-golang/blob/master/_autodocs/quick-reference.md Illustrates the common constructor pattern for geocoding providers that require an API key and optionally accept base URLs. ```go func Geocoder(apiKey string, baseURLs ...string) geo.Geocoder ```