nominatim

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 21, 2026 License: MIT Imports: 10 Imported by: 0

README

nominatim-go-lite

A tiny, dependency-free Go client for geocoding addresses with OpenStreetMap Nominatim.

The package uses the Nominatim /search endpoint with format=jsonv2, returns the best result by default, and exposes a small Search method when you need multiple matches.

Installation

go get github.com/teddy-vltn/nominatim-go-lite

Quick Start

package main

import (
	"context"
	"fmt"
	"log"

	nominatim "github.com/teddy-vltn/nominatim-go-lite"
)

func main() {
	client := nominatim.NewClient(
		nominatim.WithUserAgent("my-app/1.0"),
		nominatim.WithContactEmail("developer@example.com"),
	)

	location, err := client.Geocode(
		context.Background(),
		"5 Avenue Anatole France, 75007 Paris, France",
	)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(location.Latitude, location.Longitude)
}

Multiple Results

locations, err := client.Search(
	context.Background(),
	"Paris",
	nominatim.WithSearchLimit(3),
)
if err != nil {
	log.Fatal(err)
}

for _, location := range locations {
	fmt.Println(location.DisplayName, location.Latitude, location.Longitude)
}

Configuration

httpClient := &http.Client{Timeout: 5 * time.Second}

client := nominatim.NewClient(
	nominatim.WithUserAgent("my-app/1.0"),
	nominatim.WithContactEmail("developer@example.com"),
	nominatim.WithHTTPClient(httpClient),
	nominatim.WithResultLimit(5),
	nominatim.WithPreferredLanguage("fr"),
	nominatim.WithCountryCodes("fr", "be", "ch"),
)

Public Nominatim Usage Policy

When using the public OpenStreetMap Nominatim service, your application must follow the Nominatim usage policy:

  • Identify your application with a valid User-Agent.
  • Avoid exceeding one request per second.
  • Cache repeated queries.
  • Provide OpenStreetMap attribution.

This library requires a non-empty User-Agent before it sends a request. The library does not add rate limiting or caching for you, because those choices are application-specific.

See the current policy at https://operations.osmfoundation.org/policies/nominatim/.

Attribution

Public Nominatim results are based on OpenStreetMap data. Applications using the public service should display attribution such as:

© OpenStreetMap contributors

See https://www.openstreetmap.org/copyright.

Self-Hosted Nominatim

Use WithBaseURL to point at a self-hosted Nominatim instance:

client := nominatim.NewClient(
	nominatim.WithBaseURL("https://nominatim.example.com"),
	nominatim.WithUserAgent("my-app/1.0"),
)

The client appends /search to the configured base URL.

API Reference Summary

func NewClient(options ...Option) *Client
func (c *Client) Geocode(ctx context.Context, address string) (Location, error)
func (c *Client) Search(ctx context.Context, address string, options ...SearchOption) ([]Location, error)

Geocode calls Search with a limit of one and returns the best result.

type Location struct {
	Latitude    float64
	Longitude   float64
	DisplayName string
	Type        string
	Importance  float64
}

Client options:

  • WithUserAgent(string)
  • WithContactEmail(string)
  • WithBaseURL(string)
  • WithHTTPClient(*http.Client)
  • WithResultLimit(int)
  • WithPreferredLanguage(string)
  • WithCountryCodes(...string)

Per-search options:

  • WithSearchLimit(int)
  • WithSearchPreferredLanguage(string)
  • WithSearchCountryCodes(...string)

Typed errors:

  • ErrEmptyAddress
  • ErrNoResults
  • ErrInvalidJSON
  • ErrInvalidCoordinate
  • ErrMissingUserAgent
  • ErrInvalidLimit
  • *HTTPError
  • *InvalidCoordinateError

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrEmptyAddress      = errors.New("nominatim: empty address")
	ErrNoResults         = errors.New("nominatim: no results")
	ErrInvalidJSON       = errors.New("nominatim: invalid JSON response")
	ErrInvalidCoordinate = errors.New("nominatim: invalid coordinate")
	ErrMissingUserAgent  = errors.New("nominatim: user agent is required")
	ErrInvalidLimit      = errors.New("nominatim: result limit must be greater than zero")
)

