Rgeo is a fast, simple solution for local reverse geocoding, Rather than relying on external software or online APIs, rgeo packages all of the data it needs in your binary. This means it will only ever work down to the level of cities , but if that's all you need then this is the library for you.
Rgeo uses data from naturalearthdata.com, if your coordinates are going to be near specific borders I would advise checking the data beforehand (links to which are in the files). If you want to use your own dataset, check out datagen.
- Fast - So I haven't actually benchmarked other reverse geocoding tools but on my laptop rgeo can run at under 800ns/op.
- Local - Rgeo doesn't require pinging some API, most of which either cost money to use or have severe rate limits.
- Lightweight - The rgeo repo is 32MB, which is large for a Go package but compared to the 800GB needed for a full planet install of Nominatim it's miniscule.
Download with
go get github.com/sams96/rgeo
and add
import "github.com/sams96/rgeo"
to the top of your Go file to include it in your project.
r, err := New(Provinces10, Cities10)
if err != nil {
// Handle error
}
loc, err := r.ReverseGeocode([]float64{141.35, 43.07})
if err != nil {
// Handle error
}
fmt.Println(loc)
// Output: <Location> Sapporo, Hokkaidō, Japan (JPN), Asia
First initialise rgeo using rgeo.New
,
func New(datasets ...func() []byte) (*Rgeo, error)
which takes any non-zero number of datasets as arguments. The included datasets are:
Countries110
- Just country information, smallest and lowest detail of the included datasets.Countries10
- The same as above but with more detail.Provinces10
- Includes province information as well as country, so can still be used alone.Cities10
- Just city information, if you want provinces and/or countries as well use one of the above datasets with it. Once initialised you can useReverseGeocode
on the value returned byNew
, with your coordinates to get the location information. See the Go Docs for more information on usage.
Then use ReverseGeocode
to get the location information of the given coordinate.
func (r *Rgeo) ReverseGeocode(loc geom.Coord) (Location, error)
The input is a geom.Coord
, which is just
a []float64
with the longitude in the zeroth position and the latitude in the
first position (i.e. []float64{lon, lat}
). ReverseGeocode
returns a
Location
, which looks like this:
type Location struct {
// Commonly used country name
Country string `json:"country,omitempty"`
// Formal name of country
CountryLong string `json:"country_long,omitempty"`
// ISO 3166-1 alpha-1 and alpha-2 codes
CountryCode2 string `json:"country_code_2,omitempty"`
CountryCode3 string `json:"country_code_3,omitempty"`
Continent string `json:"continent,omitempty"`
Region string `json:"region,omitempty"`
SubRegion string `json:"subregion,omitempty"`
Province string `json:"province,omitempty"`
// ISO 3166-2 code
ProvinceCode string `json:"province_code,omitempty"`
City string `json:"city,omitempty"`
}
So, to put it all together:
r, err := rgeo.New(Countries110)
if err != nil {
// Handle error
}
loc, err := r.ReverseGeocode([]float64{0, 52})
if err != nil {
// Handle error
}
fmt.Printf("%s\n", loc.Country)
fmt.Printf("%s\n", loc.CountryLong)
fmt.Printf("%s\n", loc.CountryCode2)
fmt.Printf("%s\n", loc.CountryCode3)
fmt.Printf("%s\n", loc.Continent)
fmt.Printf("%s\n", loc.Region)
fmt.Printf("%s\n", loc.SubRegion)
// Output: United Kingdom
// United Kingdom of Great Britain and Northern Ireland
// GB
// GBR
// Europe
// Europe
// Northern Europe
If you have troubles resolving the location of the coordinates due to GPS and/or
data inaccuracy (e.g. at the coast, GPS might give you coordinates of a location
several meters out in the water) you can use ReverseGeocodeSnapping
instead of
ReverseGeocode
.
func (r *Rgeo) ReverseGeocodeSnapping(loc geom.Coord) (Location, error)
It first tries to find the location using ReverseGeocode
and only if that fails,
it will look up the closest location in a default radius of 5km. This radius can
be set via the SetSnappingDistanceEarth
and SetSnappingDistanceCustom
functions.
func (r *Rgeo) SetSnappingDistanceEarth(d float64)
func (r *Rgeo) SetSnappingDistanceCustom(d float64, radius float64)
SetSnappingDistanceEarth
takes the radius float64
in km, which is the search
radius of the alternative location. If the dataset contains geo-data of a sphere
that has different dimensions than earth, use SetSnappingDistanceCustom
with the
distance d float64
in the same units used for the radius of the sphere.
Contributions are welcome, I haven't got any guidelines or anything so maybe just make an issue first.
- rgeoSrv - rgeo as a microservice