Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

issue/28: Exposing http client for google geocoder #29

Closed
wants to merge 14 commits into from
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# golang-geo changelog

## [0.5.0](https://github.com/kellydunn/golang-geo/tree/v0.5.0) January 14, 2015

- Exposes `GoogleGeocoder.HttpClient` so that clients may be able to swap out underlying http client implementations.

## [0.4.1](https://github.com/kellydunn/golang-geo/tree/v0.4.1) December 1, 2014

- Improves geocoder testing.

## [0.4.0](https://github.com/kellydunn/golang-geo/tree/v0.4.0) November 2, 2014

- Introduces the OpenCage Geocoder
Expand Down
1 change: 1 addition & 0 deletions VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.5.0
24 changes: 14 additions & 10 deletions google_geocoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import (

// This struct contains all the funcitonality
// of interacting with the Google Maps Geocoding Service
type GoogleGeocoder struct{}
type GoogleGeocoder struct{
HttpClient *http.Client
}

// This struct contains selected fields from Google's Geocoding Service response
type googleGeocodeResponse struct {
Expand Down Expand Up @@ -44,7 +46,11 @@ func SetGoogleGeocodeURL(newGeocodeURL string) {
// Issues a request to the google geocoding service and forwards the passed in params string
// as a URL-encoded entity. Returns an array of byes as a result, or an error if one occurs during the process.
func (g *GoogleGeocoder) Request(params string) ([]byte, error) {
client := &http.Client{}
if g.HttpClient == nil {
g.HttpClient = &http.Client{}
}

client := g.HttpClient

fullUrl := fmt.Sprintf("%s?sensor=false&%s", googleGeocodeURL, params)

Expand Down Expand Up @@ -74,29 +80,27 @@ func (g *GoogleGeocoder) Geocode(query string) (*Point, error) {
return nil, err
}

lat, lng, err := g.extractLatLngFromResponse(data)
point, err := g.extractLatLngFromResponse(data)
if err != nil {
return nil, err
}

p := &Point{lat: lat, lng: lng}

return p, nil
return &point, nil
}

// Extracts the first lat and lng values from a Google Geocoder Response body.
func (g *GoogleGeocoder) extractLatLngFromResponse(data []byte) (float64, float64, error) {
// Extracts the first location from a Google Geocoder Response body.
func (g *GoogleGeocoder) extractLatLngFromResponse(data []byte) (Point, error) {
res := &googleGeocodeResponse{}
json.Unmarshal(data, &res)

if len(res.Results) == 0 {
return 0, 0, googleZeroResultsError
return Point{}, googleZeroResultsError
}

lat := res.Results[0].Geometry.Location.Lat
lng := res.Results[0].Geometry.Location.Lng

return lat, lng, nil
return Point{lat, lng}, nil
}

// Reverse geocodes the pointer to a Point struct and returns the first address that matches
Expand Down
8 changes: 4 additions & 4 deletions google_geocoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ func TestExtractLatLngFromRequest(t *testing.T) {
t.Error("%v\n", err)
}

lat, lng, err := g.extractLatLngFromResponse(data)
point, err := g.extractLatLngFromResponse(data)
if err != nil {
t.Error("%v\n", err)
}

if lat != 37.615223 || lng != -122.389979 {
t.Error(fmt.Sprintf("Expected: [37.615223, -122.389979], Got: [%f, %f]", lat, lng))
if point.lat != 37.615223 || point.lng != -122.389979 {
t.Error(fmt.Sprintf("Expected: [37.615223, -122.389979], Got: [%f, %f]", point.lat, point.lng))
}
}

Expand All @@ -51,7 +51,7 @@ func TestExtractLatLngFromRequestZeroResults(t *testing.T) {
t.Error("%v\n", err)
}

_, _, err = g.extractLatLngFromResponse(data)
_, err = g.extractLatLngFromResponse(data)
if err != googleZeroResultsError {
t.Error(fmt.Sprintf("Expected error: %v, Got: %v"), googleZeroResultsError, err)
}
Expand Down
14 changes: 6 additions & 8 deletions mapquest_geocoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,29 +63,27 @@ func (g *MapQuestGeocoder) Geocode(query string) (*Point, error) {
return nil, err
}

lat, lng, extractErr := g.extractLatLngFromResponse(data)
point, extractErr := g.extractLatLngFromResponse(data)
if extractErr != nil {
return nil, extractErr
}

p := &Point{lat: lat, lng: lng}

return p, nil
return &point, nil
}

// Extracts the first lat and lng values from a MapQuest response body.
func (g *MapQuestGeocoder) extractLatLngFromResponse(data []byte) (float64, float64, error) {
// Extracts the first location from a MapQuest response body.
func (g *MapQuestGeocoder) extractLatLngFromResponse(data []byte) (Point, error) {
res := make([]map[string]interface{}, 0)
json.Unmarshal(data, &res)

if len(res) == 0 {
return 0, 0, mapquestZeroResultsError
return Point{}, mapquestZeroResultsError
}

lat, _ := strconv.ParseFloat(res[0]["lat"].(string), 64)
lng, _ := strconv.ParseFloat(res[0]["lon"].(string), 64)

return lat, lng, nil
return Point{lat, lng}, nil
}

// Returns the first most available address that corresponds to the passed in point.
Expand Down
8 changes: 4 additions & 4 deletions mapquest_geocoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ func TestMapQuestExtractLatLngFromRequest(t *testing.T) {
t.Error("%v\n", err)
}

lat, lng, err := g.extractLatLngFromResponse(data)
point, err := g.extractLatLngFromResponse(data)
if err != nil {
t.Error("%v\n", err)
}

if lat != 37.62181845 || lng != -122.383992092462 {
t.Error(fmt.Sprintf("Expected: [37.62181845, -122.383992092462], Got: [%f, %f]", lat, lng))
if point.lat != 37.62181845 || point.lng != -122.383992092462 {
t.Error(fmt.Sprintf("Expected: [37.62181845, -122.383992092462], Got: [%f, %f]", point.lat, point.lng))
}
}

Expand All @@ -33,7 +33,7 @@ func TestMapQuestExtractLatLngFromRequestZeroResults(t *testing.T) {
t.Error("%v\n", err)
}

_, _, err = g.extractLatLngFromResponse(data)
_, err = g.extractLatLngFromResponse(data)
if err != mapquestZeroResultsError {
t.Error(fmt.Sprintf("Expected error: %v, Got: %v"), mapquestZeroResultsError, err)
}
Expand Down
18 changes: 8 additions & 10 deletions opencage_geocoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ type OpenCageGeocoder struct{}
type opencageGeocodeResponse struct {
Results []struct {
Formatted string `json:"formatted"`
Geometry struct {
Geometry struct {
Lat float64
Lng float64
}
}
}

// This is the error that consumers receive when there
// are no results from the geocoding request.
var opencageZeroResultsError = errors.New("ZERO_RESULTS")
Expand Down Expand Up @@ -72,33 +73,30 @@ func (g *OpenCageGeocoder) Geocode(query string) (*Point, error) {
return nil, err
}

lat, lng, extractErr := g.extractLatLngFromResponse(data)
point, extractErr := g.extractLatLngFromResponse(data)
if extractErr != nil {
return nil, extractErr
}

p := &Point{lat: lat, lng: lng}

return p, nil
return &point, nil
}

// Extracts the first lat and lng values from a OpenCage response body.
func (g *OpenCageGeocoder) extractLatLngFromResponse(data []byte) (float64, float64, error) {
// Extracts the first location from a OpenCage response body.
func (g *OpenCageGeocoder) extractLatLngFromResponse(data []byte) (Point, error) {
res := &opencageGeocodeResponse{}
json.Unmarshal(data, &res)

// fmt.Printf("%s\n", data)
// fmt.Printf("%v\n", res)


if len(res.Results) == 0 {
return 0, 0, opencageZeroResultsError
return Point{}, opencageZeroResultsError
}

lat := res.Results[0].Geometry.Lat
lng := res.Results[0].Geometry.Lng

return lat, lng, nil
return Point{lat, lng}, nil
}

// Returns the first most available address that corresponds to the passed in point.
Expand Down
8 changes: 4 additions & 4 deletions opencage_geocoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ func TestOpenCageExtractLatLngFromRequest(t *testing.T) {
t.Error("%v\n", err)
}

lat, lng, err := g.extractLatLngFromResponse(data)
point, err := g.extractLatLngFromResponse(data)
if err != nil {
t.Error("%v\n", err)
}

if lat != -23.5373732 || lng != -46.8374628 {
t.Error(fmt.Sprintf("Expected: [-23.5373732, -46.8374628], Got: [%f, %f]", lat, lng))
if point.lat != -23.5373732 || point.lng != -46.8374628 {
t.Error(fmt.Sprintf("Expected: [-23.5373732, -46.8374628], Got: [%f, %f]", point.lat, point.lng))
}
}

Expand Down Expand Up @@ -48,7 +48,7 @@ func TestOpenCageExtractLatLngFromRequestZeroResults(t *testing.T) {
t.Error("%v\n", err)
}

_, _, err = g.extractLatLngFromResponse(data)
_, err = g.extractLatLngFromResponse(data)
if err != opencageZeroResultsError {
t.Error(fmt.Sprintf("Expected error: %v, Got: %v"), opencageZeroResultsError, err)
}
Expand Down
2 changes: 1 addition & 1 deletion point.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func (p *Point) BearingTo(p2 *Point) float64 {
// Renders the current Point to valid JSON.
// Implements the json.Marshaller Interface.
func (p *Point) MarshalJSON() ([]byte, error) {
res := fmt.Sprintf(`{"lat":%v, "lng":%v}`, p.Lat(), p.Lng())
res := fmt.Sprintf(`{"lat":%v, "lng":%v}`, p.lat, p.lng)
return []byte(res), nil
}

Expand Down
2 changes: 1 addition & 1 deletion point_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func TestUnmarshalJSON(t *testing.T) {
t.Errorf("Should not encounter an error when attempting to Unmarshal a Point from JSON")
}

if p.Lat() != 40.7486 || p.Lng() != -73.9864 {
if p.lat != 40.7486 || p.lng != -73.9864 {
t.Errorf("Point has mismatched data after Unmarshalling from JSON")
}
}
24 changes: 12 additions & 12 deletions polygon.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (p *Polygon) Contains(point *Point) bool {
func (p *Polygon) intersectsWithRaycast(point *Point, start *Point, end *Point) bool {
// Always ensure that the the first point
// has a y coordinate that is less than the second point
if start.Lng() > end.Lng() {
if start.lng > end.lng {

// Switch the points if otherwise.
start, end = end, start
Expand All @@ -78,35 +78,35 @@ func (p *Polygon) intersectsWithRaycast(point *Point, start *Point, end *Point)
// Move the point's y coordinate
// outside of the bounds of the testing region
// so we can start drawing a ray
for point.Lng() == start.Lng() || point.Lng() == end.Lng() {
newLng := math.Nextafter(point.Lng(), math.Inf(1))
point = NewPoint(point.Lat(), newLng)
for point.lng == start.lng || point.lng == end.lng {
newLng := math.Nextafter(point.lng, math.Inf(1))
point = NewPoint(point.lat, newLng)
}

// If we are outside of the polygon, indicate so.
if point.Lng() < start.Lng() || point.Lng() > end.Lng() {
if point.lng < start.lng || point.lng > end.lng {
return false
}

if start.Lat() > end.Lat() {
if point.Lat() > start.Lat() {
if start.lat > end.lat {
if point.lat > start.lat {
return false
}
if point.Lat() < end.Lat() {
if point.lat < end.lat {
return true
}

} else {
if point.Lat() > end.Lat() {
if point.lat > end.lat {
return false
}
if point.Lat() < start.Lat() {
if point.lat < start.lat {
return true
}
}

raySlope := (point.Lng() - start.Lng()) / (point.Lat() - start.Lat())
diagSlope := (end.Lng() - start.Lng()) / (end.Lat() - start.Lat())
raySlope := (point.lng - start.lng) / (point.lat - start.lat)
diagSlope := (end.lng - start.lng) / (end.lat - start.lat)

return raySlope >= diagSlope
}