Skip to content

Commit f2bf9b2

Browse files
mekegicodingsince1985
authored andcommitted
1 parent 9ce7634 commit f2bf9b2

File tree

4 files changed

+704
-0
lines changed

4 files changed

+704
-0
lines changed

README.md

+10
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ This product is designed to open to any Geocoding service. Based on it,
2525
+ [geocodio](https://geocod.io)
2626
+ [Mapzen](https://mapzen.com)
2727
+ [TomTom](https://www.tomtom.com)
28+
+ [Yandex.Maps](https://tech.yandex.com/maps/doc/geocoder/desc/concepts/About-docpage/)
2829

2930
clients are implemented in ~50 LoC each.
3031

@@ -53,6 +54,7 @@ import (
5354
"github.com/codingsince1985/geo-golang/openstreetmap"
5455
"github.com/codingsince1985/geo-golang/pickpoint"
5556
"github.com/codingsince1985/geo-golang/tomtom"
57+
"github.com/codingsince1985/geo-golang/yandex"
5658
)
5759

5860
const (
@@ -110,6 +112,9 @@ func ExampleGeocoder() {
110112
fmt.Println("TomTom")
111113
try(tomtom.Geocoder(os.Getenv("TOMTOM_API_KEY")))
112114

115+
fmt.Println("Yandex")
116+
try(yandex.Geocoder(os.Getenv("YANDEX_API_KEY")))
117+
113118
// Chained geocoder will fallback to subsequent geocoders
114119
fmt.Println("ChainedAPI[OpenStreetmap -> Google]")
115120
try(chained.Geocoder(
@@ -206,6 +211,11 @@ Melbourne VIC location is (-37.815340, 144.963230)
206211
Address of (-37.813611,144.963056) is Doyles Road, Elaine, West Central Victoria, Victoria, 3334
207212
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"}
208213
214+
Yandex
215+
Melbourne VIC location is (41.926823, 2.254232)
216+
Address of (-37.813611,144.963056) is Victoria, City of Melbourne, Elizabeth Street
217+
Detailed address: &geo.Address{FormattedAddress:"Victoria, City of Melbourne, Elizabeth Street", Street:"Elizabeth Street", HouseNumber:"", Suburb:"", Postcode:"", State:"Victoria", StateDistrict:"", County:"", Country:"Australia", CountryCode:"AU", City:"City of Melbourne"}
218+
209219
ChainedAPI[OpenStreetmap -> Google]
210220
Melbourne VIC location is (-37.814217, 144.963161)
211221
Address of (-37.813611,144.963056) is Melbourne's GPO, Postal Lane, Chinatown, Melbourne, City of Melbourne, Greater Melbourne, Victoria, 3000, Australia

examples/geocoder_example.go

+11
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/codingsince1985/geo-golang/openstreetmap"
2121
"github.com/codingsince1985/geo-golang/pickpoint"
2222
"github.com/codingsince1985/geo-golang/tomtom"
23+
"github.com/codingsince1985/geo-golang/yandex"
2324
)
2425

2526
const (
@@ -77,6 +78,9 @@ func ExampleGeocoder() {
7778
fmt.Println("TomTom")
7879
try(tomtom.Geocoder(os.Getenv("TOMTOM_API_KEY")))
7980

81+
fmt.Println("Yandex")
82+
try(yandex.Geocoder(os.Getenv("YANDEX_API_KEY")))
83+
8084
// Chained geocoder will fallback to subsequent geocoders
8185
fmt.Println("ChainedAPI[OpenStreetmap -> Google]")
8286
try(chained.Geocoder(
@@ -171,6 +175,13 @@ func ExampleGeocoder() {
171175
// Address of (-37.813611,144.963056) is Doyles Road, Elaine, West Central Victoria, Victoria, 3334
172176
// 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"}
173177
//
178+
// Yandex
179+
// Melbourne VIC location is (41.926823, 2.254232)
180+
// Address of (-37.813611,144.963056) is Victoria, City of Melbourne, Elizabeth Street
181+
// Detailed address: &geo.Address{FormattedAddress:"Victoria, City of Melbourne, Elizabeth Street", Street:"Elizabeth Street",
182+
// HouseNumber:"", Suburb:"", Postcode:"", State:"Victoria", StateDistrict:"", County:"", Country:"Australia", CountryCode:"AU",
183+
// City:"City of Melbourne"}
184+
//
174185
// ChainedAPI[OpenStreetmap -> Google]
175186
// Melbourne VIC location is (-37.814217, 144.963161)
176187
// Address of (-37.813611,144.963056) is Melbourne's GPO, Postal Lane, Chinatown, Melbourne, City of Melbourne, Greater Melbourne, Victoria, 3000, Australia

yandex/geocoder.go

+156
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
// Package yandex is a geo-golang based Yandex Maps Location API
2+
package yandex
3+
4+
import (
5+
"fmt"
6+
"strconv"
7+
"strings"
8+
9+
"github.com/codingsince1985/geo-golang"
10+
)
11+
12+
type (
13+
baseURL string
14+
geocodeResponse struct {
15+
Response struct {
16+
GeoObjectCollection struct {
17+
MetaDataProperty struct {
18+
GeocoderResponseMetaData struct {
19+
Request string `json:"request"`
20+
Found string `json:"found"`
21+
Results string `json:"results"`
22+
} `json:"GeocoderResponseMetaData"`
23+
} `json:"metaDataProperty"`
24+
FeatureMember []*yandexFeatureMember `json:"featureMember"`
25+
} `json:"GeoObjectCollection"`
26+
} `json:"response"`
27+
}
28+
29+
yandexFeatureMember struct {
30+
GeoObject struct {
31+
MetaDataProperty struct {
32+
GeocoderMetaData struct {
33+
Kind string `json:"kind"`
34+
Text string `json:"text"`
35+
Precision string `json:"precision"`
36+
Address struct {
37+
CountryCode string `json:"country_code"`
38+
PostalCode string `json:"postal_code"`
39+
Formatted string `json:"formatted"`
40+
Components []struct {
41+
Kind string `json:"kind"`
42+
Name string `json:"name"`
43+
} `json:"Components"`
44+
} `json:"Address"`
45+
} `json:"GeocoderMetaData"`
46+
} `json:"metaDataProperty"`
47+
Description string `json:"description"`
48+
Name string `json:"name"`
49+
BoundedBy struct {
50+
Envelope struct {
51+
LowerCorner string `json:"lowerCorner"`
52+
UpperCorner string `json:"upperCorner"`
53+
} `json:"Envelope"`
54+
} `json:"boundedBy"`
55+
Point struct {
56+
Pos string `json:"pos"`
57+
} `json:"Point"`
58+
} `json:"GeoObject"`
59+
}
60+
)
61+
62+
const (
63+
componentTypeHouseNumber = "house"
64+
componentTypeStreetName = "street"
65+
componentTypeLocality = "locality"
66+
componentTypeStateDistrict = "area"
67+
componentTypeState = "province"
68+
componentTypeCountry = "country"
69+
)
70+
71+
// Geocoder constructs Yandex geocoder
72+
func Geocoder(apiKey string, baseURLs ...string) geo.Geocoder {
73+
return geo.HTTPGeocoder{
74+
EndpointBuilder: baseURL(getURL(apiKey, baseURLs...)),
75+
ResponseParserFactory: func() geo.ResponseParser { return &geocodeResponse{} },
76+
}
77+
}
78+
79+
func getURL(apiKey string, baseURLs ...string) string {
80+
if len(baseURLs) > 0 {
81+
return baseURLs[0]
82+
}
83+
return fmt.Sprintf("https://geocode-maps.yandex.ru/1.x/?results=1&lang=en_US&format=json&apikey=%s&", apiKey)
84+
}
85+
86+
func (b baseURL) GeocodeURL(address string) string {
87+
return string(b) + "geocode=" + address
88+
}
89+
90+
func (b baseURL) ReverseGeocodeURL(l geo.Location) string {
91+
return string(b) + fmt.Sprintf("sco=latlong&geocode=%f,%f", l.Lat, l.Lng)
92+
}
93+
94+
func (r *geocodeResponse) Location() (*geo.Location, error) {
95+
if r.Response.GeoObjectCollection.MetaDataProperty.GeocoderResponseMetaData.Found == "0" {
96+
return nil, nil
97+
}
98+
if len(r.Response.GeoObjectCollection.FeatureMember) == 0 {
99+
return nil, nil
100+
}
101+
featureMember := r.Response.GeoObjectCollection.FeatureMember[0]
102+
result := &geo.Location{}
103+
latLng := strings.Split(featureMember.GeoObject.Point.Pos, " ")
104+
if len(latLng) > 1 {
105+
// Yandex return geo coord in format "long lat"
106+
result.Lat, _ = strconv.ParseFloat(latLng[1], 64)
107+
result.Lng, _ = strconv.ParseFloat(latLng[0], 64)
108+
}
109+
110+
return result, nil
111+
}
112+
113+
func (r *geocodeResponse) Address() (*geo.Address, error) {
114+
if r.Response.GeoObjectCollection.MetaDataProperty.GeocoderResponseMetaData.Found == "0" {
115+
return nil, nil
116+
}
117+
if len(r.Response.GeoObjectCollection.FeatureMember) == 0 {
118+
return nil, nil
119+
}
120+
121+
return parseYandexResult(r.Response.GeoObjectCollection.FeatureMember[0]), nil
122+
}
123+
124+
func parseYandexResult(r *yandexFeatureMember) *geo.Address {
125+
addr := &geo.Address{}
126+
res := r.GeoObject.MetaDataProperty.GeocoderMetaData
127+
128+
for _, comp := range res.Address.Components {
129+
switch comp.Kind {
130+
case componentTypeHouseNumber:
131+
addr.HouseNumber = comp.Name
132+
continue
133+
case componentTypeStreetName:
134+
addr.Street = comp.Name
135+
continue
136+
case componentTypeLocality:
137+
addr.City = comp.Name
138+
continue
139+
case componentTypeStateDistrict:
140+
addr.StateDistrict = comp.Name
141+
continue
142+
case componentTypeState:
143+
addr.State = comp.Name
144+
continue
145+
case componentTypeCountry:
146+
addr.Country = comp.Name
147+
continue
148+
}
149+
}
150+
151+
addr.Postcode = res.Address.PostalCode
152+
addr.CountryCode = res.Address.CountryCode
153+
addr.FormattedAddress = res.Address.Formatted
154+
155+
return addr
156+
}

0 commit comments

Comments
 (0)