mirror of
https://github.com/imjasonh/staticmaps
synced 2026-07-08 10:55:03 +00:00
208 lines
5.6 KiB
Go
208 lines
5.6 KiB
Go
package maps
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
AvoidTolls = "tolls"
|
|
AvoidHighways = "highways"
|
|
AvoidFerries = "ferries"
|
|
|
|
ModeDriving = "driving"
|
|
ModeWalking = "walking"
|
|
ModeTransit = "transit"
|
|
ModeBicycling = "bicycling"
|
|
|
|
UnitMetric = "metric"
|
|
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
|
|
|
|
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 {
|
|
return nil, err
|
|
}
|
|
if d.Status != StatusOK {
|
|
return nil, APIError{d.Status, d.ErrorMessage}
|
|
}
|
|
return d.Routes, nil
|
|
}
|
|
|
|
func directions(orig, dest Location, opts *DirectionsOpts) string {
|
|
p := url.Values{}
|
|
p.Set("origin", orig.Location())
|
|
p.Set("destination", dest.Location())
|
|
opts.update(p)
|
|
return "directions/json?" + p.Encode()
|
|
}
|
|
|
|
type DirectionsOpts struct {
|
|
Waypoints []Location
|
|
Alternatives bool
|
|
Avoid []string
|
|
Mode, Language, Units, Region string
|
|
DepartureTime, ArrivalTime time.Time
|
|
}
|
|
|
|
func (do *DirectionsOpts) update(p url.Values) {
|
|
if do == nil {
|
|
return
|
|
}
|
|
if do.Mode != "" {
|
|
p.Set("mode", do.Mode)
|
|
}
|
|
if do.Waypoints != nil {
|
|
p.Set("waypoints", encodeLocations(do.Waypoints))
|
|
}
|
|
if do.Alternatives {
|
|
p.Set("alternatives", "true")
|
|
}
|
|
if do.Avoid != nil {
|
|
p.Set("avoid", strings.Join(do.Avoid, "|"))
|
|
}
|
|
if do.Language != "" {
|
|
p.Set("language", do.Language)
|
|
}
|
|
if do.Units != "" {
|
|
p.Set("units", do.Units)
|
|
}
|
|
if do.Region != "" {
|
|
p.Set("region", do.Region)
|
|
}
|
|
if !do.DepartureTime.IsZero() {
|
|
p.Set("departure_time", fmt.Sprintf("%d", do.DepartureTime.Unix()))
|
|
}
|
|
if !do.ArrivalTime.IsZero() {
|
|
p.Set("arrival_time", fmt.Sprintf("%d", do.ArrivalTime.Unix()))
|
|
}
|
|
}
|
|
|
|
type directionsResponse struct {
|
|
Status string `json:"status"`
|
|
ErrorMessage string `json:"error_message"`
|
|
Routes []Route `json:"routes"`
|
|
}
|
|
|
|
type Route struct {
|
|
Summary string `json:"summary"`
|
|
Legs []struct {
|
|
Duration *Duration `json:"duration"`
|
|
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"`
|
|
} `json:"legs"`
|
|
Bounds Bounds `json:"bounds"`
|
|
Copyrights string `json:"copyrights"`
|
|
OverviewPolyline Polyline `json:"overview_polyline"`
|
|
Warnings []string `json:"warnings"`
|
|
WaypointOrder []int `json:"waypoint_order"`
|
|
}
|
|
|
|
type Time struct {
|
|
Value int64 `json:"value"`
|
|
Text string `json:"text"`
|
|
TimeZone string `json:"time_zone"`
|
|
}
|
|
|
|
func (t Time) Time() (*time.Time, error) {
|
|
l, err := time.LoadLocation(t.TimeZone)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tm := time.Unix(t.Value, 0).In(l)
|
|
return &tm, nil
|
|
}
|
|
|
|
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"`
|
|
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"`
|
|
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
|
|
IconURL string `json:"icon"`
|
|
} `json:"vehicle"`
|
|
} `json:"line"`
|
|
} `json:"transit_details"`
|
|
}
|
|
|
|
type Stop struct {
|
|
Location LatLng `json:"location"`
|
|
Name string `json:"name"`
|
|
}
|
|
|
|
type Bounds struct {
|
|
Northeast LatLng `json:"northeast"`
|
|
Southwest LatLng `json:"southwest"`
|
|
}
|
|
|
|
func (b Bounds) String() string {
|
|
return fmt.Sprintf("%s|%s", b.Northeast, b.Southwest)
|
|
}
|
|
|
|
type Duration struct {
|
|
Value int64 `json:"value"`
|
|
Text string `json:"text"`
|
|
}
|
|
|
|
func (d Duration) Duration() time.Duration {
|
|
return time.Duration(d.Value) * time.Second
|
|
}
|
|
|
|
type Distance struct {
|
|
Value int64 `json:"value"` // meters
|
|
Text string `json:"text"`
|
|
}
|
|
|
|
type Polyline struct {
|
|
Points string `json:"points"`
|
|
}
|
|
|
|
// TODO: methods to decode polyline points?
|