Skip to content

Commit

Permalink
feat!: Address descriptors (#295)
Browse files Browse the repository at this point in the history
* feat! Added Address Descriptors to Geocoding response. Refactored Geocoding response to allow fields outside the geocoding result to be exposed through the client.

* feat! Added Address Descriptors to Geocoding response. Refactored Geocoding response to allow fields outside the geocoding result to be exposed through the client.

* feat! Added Address Descriptors to Geocoding response. Refactored Geocoding response to allow fields outside the geocoding result to be exposed through the client.

* feat! Added Address Descriptors to Geocoding response. Refactored Geocoding response to allow fields outside the geocoding result to be exposed through the client.

* feat! Added Address Descriptors to Geocoding response. Refactored Geocoding response to allow fields outside the geocoding result to be exposed through the client.

* Update README.md

---------

Co-authored-by: Tom Clifford <[email protected]>
  • Loading branch information
tcliff111 and Tom Clifford authored Jul 16, 2024
1 parent 51148b6 commit eb7d4f9
Show file tree
Hide file tree
Showing 5 changed files with 539 additions and 180 deletions.
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,37 @@ func main() {
}
```

Sample usage of the Geocoding API with an API key to get an [Address Descriptor](https://developers.google.com/maps/documentation/geocoding/address-descriptors/requests-address-descriptors):

```go
package main

import (
"context"
"log"

"github.com/kr/pretty"
"googlemaps.github.io/maps"
)

func main() {
c, err := maps.NewClient(maps.WithAPIKey("Insert-API-Key-Here"))
if err != nil {
log.Fatalf("fatal error: %s", err)
}
r := &maps.GeocodingRequest{
LatLng: &LatLng{Lat: 40.714224, Lng: -73.961452},
EnableAddressDescriptor: True
}
reverseGeocodingResponse, _, err := c.ReverseGeocode(context.Background(), r)
if err != nil {
log.Fatalf("fatal error: %s", err)
}

pretty.Println(reverseGeocodingResponse)
}
```

## Features

### Rate limiting
Expand Down
148 changes: 148 additions & 0 deletions addressdescriptor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// Copyright 2024 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package maps

import (
"fmt"
)

/**
* An enum representing the relationship in space between the landmark and the target.
*/
type SpatialRelationship string

const (
// This is the default relationship when nothing more specific below
// applies.
SPATIAL_RELATIONSHIP_NEAR SpatialRelationship = "NEAR"
// The landmark has a spatial geometry and the target is within its
// bounds.
SPATIAL_RELATIONSHIP_WITHIN SpatialRelationship = "WITHIN"
// The target is directly adjacent to the landmark or landmark's access
// point.
SPATIAL_RELATIONSHIP_BESIDE SpatialRelationship = "BESIDE"
// The target is directly opposite the landmark on the other side of the
// road.
SPATIAL_RELATIONSHIP_ACROSS_THE_ROAD SpatialRelationship = "ACROSS_THE_ROAD"
// On the same route as the landmark but not besides or across.
SPATIAL_RELATIONSHIP_DOWN_THE_ROAD SpatialRelationship = "DOWN_THE_ROAD"
// Not on the same route as the landmark but a single 'turn' away.
SPATIAL_RELATIONSHIP_AROUND_THE_CORNER SpatialRelationship = "AROUND_THE_CORNER"
// Close to the landmark's structure but further away from its access
// point.
SPATIAL_RELATIONSHIP_BEHIND SpatialRelationship = "BEHIND"
)

// String method for formatted output
func (sr SpatialRelationship) String() string {
return string(sr)
}

/**
* An enum representing the relationship in space between the area and the target.
*/
type Containment string

const (
/**
* Indicates an unknown containment returned by the server.
*/
CONTAINMENT_UNSPECIFIED Containment = "CONTAINMENT_UNSPECIFIED"
/** The target location is within the area region, close to the center. */
CONTAINMENT_WITHIN Containment = "WITHIN"
/** The target location is within the area region, close to the edge. */
CONTAINMENT_OUTSKIRTS Containment = "OUTSKIRTS"
/** The target location is outside the area region, but close by. */
CONTAINMENT_NEAR Containment = "NEAR"
)

// String method for formatted output
func (c Containment) String() string {
return string(c)
}

/**
* Localized variant of a text in a particular language.
*/
type LocalizedText struct {
// Localized string in the language corresponding to language_code below.
Text string `json:"text"`
// The text's BCP-47 language code, such as "en-US" or "sr-Latn".
//
// For more information, see
// http://www.unicode.org/reports/tr35/#Unicode_locale_identifier.
LanguageCode string `json:"language_code"`
}

// String method for formatted output
func (lt LocalizedText) String() string {
return fmt.Sprintf("(text=%s, languageCode=%s)", lt.Text, lt.LanguageCode)
}

// Landmarks that are useful at describing a location.
type Landmark struct {
// The Place ID of the underlying establishment serving as the landmark.
// Can be used to resolve more information about the landmark through Place
// Details or Place Id Lookup.
PlaceID string `json:"place_id"`
// The best name for the landmark.
DisplayName LocalizedText `json:"display_name"`
// One or more values indicating the type of the returned result. Please see <a
// href="https://developers.google.com/maps/documentation/places/web-service/supported_types">Types
// </a> for more detail.
Types []string `json:"types"`
// Defines the spatial relationship between the target location and the
// landmark.
SpatialRelationship SpatialRelationship `json:"spatial_relationship"`
// The straight line distance between the target location and one of the
// landmark's access points.
StraightLineDistanceMeters float32 `json:"straight_line_distance_meters"`
// The travel distance along the road network between the target
// location's closest point on a road, and the landmark's closest access
// point on a road. This can be unpopulated if the landmark is disconnected
// from the part of the road network the target is closest to OR if the
// target location was not actually considered to be on the road network.
TravelDistanceMeters float32 `json:"travel_distance_meters"`
}

// Precise regions that are useful at describing a location.
type Area struct {
// The Place ID of the underlying area feature. Can be used to
// resolve more information about the area through Place Details or
// Place Id Lookup.
PlaceID string `json:"place_id"`
// The best name for the area.
DisplayName LocalizedText `json:"display_name"`
/**
* An enum representing the relationship in space between the area and the target.
*/
Containment Containment `json:"containment"`
}

/**
* Represents a descriptor of an address.
*
* <p>Please see <a
* href="https://mapsplatform.google.com/demos/address-descriptors/">Address
* Descriptors</a> for more detail.
*/
type AddressDescriptor struct {
// A ranked list of nearby landmarks. The most useful (recognizable and
// nearby) landmarks are ranked first.
Landmarks []Landmark `json:"landmarks"`
// A ranked list of containing or adjacent areas. The most useful
// (recognizable and precise) areas are ranked first.
Areas []Area `json:"areas"`
}
10 changes: 10 additions & 0 deletions examples/geocoding/cmdline/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ var (
latlng = flag.String("latlng", "", "The textual latitude/longitude value for which you wish to obtain the closest, human-readable address.")
resultType = flag.String("result_type", "", "One or more address types, separated by a pipe (|).")
locationType = flag.String("location_type", "", "One or more location types, separated by a pipe (|).")
enableAddressDescriptor = flag.String("enable_address_descriptor", "", "True or False. Whether to return the Address Descriptors in the response.")
)

func usageAndExit(msg string) {
Expand Down Expand Up @@ -81,6 +82,7 @@ func main() {
parseLatLng(*latlng, r)
parseResultType(*resultType, r)
parseLocationType(*locationType, r)
parseEnableAddressDescriptor(*enableAddressDescriptor, r)

resp, err := client.Geocode(context.Background(), r)
check(err)
Expand Down Expand Up @@ -187,3 +189,11 @@ func parseLocationType(locationType string, r *maps.GeocodingRequest) {

}
}

func parseEnableAddressDescriptor(enableAddressDescriptor string, r *maps.GeocodingRequest) {
if enableAddressDescriptor == "True" {
r.EnableAddressDescriptor = true
} else {
r.EnableAddressDescriptor = false
}
}
36 changes: 25 additions & 11 deletions geocoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ var geocodingAPI = &apiConfig{
}

// Geocode makes a Geocoding API request
func (c *Client) Geocode(ctx context.Context, r *GeocodingRequest) ([]GeocodingResult, error) {
func (c *Client) Geocode(ctx context.Context, r *GeocodingRequest) (GeocodingResponse, error) {
if r.Address == "" && len(r.Components) == 0 && r.LatLng == nil {
return nil, errors.New("maps: address, components and LatLng are all missing")
return GeocodingResponse{}, errors.New("maps: address, components and LatLng are all missing")
}

var response struct {
Expand All @@ -43,38 +43,38 @@ func (c *Client) Geocode(ctx context.Context, r *GeocodingRequest) ([]GeocodingR
}

if err := c.getJSON(ctx, geocodingAPI, r, &response); err != nil {
return nil, err
return GeocodingResponse{}, err
}

if err := response.StatusError(); err != nil {
return nil, err
return GeocodingResponse{}, err
}

return response.Results, nil
return GeocodingResponse{response.Results, AddressDescriptor{}}, nil
}

// ReverseGeocode makes a Reverse Geocoding API request
func (c *Client) ReverseGeocode(ctx context.Context, r *GeocodingRequest) ([]GeocodingResult, error) {
func (c *Client) ReverseGeocode(ctx context.Context, r *GeocodingRequest) (GeocodingResponse, error) {
// Since Geocode() does not allow a nil LatLng, whereas it is allowed here
if r.LatLng == nil && r.PlaceID == "" {
return nil, errors.New("maps: LatLng and PlaceID are both missing")
return GeocodingResponse{}, errors.New("maps: LatLng and PlaceID are both missing")
}

var response struct {
Results []GeocodingResult `json:"results"`
AddressDescriptor AddressDescriptor `json:"address_descriptor"`
commonResponse
}

if err := c.getJSON(ctx, geocodingAPI, r, &response); err != nil {
return nil, err
return GeocodingResponse{}, err
}

if err := response.StatusError(); err != nil {
return nil, err
return GeocodingResponse{}, err
}

return response.Results, nil

return GeocodingResponse{response.Results, response.AddressDescriptor}, nil
}

func (r *GeocodingRequest) params() url.Values {
Expand Down Expand Up @@ -119,6 +119,9 @@ func (r *GeocodingRequest) params() url.Values {
if r.Language != "" {
q.Set("language", r.Language)
}
if r.EnableAddressDescriptor == true {
q.Set("enable_address_descriptor", "true")
}

return q
}
Expand Down Expand Up @@ -176,12 +179,23 @@ type GeocodingRequest struct {
// Language is the language in which to return results. Optional.
Language string

// Language is the language in which to return results. Optional.
EnableAddressDescriptor bool

// Custom allows passing through custom parameters to the Geocoding back end.
// Use with caution. For more detail on why this is required, please see
// https://googlegeodevelopers.blogspot.com/2016/11/address-geocoding-in-google-maps-apis.html
Custom url.Values
}

// GeocodingResponse is the response to a Geocoding API request.
type GeocodingResponse struct {
// Results is the Geocoding results
Results []GeocodingResult
// The Address Descriptor for the target in the reverse geocoding requeest
AddressDescriptor AddressDescriptor
}

// GeocodingResult is a single geocoded address
type GeocodingResult struct {
AddressComponents []AddressComponent `json:"address_components"`
Expand Down
Loading

0 comments on commit eb7d4f9

Please sign in to comment.