diff --git a/directions.go b/directions.go index 71d9916..5c4de26 100644 --- a/directions.go +++ b/directions.go @@ -8,30 +8,33 @@ import ( ) const ( - AvoidTolls = "tolls" + // AvoidTolls indicates that the route should avoid toll roads. + AvoidTolls = "tolls" + // AvoidHighways indicates that the route should avoid highways. AvoidHighways = "highways" - AvoidFerries = "ferries" + // AvoidFerries indicates that the route should avoid ferries. + AvoidFerries = "ferries" - ModeDriving = "driving" - ModeWalking = "walking" - ModeTransit = "transit" + // ModeDriving indicates that driving directions are requested. + ModeDriving = "driving" + // ModeWalking indicates that walking directions are requested. + ModeWalking = "walking" + // ModeTransit indicates that transit directions are requested. + ModeTransit = "transit" + // ModeBicycling indicates that bicycling directions are requested. ModeBicycling = "bicycling" - UnitMetric = "metric" + // UnitMetric indicates that the results should be stated in metric units. + UnitMetric = "metric" + // UnitImperial indicates that the results should be stated in imperial units. UnitImperial = "imperial" - - StatusOK = "OK" - StatusNotFound = "NOT_FOUND" - StatusZeroResults = "ZERO_RESULTS" - StatusMaxWaypointsExceeded = "MAX_WAYPOINTS_EXCEEDED" - StatusInvalidRequest = "INVALID_REQUEST" - StatusRequestDenied = "REQUEST_DENIED" - StatusUnknownError = "UNKNOWN_ERROR" - StatusOverQueryLimit = "OVER_QUERY_LIMIT" ) // TODO: via_waypoint not documented +// Directions requests routes between orig and dest Locations. +// +// See https://developers.google.com/maps/documentation/directions/ func (c Client) Directions(orig, dest Location, opts *DirectionsOpts) ([]Route, error) { var d directionsResponse if err := c.doDecode(baseURL+directions(orig, dest, opts), &d); err != nil { @@ -51,12 +54,61 @@ func directions(orig, dest Location, opts *DirectionsOpts) string { return "directions/json?" + p.Encode() } +// DirectionsOpts defines options for Directions requests type DirectionsOpts struct { - Waypoints []Location - Alternatives bool - Avoid []string - Mode, Language, Units, Region string - DepartureTime, ArrivalTime time.Time + // Waypoints alter a route by routing it through the specified location(s). + // + // Waypoints are only supported for driving, walking and bicycling directions. + // + // See https://developers.google.com/maps/documentation/directions/#Waypoints + Waypoints []Location + + // If true and if waypoints are specified, the Directions API may optimize the provided route by rearranging the waypoints in a more efficient order. + OptimizeWaypoints bool + + // If true, multiple Routes may be returned. This may increase the response time from the server. + Alternatives bool + + // Indicates that the calculated route(s) should avoid the indicated features. + // + // Accepted values are AvoidTolls, AvoidHighways and AvoidFerries + // + // See https://developers.google.com/maps/documentation/directions/#Restrictions + Avoid []string + + // Specifies the mode of transport to use when calculating directions. + // + // Accepted values are ModeDriving (the default), ModeWalking, ModeTransit and ModeBicycling. + // If ModeTransit is specified, either DepartureTime or ArrivalTime must also be specified. + // + // See https://developers.google.com/maps/documentation/directions/#TravelModes + Mode string + + // The language in which to return results. + // + // See https://developers.google.com/maps/faq#languagesupport + Language string + + // Specifies the unit system to use when displaying results. + // + // Accepted values are UnitMetric and UnitImperial. + // + // See https://developers.google.com/maps/documentation/directions/#UnitSystems + Units string + + // The region code, specified as a ccTLD ("top-level domain") two-character value. + // + // See https://developers.google.com/maps/documentation/directions/#RegionBiasing + Region string + + // Specifies the desired time of departure. + // + // The departure time can be specified for transit directions, or for Google Maps API for Work + // clients to receive trip duration considering current traffic conditions. + DepartureTime time.Time + + // Specifies the desired time of arrival for transit directions. + ArrivalTime time.Time } func (do *DirectionsOpts) update(p url.Values) { @@ -67,7 +119,11 @@ func (do *DirectionsOpts) update(p url.Values) { p.Set("mode", do.Mode) } if do.Waypoints != nil { - p.Set("waypoints", encodeLocations(do.Waypoints)) + v := "" + if do.OptimizeWaypoints { + v := "optimize:true|" + } + p.Set("waypoints", v+encodeLocations(do.Waypoints)) } if do.Alternatives { p.Set("alternatives", "true") @@ -98,33 +154,91 @@ type directionsResponse struct { Routes []Route `json:"routes"` } +// Route describes a possible route between the requested origin and destination. type Route struct { + // Summary contains a short textual description of the route, suitable for naming and disambiguating the route from alternatives. Summary string `json:"summary"` - Legs []struct { - Duration *Duration `json:"duration"` + + // Legs contains information about the legs of a route, between two locations within the route. + // A separate leg will be present for each waypoint or destination specified. If no waypoints were + // requested, this will contain one element. + // + // See https://developers.google.com/maps/documentation/directions/#Legs + Legs []struct { + // Duration indicates the total duration of this leg. + Duration *Duration `json:"duration"` + + // DurationInTraffic indicates the total duration of this leg, taking into account current traffic conditions. + // + // It will only be included if all of the following are true: + // - The directions request includes a DepartureTime parameter set to a value within a few minutes of the current time. + // - The request is made using a Google Maps API for Work client. + // - Traffic conditions are available for the requested route. + // - The directions request doesnot include stopover waypoints. DurationInTraffic *Duration `json:"duration_in_traffic"` - Distance *Distance `json:"distance"` - ArrivalTime Time `json:"arrival_time"` - DepartureTime Time `json:"departure_time"` - StartLocation *LatLng `json:"start_location"` - EndLocation *LatLng `json:"end_location"` - StartAddress string `json:"start_address"` - EndAddress string `json:"end_address"` - Steps []Step `json:"steps"` + + // Distance is the total distance covered by this leg. + Distance *Distance `json:"distance"` + + // ArrivalTime indicates the estimated time of arrival for this leg. This is only included for transit directions. + ArrivalTime Time `json:"arrival_time"` + + // DepartureTime indicates the estimated time of arrival for this leg. This is only included for transit directions. + DepartureTime Time `json:"departure_time"` + + // StartLocation indicates the origin of this leg. + // + // Because the Directions API calculates directions between locations by using the nearest transportation option (usually a road) + // at the start and end points, StartLocation may be different than the provided origin of this leg if, for example, a road is not near the origin. + StartLocation *LatLng `json:"start_location"` + + // EndLocation indicates the destination of this leg. + // + // Because the Directions API calculates directions between locations by using the nearest transportation option (usually a road) + // at the start and end points, EndLocation may be different than the provided destination of this leg if, for example, a road is not near the destination. + EndLocation *LatLng `json:"end_location"` + + // StartAddress contains the human-readable address (typically a street address) reflecting the StartLocation of this leg. + StartAddress string `json:"start_address"` + + // EndAddress contains the human-readable address (typically a street address) reflecting the EndLocation of this leg. + EndAddress string `json:"end_address"` + + // Steps describes each step of the leg of the journey. + // + // See https://developers.google.com/maps/documentation/directions/#Steps + Steps []Step `json:"steps"` } `json:"legs"` - Bounds Bounds `json:"bounds"` - Copyrights string `json:"copyrights"` + + // Bounds describes the viewport bounding box of the OverviewPolyline + Bounds Bounds `json:"bounds"` + + // Copyrights contains the copyright text to be displayed for this route. You must display this information yourself. + Copyrights string `json:"copyrights"` + + // OverviewPolyline contains the encoded polyline representation of the route. This polyline is an approximate (smoothed) path of the resulting directions. OverviewPolyline Polyline `json:"overview_polyline"` - Warnings []string `json:"warnings"` - WaypointOrder []int `json:"waypoint_order"` + + // Warnings contains any warnings to display to the user when showing these directions. You must display these warnings yourself. + Warnings []string `json:"warnings"` + + // WaypointOrder indicates the order of any waypoints in the calculated route. The waypoints may be reordered if OptimizeWaypoints was specified. + WaypointOrder []int `json:"waypoint_order"` } +// Time represents a time. type Time struct { - Value int64 `json:"value"` - Text string `json:"text"` + // Value indicates the number of seconds since epoch of this time. + Value int64 `json:"value"` + + // Text contains a human-readable time, displayed in the time zone of the transit stop. + Text string `json:"text"` + + // TimeZone indicates the time zone of this station, e.g., "America/New_York" TimeZone string `json:"time_zone"` } +// Time returns the Time as a time.Time, in the specified TimeZone. func (t Time) Time() (*time.Time, error) { l, err := time.LoadLocation(t.TimeZone) if err != nil { @@ -134,52 +248,130 @@ func (t Time) Time() (*time.Time, error) { return &tm, nil } +// Step describes a step of a leg of a journey. +// +// See https://developers.google.com/maps/documentation/directions/#Steps type Step struct { - TravelMode string `json:"travel_mode"` // TODO: enum - StartLocation *LatLng `json:"start_location"` - EndLocation *LatLng `json:"end_location"` - Maneuver string `json:"maneuver"` // TODO: not documented? - Polyline *Polyline `json:"polyline"` - Duration *Duration `json:"duration"` - Distance *Distance `json:"distance"` - HTMLInstructions string `json:"html_instructions"` - Steps []Step `json:"steps"` // sub-steps - TransitDetails *struct { - ArrivalStop Stop `json:"arrival_stop"` - DepartureStop Stop `json:"departure_stop"` - ArrivalTime Time `json:"arrival_time"` - DepartureTime Time `json:"departure_time"` - Headsign string `json:"headsign"` - Headway int64 `json:"headway"` // Seconds until next departure, TODO: to time.Duration? - NumStops int `json:"num_stops"` - Line struct { - Name string `json:"name"` + TravelMode string `json:"travel_mode"` // TODO: not documented? (all caps) + + // StartLocation contains the location of the starting point of this step. + StartLocation *LatLng `json:"start_location"` + + // EndLocation contains the location of the ending point of this step. + EndLocation *LatLng `json:"end_location"` + + Maneuver string `json:"maneuver"` // TODO: not documented? + + // Contains an encoded polyline representation of the step. This is an approximate (smoothed) path of the step. + Polyline *Polyline `json:"polyline"` + + // Duration indicates the total duration of this step. + Duration *Duration `json:"duration"` + + // Distance is the total distance covered by this step. + Distance *Distance `json:"distance"` + + // HTMLInstructions contains formatted instructions for this step, presented as an HTML text string. + HTMLInstructions string `json:"html_instructions"` + + // Steps contains detailed directions for walking or driving steps in transit directions. These are only available when the requested Mode is ModeTransit + Steps []Step `json:"steps"` + + // TransitDetails contains transit-specific information. This is only included when the requested Mode is ModeTransit + // + // See https://developers.google.com/maps/documentation/directions/#TransitDetails + TransitDetails *struct { + // ArrivalStop contains information about the arrival stop for this part of the trip. + ArrivalStop Stop `json:"arrival_stop"` + + // DepartureStop contains information about the departure stop for this part of the trip. + DepartureStop Stop `json:"departure_stop"` + + // ArrivalTime indicates the arrival time for this leg of the journey. + ArrivalTime Time `json:"arrival_time"` + + // DepartureTime indicates the departure time for this leg of the journey. + DepartureTime Time `json:"departure_time"` + + // Headsign specifies the direction in which to travel on this line, as it is marked on the vehicle or at the departure stop. This will often be the terminus station. + Headsign string `json:"headsign"` + + // Headway specifies the expected number of seconds between departure from the same stop at this time. + // + // For example, with a Headway of 600, you would expect a ten minute way if you should miss your bus. + Headway int64 `json:"headway"` // TODO: to time.Duration? + + // NumStops indicates the number of stops in this step, counting the arrival stop, but not the departure stop. + // + // For example, if your directions involve leaving Stop A, passing through Stops B and C, and arriving at Stop D, NumStops will be 3. + NumStops int `json:"num_stops"` + + // Line contains information about the transit line used in this step. + Line struct { + // Name is the full name of this transit line, e.g., "7 Avenue Express" + Name string `json:"name"` + + // ShortName is the short name of this transit line. This will normally be a line number, such as "M7" or "355" ShortName string `json:"short_name"` - Color string `json:"color"` // hex color, TODO: to image.Color? - Agencies []struct { - Name string `json:"name"` - URL string `json:"url"` + + // Color is the color commonly used in signage for this transit line, specified as a hex string such as "#FF0033" + Color string `json:"color"` // TODO: to image.Color? + + // Agencies contains information about the operator of the line. + // + // You must display the names and URLs of the transit agencies servicing the trip results. + Agencies []struct { + // Name is the name of the transit agency. + Name string `json:"name"` + + // URL is the URL for the transit agency. + URL string `json:"url"` + + // Phone is the phone number of the transit agency. Phone string `json:"phone"` } `json:"agencies"` - URL string `json:"url"` - IconURL string `json:"icon"` - TextColor string `json:"text_color"` // hex color, TODO: to image.Color? - Vehicle *struct { - Name string `json:"name"` - Type string `json:"type"` // TODO: enum + + // URL is the URL for this transit line as provided by the transit agency. + URL string `json:"url"` + + // IconURL is the URL for the icon associated with this line. + IconURL string `json:"icon"` + + // TextColor is the color of text commonly used for signage of this line, specified as a hex string such as "#FF0033" + TextColor string `json:"text_color"` // TODO: to image.Color? + + // Vehicle contains the type of vehicle used on this line. + Vehicle *struct { + // Name is the name of the vehicle used on this line, e.g., "Subway" + Name string `json:"name"` + + // Type is the type of the vehicle that runs on this line. + // + // See https://developers.google.com/maps/documentation/directions/#VehicleType + Type string `json:"type"` + + // IconURL is the URL for an icon associated with this vehicle type. IconURL string `json:"icon"` } `json:"vehicle"` } `json:"line"` } `json:"transit_details"` } +// Stop describes a transit station or stop. type Stop struct { + // Location is the location of the station/stop. Location LatLng `json:"location"` - Name string `json:"name"` + + // Name is the name of the transit station/stop, e.g., "Union Square". + Name string `json:"name"` } +// Bounds describes a viewport bounding box. type Bounds struct { + // The northeasternmost point of the bounding box. Northeast LatLng `json:"northeast"` + + // The southwesternmost point of the bounding box. Southwest LatLng `json:"southwest"` } @@ -187,21 +379,34 @@ func (b Bounds) String() string { return fmt.Sprintf("%s|%s", b.Northeast, b.Southwest) } +// Duration describes an amount of time for a leg or step. type Duration struct { - Value int64 `json:"value"` - Text string `json:"text"` + // Value indicates the duration in seconds. + Value int64 `json:"value"` + + // Text contains a human-readable representation of the duration. + Text string `json:"text"` } +// Duration translates the Duration into a time.Duration. func (d Duration) Duration() time.Duration { return time.Duration(d.Value) * time.Second } +// Distance describes a distance for a leg or step. type Distance struct { - Value int64 `json:"value"` // meters - Text string `json:"text"` + // Value is the distance in meters + Value int64 `json:"value"` + + // Text contains a human-readable representation of the distance, displayed in the requested unit and language, or in the unit and language used at the origin. + Text string `json:"text"` } +// Polyline contains data describing an encoded polyline. type Polyline struct { + // Points is an encoded polyline describing some path. + // + // See https://developers.google.com/maps/documentation/utilities/polylinealgorithm Points string `json:"points"` } diff --git a/maps.go b/maps.go index 4403233..3960b7f 100644 --- a/maps.go +++ b/maps.go @@ -9,6 +9,27 @@ import ( const baseURL = "https://maps.googleapis.com/maps/api/" +const ( + // StatusOK indicates the response contains a valid result. + StatusOK = "OK" + // StatusNotFound indicates at least one of the locations specified could not be geocoded. + StatusNotFound = "NOT_FOUND" + // StatusZeroResults indicates no route could be found between the origin and destination. + StatusZeroResults = "ZERO_RESULTS" + // StatusMaxWaypointsExceeded indicates that too many Waypoints were provided in the request. + // + // The maximum allowed waypoints is 8, plus the origin and destination. Google Maps API for Work clients may contain requests with up to 23 waypoints. + StatusMaxWaypointsExceeded = "MAX_WAYPOINTS_EXCEEDED" + // StatusInvalidRequest indicates that the provided request was invalid. + StatusInvalidRequest = "INVALID_REQUEST" + // StatusRequestDenied indicates that the service denied use of the service by your application. + StatusRequestDenied = "REQUEST_DENIED" + // StatusUnknownError indicates that the request could not be processed due to a server error. The request may succeed if you try again. + StatusUnknownError = "UNKNOWN_ERROR" + // StatusOverQueryLimit indicates that the service has received too many requests from your application within the allowed time period. + StatusOverQueryLimit = "OVER_QUERY_LIMIT" +) + type Client struct { Transport http.RoundTripper Key, ClientID, Signature string @@ -59,7 +80,7 @@ func (c Client) doDecode(url string, r interface{}) error { return nil } -// HTTPError indicates an error communicating with the API, and includes the HTTP response returned from the server. +// HTTPError indicates an error communicating with the API server, and includes the HTTP response returned from the server. type HTTPError struct { Response *http.Response }