mirror of
https://github.com/imjasonh/staticmaps
synced 2026-07-09 11:16:42 +00:00
initial commit
This commit is contained in:
parent
bec142c216
commit
e0af619690
4 changed files with 305 additions and 1 deletions
1
deleteme
1
deleteme
|
|
@ -1 +0,0 @@
|
|||
do eet
|
||||
197
directions.go
Normal file
197
directions.go
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
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"
|
||||
)
|
||||
|
||||
// TODO: via_waypoint not documented
|
||||
|
||||
func (c Client) Directions(orig, dest Location, opts *DirectionsOpts) (*DirectionsResponse, error) {
|
||||
var d DirectionsResponse
|
||||
if err := c.do(baseURL+directions(orig, dest, opts), &d); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &d, 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 {
|
||||
Mode string
|
||||
Waypoints Route
|
||||
Alternatives bool
|
||||
Avoid []string
|
||||
Language string
|
||||
Units string
|
||||
Region string
|
||||
DepartureTime time.Time
|
||||
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", do.Waypoints.String())
|
||||
}
|
||||
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 []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"`
|
||||
} `json:"routes"`
|
||||
}
|
||||
|
||||
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 string `json:"arrival_stop"`
|
||||
DepartureStop string `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 Bounds struct {
|
||||
Northeast LatLng `json:"northeast"`
|
||||
Southwest LatLng `json:"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?
|
||||
87
maps.go
Normal file
87
maps.go
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
package maps
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const baseURL = "https://maps.googleapis.com/maps/api/"
|
||||
|
||||
type Client struct {
|
||||
Transport http.RoundTripper
|
||||
Key string
|
||||
}
|
||||
|
||||
func NewClient(key string) Client {
|
||||
return Client{Key: key, Transport: http.DefaultTransport}
|
||||
}
|
||||
|
||||
func (c Client) do(url string, r interface{}) error {
|
||||
cl := &http.Client{Transport: c.Transport}
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.Key != "" {
|
||||
q := req.URL.Query()
|
||||
q.Set("key", c.Key)
|
||||
req.URL.RawQuery = q.Encode()
|
||||
}
|
||||
|
||||
resp, err := cl.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return HTTPError{resp}
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type HTTPError struct {
|
||||
Response *http.Response
|
||||
}
|
||||
|
||||
func (e HTTPError) Error() string {
|
||||
return fmt.Sprintf("http error %d", e.Response.StatusCode)
|
||||
}
|
||||
|
||||
type Location interface {
|
||||
Location() string
|
||||
}
|
||||
|
||||
type LatLng struct {
|
||||
Lat float64 `json:"lat"`
|
||||
Lng float64 `json:"lng"`
|
||||
}
|
||||
|
||||
func (ll LatLng) Location() string {
|
||||
return fmt.Sprintf("%f,%f", ll.Lat, ll.Lng)
|
||||
}
|
||||
|
||||
func (ll LatLng) String() string {
|
||||
return ll.Location()
|
||||
}
|
||||
|
||||
type Address string
|
||||
|
||||
func (a Address) Location() string {
|
||||
return string(a)
|
||||
}
|
||||
|
||||
type Route []Location
|
||||
|
||||
func (r Route) String() string {
|
||||
s := make([]string, len(r))
|
||||
for i, l := range r {
|
||||
s[i] = l.Location()
|
||||
}
|
||||
return strings.Join(s, "|")
|
||||
}
|
||||
21
maps_test.go
Normal file
21
maps_test.go
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package maps
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestDirections(t *testing.T) {
|
||||
c := NewClient("")
|
||||
orig, dest := Address("111 8th Ave, NYC"), Address("170 E 92nd St, NYC")
|
||||
opts := &DirectionsOpts{
|
||||
Mode: ModeWalking,
|
||||
DepartureTime: time.Now(),
|
||||
}
|
||||
t.Logf("%s", baseURL+directions(orig, dest, opts))
|
||||
r, err := c.Directions(orig, dest, nil)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
t.Logf("%v", r)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue