diff --git a/match.go b/match.go index 467f29b..59d881e 100644 --- a/match.go +++ b/match.go @@ -4,15 +4,13 @@ import geo "github.com/paulmach/go.geo" // MatchRequest represents a request to the match method type MatchRequest struct { + GeneralOptions Profile string Coordinates Geometry - Bearings []Bearing Steps Steps Annotations Annotations Tidy Tidy Timestamps []int64 - Radiuses []float64 - Hints []string Overview Overview Gaps Gaps Geometries Geometries @@ -41,15 +39,7 @@ func (r MatchRequest) request() *request { if len(r.Timestamps) > 0 { options.addInt64("timestamps", r.Timestamps...) } - if len(r.Radiuses) > 0 { - options.addFloat("radiuses", r.Radiuses...) - } - if len(r.Hints) > 0 { - options.add("hints", r.Hints...) - } - if len(r.Bearings) > 0 { - options.set("bearings", bearings(r.Bearings)) - } + options = r.GeneralOptions.options(options) return &request{ profile: r.Profile, diff --git a/match_test.go b/match_test.go index 379bd72..fc247a2 100644 --- a/match_test.go +++ b/match_test.go @@ -20,15 +20,19 @@ func TestEmptyMatchRequestOptions(t *testing.T) { name: "with timestamps and radiuses", request: MatchRequest{ Timestamps: []int64{0, 1, 2}, - Radiuses: []float64{0.123123, 0.12312}, + GeneralOptions: GeneralOptions{ + Radiuses: []float64{0.123123, 0.12312}, + }, }, expectedURI: "geometries=polyline6&radiuses=0.123123;0.12312×tamps=0;1;2", }, { name: "with gaps and tidy", request: MatchRequest{ + GeneralOptions: GeneralOptions{ + Radiuses: []float64{0.123123, 0.12312}, + }, Timestamps: []int64{0, 1, 2}, - Radiuses: []float64{0.123123, 0.12312}, Gaps: GapsSplit, Tidy: TidyTrue, }, @@ -37,15 +41,19 @@ func TestEmptyMatchRequestOptions(t *testing.T) { { name: "with hints", request: MatchRequest{ - Hints: []string{"a", "b", "c", "d"}, + GeneralOptions: GeneralOptions{ + Hints: []string{"a", "b", "c", "d"}, + }, }, expectedURI: "geometries=polyline6&hints=a;b;c;d", }, { name: "with bearings", request: MatchRequest{ - Bearings: []Bearing{ - {0, 20}, {10, 20}, + GeneralOptions: GeneralOptions{ + Bearings: []Bearing{ + {0, 20}, {10, 20}, + }, }, }, expectedURI: "bearings=0%2C20%3B10%2C20&geometries=polyline6", diff --git a/nearest.go b/nearest.go index 06172ba..2d027e3 100644 --- a/nearest.go +++ b/nearest.go @@ -4,9 +4,9 @@ import geo "github.com/paulmach/go.geo" // NearestRequest represents a request to the nearest method type NearestRequest struct { + GeneralOptions Profile string Coordinates Geometry - Bearings []Bearing Number int } @@ -18,11 +18,8 @@ type NearestResponse struct { // NearestWaypoint represents a nearest point on a nearest query type NearestWaypoint struct { - Location geo.Point `json:"location"` - Distance float64 `json:"distance"` - Name string `json:"name"` - Hint string `json:"hint"` - Nodes []uint64 `json:"nodes"` + Waypoint + Nodes []uint64 `json:"nodes"` } func (r NearestRequest) request() *request { @@ -31,9 +28,7 @@ func (r NearestRequest) request() *request { opts.addInt("number", r.Number) } - if len(r.Bearings) > 0 { - opts.set("bearings", bearings(r.Bearings)) - } + opts = r.GeneralOptions.options(opts) return &request{ profile: r.Profile, @@ -42,3 +37,10 @@ func (r NearestRequest) request() *request { options: opts, } } + +type Waypoint struct { + Location geo.Point `json:"location"` + Distance float64 `json:"distance"` + Name string `json:"name"` + Hint string `json:"hint"` +} diff --git a/nearest_test.go b/nearest_test.go index b8c4005..0c271a4 100644 --- a/nearest_test.go +++ b/nearest_test.go @@ -9,8 +9,10 @@ import ( func TestNearestRequestOverviewOption(t *testing.T) { req := NearestRequest{ Number: 2, - Bearings: []Bearing{ - {60, 380}, + GeneralOptions: GeneralOptions{ + Bearings: []Bearing{ + {60, 380}, + }, }, } assert.Equal( @@ -19,8 +21,10 @@ func TestNearestRequestOverviewOption(t *testing.T) { req.request().options.encode()) req = NearestRequest{ - Bearings: []Bearing{ - {60, 380}, + GeneralOptions: GeneralOptions{ + Bearings: []Bearing{ + {60, 380}, + }, }, } assert.Equal( diff --git a/options.go b/options.go index 73726f4..c9ce34a 100644 --- a/options.go +++ b/options.go @@ -7,6 +7,38 @@ import ( "strconv" ) +type GeneralOptions struct { + Bearings []Bearing + Radiuses []float64 + GenerateHintsDisabled bool + Hints []string + Approaches []string + Exclude []string +} + +func (g GeneralOptions) options(opts options) options { + if len(g.Bearings) > 0 { + opts.add("bearings", bearings(g.Bearings)) + } + if len(g.Radiuses) > 0 { + opts.addFloat("radiuses", g.Radiuses...) + } + // generate_hints option default is true + if g.GenerateHintsDisabled { + opts.setBool("generate_hints", !g.GenerateHintsDisabled) + } + if len(g.Hints) > 0 { + opts.add("hints", g.Hints...) + } + if len(g.Approaches) > 0 { + opts.add("approaches", g.Approaches...) + } + if len(g.Exclude) > 0 { + opts.add("exclude", g.Exclude...) + } + return opts +} + // options represents OSRM query params to be encoded in URL type options map[string][]string diff --git a/options_test.go b/options_test.go index b92ef61..2952452 100644 --- a/options_test.go +++ b/options_test.go @@ -101,3 +101,54 @@ func TestOptionsAddFloatValsAsVariadic(t *testing.T) { opts.addFloat("foo", 1.1231312, 2.1233) assert.Equal(t, "foo=1.1231312;2.1233", opts.encode()) } + +func TestGeneralOptions(t *testing.T) { + cases := []struct { + name string + options GeneralOptions + expectedURI string + }{ + { + name: "empty", + options: GeneralOptions{}, + expectedURI: "", + }, + { + name: "with bearings", + options: GeneralOptions{ + Bearings: []Bearing{ + {0, 20}, {10, 20}, + }, + }, + expectedURI: "bearings=0%2C20%3B10%2C20", + }, + { + name: "with radiuses", + options: GeneralOptions{ + Radiuses: []float64{0.123123, 0.12312}, + }, + expectedURI: "radiuses=0.123123;0.12312", + }, + { + name: "generate hints disabled", + options: GeneralOptions{ + Radiuses: []float64{0.123123, 0.12312}, + GenerateHintsDisabled: true, + }, + expectedURI: "generate_hints=false&radiuses=0.123123;0.12312", + }, + { + name: "with approaches and exclude", + options: GeneralOptions{ + Exclude: []string{"toll", "highway"}, + Approaches: []string{"a", "b"}, + }, + expectedURI: "approaches=a;b&exclude=toll;highway", + }, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + assert.Equal(t, c.expectedURI, c.options.options(options{}).encode()) + }) + } +} diff --git a/route.go b/route.go index 5791030..2db1a41 100644 --- a/route.go +++ b/route.go @@ -8,9 +8,9 @@ import ( // RouteRequest represents a request to the route method type RouteRequest struct { + GeneralOptions Profile string Coordinates Geometry - Bearings []Bearing Steps Steps Annotations Annotations Overview Overview @@ -21,7 +21,8 @@ type RouteRequest struct { // RouteResponse represents a response from the route method type RouteResponse struct { ResponseStatus - Routes []Route `json:"routes"` + Routes []Route `json:"routes"` + Waypoints []Waypoint `json:"waypoints"` } // Route represents a route through (potentially multiple) points. @@ -69,9 +70,7 @@ func (r RouteRequest) request() *request { opts := stepsOptions(r.Steps, r.Annotations, r.Overview, r.Geometries). setStringer("continue_straight", r.ContinueStraight) - if len(r.Bearings) > 0 { - opts.set("bearings", bearings(r.Bearings)) - } + opts = r.GeneralOptions.options(opts) return &request{ profile: r.Profile, diff --git a/route_test.go b/route_test.go index 49a4d59..0d7a822 100644 --- a/route_test.go +++ b/route_test.go @@ -16,9 +16,11 @@ func TestEmptyRouteRequestOptions(t *testing.T) { func TestRouteRequestOptionsWithBearings(t *testing.T) { req := RouteRequest{ - Bearings: []Bearing{ - {60, 380}, - {45, 180}, + GeneralOptions: GeneralOptions{ + Bearings: []Bearing{ + {60, 380}, + {45, 180}, + }, }, ContinueStraight: ContinueStraightTrue, } diff --git a/table.go b/table.go index a539131..5b99618 100644 --- a/table.go +++ b/table.go @@ -2,15 +2,24 @@ package osrm // TableRequest represents a request to the table method type TableRequest struct { + GeneralOptions Profile string Coordinates Geometry Sources, Destinations []int + Annotations Annotations + FallbackSpeed float64 + FallbackCoordinate FallbackCoordinate + ScaleFactor float64 } // TableResponse resresents a response from the table method type TableResponse struct { ResponseStatus - Durations [][]float32 `json:"durations"` + Durations [][]float32 `json:"durations"` + Distances [][]float32 `json:"distances"` + Sources []Waypoint `json:"sources"` + Destinations []Waypoint `json:"destinations"` + FallbackSpeedCells [][]int `json:"fallback_speed_cells"` } func (r TableRequest) request() *request { @@ -21,6 +30,20 @@ func (r TableRequest) request() *request { if len(r.Destinations) > 0 { opts.addInt("destinations", r.Destinations...) } + if len(r.Annotations) > 0 { + opts.setStringer("annotations", r.Annotations) + } + if r.FallbackSpeed > 0 { + opts.addFloat("fallback_speed", r.FallbackSpeed) + } + if r.FallbackCoordinate.Valid() { + opts.setStringer("fallback_coordinate", r.FallbackCoordinate) + } + if r.ScaleFactor > 0 { + opts.addFloat("scale_factor", r.ScaleFactor) + } + + opts = r.GeneralOptions.options(opts) return &request{ profile: r.Profile, diff --git a/table_test.go b/table_test.go index cf8a55f..2d2f6a1 100644 --- a/table_test.go +++ b/table_test.go @@ -6,15 +6,73 @@ import ( "github.com/stretchr/testify/assert" ) -func TestEmptyTableRequestOptions(t *testing.T) { - req := TableRequest{} - assert.Empty(t, req.request().options.encode()) -} - -func TestNotEmptyTableRequestOptions(t *testing.T) { - req := TableRequest{ - Sources: []int{0, 1, 2}, - Destinations: []int{1, 3}, +func TestTableRequestOptions(t *testing.T) { + cases := []struct { + name string + request TableRequest + expectedURI string + }{ + { + name: "empty", + request: TableRequest{}, + expectedURI: "", + }, + { + name: "with sources and destinations", + request: TableRequest{ + Sources: []int{0, 1, 2}, + Destinations: []int{1, 3}, + }, + expectedURI: "destinations=1;3&sources=0;1;2", + }, + { + name: "scale_factor", + request: TableRequest{ + ScaleFactor: 0.8, + GeneralOptions: GeneralOptions{ + Exclude: []string{"toll"}, + }, + }, + expectedURI: "exclude=toll&scale_factor=0.8", + }, + { + name: "fallback_coordinate", + request: TableRequest{ + FallbackSpeed: 11.5, + GeneralOptions: GeneralOptions{ + Hints: []string{"a", "b"}, + }, + }, + expectedURI: "fallback_speed=11.5&hints=a;b", + }, + { + name: "fallback_coordinate", + request: TableRequest{ + FallbackSpeed: 11.5, + FallbackCoordinate: FallbackCoordinateInput, + }, + expectedURI: "fallback_coordinate=input&fallback_speed=11.5", + }, + { + name: "annotations", + request: TableRequest{ + Annotations: AnnotationsDurationDistance, + FallbackSpeed: 11.5, + }, + expectedURI: "annotations=duration%2Cdistance&fallback_speed=11.5", + }, + { + name: "fallback_coordinate snapped", + request: TableRequest{ + FallbackSpeed: 11.5, + FallbackCoordinate: FallbackCoordinateSnapped, + }, + expectedURI: "fallback_coordinate=snapped&fallback_speed=11.5", + }, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + assert.Equal(t, c.expectedURI, c.request.request().options.encode()) + }) } - assert.Equal(t, "destinations=1;3&sources=0;1;2", req.request().options.encode()) } diff --git a/types.go b/types.go index 3a1e886..3fbaaec 100644 --- a/types.go +++ b/types.go @@ -67,14 +67,15 @@ type Annotations string // Supported annotations param values const ( - AnnotationsTrue Annotations = "true" - AnnotationsFalse Annotations = "false" - AnnotationsNodes Annotations = "nodes" - AnnotationsDistance Annotations = "distance" - AnnotationsDuration Annotations = "duration" - AnnotationsDatasources Annotations = "datasources" - AnnotationsWeight Annotations = "weight" - AnnotationsSpeed Annotations = "speed" + AnnotationsTrue Annotations = "true" + AnnotationsFalse Annotations = "false" + AnnotationsNodes Annotations = "nodes" + AnnotationsDistance Annotations = "distance" + AnnotationsDuration Annotations = "duration" + AnnotationsDatasources Annotations = "datasources" + AnnotationsWeight Annotations = "weight" + AnnotationsSpeed Annotations = "speed" + AnnotationsDurationDistance Annotations = "duration,distance" ) // String returns Annotations as a string @@ -203,3 +204,18 @@ func bearings(br []Bearing) string { } return strings.Join(s, ";") } + +type FallbackCoordinate string + +const ( + FallbackCoordinateInput FallbackCoordinate = "input" + FallbackCoordinateSnapped FallbackCoordinate = "snapped" +) + +func (f FallbackCoordinate) String() string { + return string(f) +} + +func (f FallbackCoordinate) Valid() bool { + return f == FallbackCoordinateInput || f == FallbackCoordinateSnapped +}