Functions

This section is empty.

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

func NewClient

func NewClient(options ...Option) *Client

func (*Client) Geocode

func (c *Client) Geocode(ctx context.Context, address string) (Location, error)
Example
package main

import (
	"context"
	"fmt"
	"net/http"
	"net/http/httptest"

	nominatim "github.com/teddy-vltn/nominatim-go-lite"
)

func main() {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		_, _ = w.Write([]byte(`[{"lat":"48.8583701","lon":"2.2944813","display_name":"Eiffel Tower, Paris, France","type":"tourism","importance":0.9}]`))
	}))
	defer server.Close()

	client := nominatim.NewClient(
		nominatim.WithBaseURL(server.URL),
		nominatim.WithUserAgent("my-app/1.0"),
		nominatim.WithContactEmail("developer@example.com"),
	)

	location, err := client.Geocode(
		context.Background(),
		"5 Avenue Anatole France, 75007 Paris, France",
	)
	if err != nil {
		panic(err)
	}

	fmt.Println(location.Latitude, location.Longitude)
}
Output:
48.8583701 2.2944813

func (*Client) Search

func (c *Client) Search(ctx context.Context, address string, options ...SearchOption) ([]Location, error)
Example
package main

import (
	"context"
	"fmt"
	"net/http"
	"net/http/httptest"

	nominatim "github.com/teddy-vltn/nominatim-go-lite"
)

func main() {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		_, _ = w.Write([]byte(`[
			{"lat":"48.8566","lon":"2.3522","display_name":"Paris, France","type":"city","importance":0.9},
			{"lat":"33.6609","lon":"-95.5555","display_name":"Paris, Texas","type":"city","importance":0.6}
		]`))
	}))
	defer server.Close()

	client := nominatim.NewClient(
		nominatim.WithBaseURL(server.URL),
		nominatim.WithUserAgent("my-app/1.0"),
	)

	locations, err := client.Search(
		context.Background(),
		"Paris",
		nominatim.WithSearchLimit(2),
	)
	if err != nil {
		panic(err)
	}

	fmt.Println(len(locations))
}
Output:
2

type HTTPError

type HTTPError struct {
	StatusCode int
	Status     string
	Body       string
}

func (*HTTPError) Error

func (e *HTTPError) Error() string

type InvalidCoordinateError

type InvalidCoordinateError struct {
	Field string
	Value string
}

func (*InvalidCoordinateError) Error

func (e *InvalidCoordinateError) Error() string

func (*InvalidCoordinateError) Unwrap

func (e *InvalidCoordinateError) Unwrap() error

type Location

type Location struct {
	Latitude    float64
	Longitude   float64
	DisplayName string
	Type        string
	Importance  float64
}

type Option

type Option func(*Client)

func WithBaseURL

func WithBaseURL(baseURL string) Option

func WithContactEmail

func WithContactEmail(email string) Option

func WithCountryCodes

func WithCountryCodes(countryCodes ...string) Option

func WithHTTPClient

func WithHTTPClient(client *http.Client) Option

func WithPreferredLanguage

func WithPreferredLanguage(language string) Option

func WithResultLimit

func WithResultLimit(limit int) Option

func WithUserAgent

func WithUserAgent(userAgent string) Option

type SearchOption

type SearchOption func(*searchConfig)

func WithSearchCountryCodes

func WithSearchCountryCodes(countryCodes ...string) SearchOption

func WithSearchLimit

func WithSearchLimit(limit int) SearchOption

func WithSearchPreferredLanguage

func WithSearchPreferredLanguage(language string) SearchOption

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL