geo-golang

repository·master·Indexed 20 days ago

https://github.com/codingsince1985/geo-golang

A generic Go framework for developing geocoding and reverse geocoding clients. It provides a unified Geocoder interface to interact with multiple providers—including Google Maps, Mapbox, OpenStreetMap, HERE, Bing, and others—and includes a chained geocoder for implementing fallback logic. The library handles the conversion of human-readable addresses to coordinates (Location) and coordinates to structured address objects (Address).

Tokens
2.8K
Snippets
13
Records
14
Agent score
67%

What's inside geo-golang

  1. Use the Geocoder interface for multiple services

    master

    The geo-golang library provides a unified Geocoder interface that allows you to interact with various geocoding providers (like Google Maps, Mapbox, OpenStreetMap, etc.) using a consistent API.

    Each provider is implemented as a client that satisfies the geo.Geocoder interface. This allows you to switch between services by changing a single line of code or to use a chained geocoder to implement fallback logic (e.g., if the first service fails, try the second).

    To use a service, call its specific constructor (e.g., google.Geocoder(apiKey)) which returns a geo.Geocoder instance. You can then call .Geocode(address) to get coordinates or .ReverseGeocode(lat, lng) to get an address.

    // Example of using a specific provider
    location, _ := google.Geocoder(apiKey).Geocode("Melbourne VIC")
    
    // Example of using a chained geocoder for fallback
    geocoder := chained.Geocoder(
        openstreetmap.Geocoder(),
        google.Geocoder(apiKey),
    )
    location, _ := geocoder.Geocode("Melbourne VIC")
  2. Implement fallback logic with Chained Geocoders

    master

    The chained package allows you to create a single geo.Geocoder that attempts to resolve a request using a sequence of providers. If one provider returns no result, the library automatically falls back to the next provider in the chain. This is useful for maximizing uptime and utilizing free quotas across multiple services.

    import (
        "github.com/codingsince1985/geo-golang/chained"
        "github.com/codingsince1985/geo-golang/openstreetmap"
        "github.com/codingsince1985/geo-golang/google"
    )
    
    // Chained geocoder will fallback to subsequent geocoders
    geocoder := chained.Geocoder(
        openstreetmap.Geocoder(),
        google.Geocoder(os.Getenv("GOOGLE_API_KEY")),
    )
    
    location, _ := geocoder.Geocode("Melbourne VIC")
  3. Perform Geocoding and Reverse Geocoding

    master

    Once you have a geo.Geocoder instance, you can perform two primary operations:

    1. Geocoding: Convert a human-readable address into geographic coordinates (Latitude/Longitude).
    2. Reverse Geocoding: Convert geographic coordinates into a structured geo.Address object.

    Note: Always check if the returned location or address is nil to handle cases where the service could not find a match.

    // Assuming 'geocoder' is an initialized geo.Geocoder
    
    // 1. Geocoding
    location, _ := geocoder.Geocode("Melbourne VIC")
    if location != nil {
        fmt.Printf("Lat: %.6f, Lng: %.6f\n", location.Lat, location.Lng)
    }
    
    // 2. Reverse Geocoding
    address, _ := geocoder.ReverseGeocode(-37.813611, 144.963056)
    if address != nil {
        fmt.Printf("Address: %s\n", address.FormattedAddress)
        fmt.Printf("City: %s, Country: %s\n", address.City, address.Country)
    }
  4. Configure logging with StdLogger

    master

    The geo package uses two global loggers for error and debug reporting. You can replace these with your own logging implementation by satisfying the StdLogger interface.

    • ErrLogger: Used for error messages. Defaults to io.Discard.
    • DebugLogger: Used for debug messages. Defaults to io.Discard.

    To customize logging, assign a new implementation to these variables:

    type myLogger struct{}
    func (l *myLogger) Printf(format string, v ...interface{}) { /* implementation */ }
    
    geo.ErrLogger = &myLogger{}
    var ErrLogger StdLogger = log.New(io.Discard, "[Geo][Err]", log.LstdFlags)
    var DebugLogger StdLogger = log.New(io.Discard, "[Geo][Debug]", log.LstdFlags)
    
    type StdLogger interface {
    	Printf(string, ...interface{})
    }
  5. Supported Geocoding Services

    master

    The library provides clients for a wide variety of geocoding providers. Each client is typically initialized with an API key or specific configuration parameters.

    Supported providers include:

    • Google Maps (google)
    • Mapbox (mapbox)
    • OpenStreetMap (openstreetmap)
    • HERE (here)
    • Bing (bing)
    • MapQuest (mapquest/nominatim, mapquest/open)
    • OpenCage (opencage)
    • ArcGIS (arcgis)
    • geocod.io (geocod)
    • TomTom (tomtom)
    • Yandex (yandex)
    • Baidu (baidu)
    • French API Gouv (frenchapigouv)
    • LocationIQ (locationiq)
    • PickPoint (pickpoint)
    • Mapzen (mapzen)
    • Amap (amap)
    • Chained (chained - for fallback logic)
  6. Implement ResponseUnmarshaler for custom data formats

    master

    The ResponseUnmarshaler interface allows you to define how raw bytes from an HTTP response are converted into Go objects. HTTPGeocoder uses this to handle different content types.

    Available implementations:

    • JSONUnmarshaler: Trims whitespace and brackets before parsing JSON.
    • XMLUnmarshaler: Standard XML unmarshaling.

    You can provide your own implementation to the HTTPGeocoder.ResponseUnmarshaler field.

    type ResponseUnmarshaler interface {
    	Unmarshal(data []byte, v any) error
    }
    
    // Example of using the built-in JSON unmarshaler
    var unmarshaler = &geo.JSONUnmarshaler{}
  7. Use HTTPGeocoder for geocoding and reverse geocoding

    master

    The HTTPGeocoder struct is the primary entry point for performing geocoding (address to coordinates) and reverse geocoding (coordinates to address) via HTTP requests. It requires three components to function:

    1. EndpointBuilder: An interface to construct the service-specific URLs.
    2. ResponseParserFactory: A factory function that returns a ResponseParser to extract data from the response.
    3. ResponseUnmarshaler (Optional): An implementation of the ResponseUnmarshaler interface to handle data formats like JSON or XML. If not provided, it defaults to JSONUnmarshaler.

    Methods:

    • Geocode(address string) (*Location, error): Returns the Location for a given address. Uses a default timeout of DefaultTimeout (8 seconds).
    • ReverseGeocode(lat, lng float64) (*Address, error): Returns the Address for the given latitude and longitude. Uses a default timeout of DefaultTimeout (8 seconds).
    // Example setup (requires implementing EndpointBuilder and ResponseParserFactory)
    geocoder := geo.HTTPGeocoder{
        EndpointBuilder:        myEndpointBuilder,
        ResponseParserFactory:  myParserFactory,
        ResponseUnmarshaler:    &geo.JSONUnmarshaler{},
    }
    
    location, err := geocoder.Geocode("1600 Amphitheatre Parkway, Mountain View, CA")
    address, err := geocoder.ReverseGeocode(37.422, -122.084)
  8. Handle geocoding timeouts

    master

    The HTTPGeocoder methods use a context with a timeout. If the request does not complete within the DefaultTimeout (8 seconds), the operation will return ErrTimeout.

    DefaultTimeout is a constant of type time.Duration set to 8s.

    const DefaultTimeout = time.Second * 8
    var ErrTimeout = errors.New("TIMEOUT")
  9. Use the Address type for structured location data

    master

    The Address struct is returned by ReverseGeocode. It provides a structured representation of a location, including specific fields for street, suburb, city, and country, as well as a FormattedAddress string.

    type Address struct {
    	FormattedAddress string
    	Street           string
    	HouseNumber      string
    	Suburb           string
    	Postcode         string
    	State            string
    	StateCode        string
    	StateDistrict    string
    	County           string
    	Country          string
    	CountryCode      string
    	City             string
    }
  10. Implement ResponseParser to extract data

    master

    The ResponseParser interface is used to extract specific domain objects (Location or Address) from the unmarshaled response data. The HTTPGeocoder uses a factory pattern (ResponseParserFactory) to create a new parser for every request.

    type ResponseParser interface {
    	Location() (*Location, error)
    	Address() (*Address, error)
    }
    
    type ResponseParserFactory func() ResponseParser
  11. Implement or use the Geocoder interface

    master

    The Geocoder interface is the core abstraction for geocoding services. Any implementation must provide methods to convert an address string into coordinates (Geocode) and coordinates into a structured address (ReverseGeocode).

    type Geocoder interface {
    	Geocode(address string) (*Location, error)
    	ReverseGeocode(lat, lng float64) (*Address, error)
    }
    type Geocoder interface {
    	Geocode(address string) (*Location, error)
    	ReverseGeocode(lat, lng float64) (*Address, error)
    }