From 1b74523e3e2943f6372115b8e9e90057952e1227 Mon Sep 17 00:00:00 2001 From: Nikita Korshak Date: Thu, 30 Jan 2020 11:44:42 +0300 Subject: [PATCH] options refactoring, lists are used instead of strings. Tests updated --- .../samples/BasicDirectionsRefresh.java | 2 +- .../java/com/mapbox/core/utils/TextUtils.java | 12 +- .../directions/v5/models/RouteOptions.java | 667 +++++++++++++++--- .../api/directions/v5/utils/FormatUtils.java | 243 +++++++ .../api/directions/v5/utils/ParseUtils.java | 155 ++++ .../api/directions/v5/utils/package-info.java | 5 + .../v5/models/RouteOptionsTest.java | 367 ++++++++-- .../v5/DirectionsResponseFactory.java | 13 +- .../api/directions/v5/MapboxDirections.java | 384 +++++++--- .../directions/v5/MapboxDirectionsTest.java | 404 +++++++++-- .../api/matching/v5/MapboxMapMatching.java | 11 +- .../matching/v5/MatchingResponseFactory.java | 10 +- .../v5/MapMatchingRouteOptionsTest.java | 2 +- .../matching/v5/MapboxMapMatchingTest.java | 23 +- .../mapbox/api/matrix/v1/MapboxMatrix.java | 5 +- .../optimization/v1/MapboxOptimization.java | 17 +- 16 files changed, 2013 insertions(+), 307 deletions(-) create mode 100644 services-directions-models/src/main/java/com/mapbox/api/directions/v5/utils/FormatUtils.java create mode 100644 services-directions-models/src/main/java/com/mapbox/api/directions/v5/utils/ParseUtils.java create mode 100644 services-directions-models/src/main/java/com/mapbox/api/directions/v5/utils/package-info.java diff --git a/samples/src/main/java/com/mapbox/samples/BasicDirectionsRefresh.java b/samples/src/main/java/com/mapbox/samples/BasicDirectionsRefresh.java index c74063163..55d907511 100644 --- a/samples/src/main/java/com/mapbox/samples/BasicDirectionsRefresh.java +++ b/samples/src/main/java/com/mapbox/samples/BasicDirectionsRefresh.java @@ -31,7 +31,7 @@ private static String simpleMapboxDirectionsRequest() throws IOException { .destination(Point.fromLngLat(-95.3591, 29.7576)) .overview(OVERVIEW_FULL) .profile(PROFILE_DRIVING_TRAFFIC) - .annotations(ANNOTATION_CONGESTION).build(); + .addAnnotation(ANNOTATION_CONGESTION).build(); Response response = directions.executeCall(); System.out.println("Directions response: " + response); diff --git a/services-core/src/main/java/com/mapbox/core/utils/TextUtils.java b/services-core/src/main/java/com/mapbox/core/utils/TextUtils.java index c892fe071..7a131dbba 100644 --- a/services-core/src/main/java/com/mapbox/core/utils/TextUtils.java +++ b/services-core/src/main/java/com/mapbox/core/utils/TextUtils.java @@ -93,7 +93,9 @@ public static String formatCoordinate(double coordinate, int precision) { * @param radiuses a double array which represents the radius values * @return a String ready for being passed into the Retrofit call * @since 3.0.0 + * @deprecated use FormatUtils.formatRadiuses(List) */ + @Deprecated public static String formatRadiuses(double[] radiuses) { if (radiuses == null || radiuses.length == 0) { return null; @@ -118,7 +120,9 @@ public static String formatRadiuses(double[] radiuses) { * @param bearings a List of doubles representing bearing values * @return a string with the bearing values * @since 3.0.0 + * @deprecated use FormatUtils.formatBearing(List) */ + @Deprecated public static String formatBearing(List bearings) { if (bearings.isEmpty()) { return null; @@ -143,7 +147,9 @@ public static String formatBearing(List bearings) { * @param distributions the list of integer arrays representing the distribution * @return a string with the distribution values * @since 3.0.0 + * @deprecated use FormatUtils.formatDistributions(List) */ + @Deprecated public static String formatDistributions(List distributions) { if (distributions.isEmpty()) { return null; @@ -170,13 +176,15 @@ public static String formatDistributions(List distributions) { * @param approaches a string representing approaches to each coordinate. * @return a formatted string. * @since 3.2.0 + * @deprecated use FormatUtils.formatApproaches(List) */ + @Deprecated public static String formatApproaches(String[] approaches) { for (int i = 0; i < approaches.length; i++) { if (approaches[i] == null) { approaches[i] = ""; } else if (!approaches[i].equals("unrestricted") - && !approaches[i].equals("curb") && !approaches[i].isEmpty()) { + && !approaches[i].equals("curb") && !approaches[i].isEmpty()) { return null; } } @@ -190,7 +198,9 @@ public static String formatApproaches(String[] approaches) { * @param waypointNames a string representing approaches to each coordinate. * @return a formatted string. * @since 3.3.0 + * @deprecated use FormatUtils.formatWaypointNames(List) */ + @Deprecated public static String formatWaypointNames(String[] waypointNames) { for (int i = 0; i < waypointNames.length; i++) { if (waypointNames[i] == null) { diff --git a/services-directions-models/src/main/java/com/mapbox/api/directions/v5/models/RouteOptions.java b/services-directions-models/src/main/java/com/mapbox/api/directions/v5/models/RouteOptions.java index 27b6b2647..5f28b8032 100644 --- a/services-directions-models/src/main/java/com/mapbox/api/directions/v5/models/RouteOptions.java +++ b/services-directions-models/src/main/java/com/mapbox/api/directions/v5/models/RouteOptions.java @@ -2,7 +2,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import com.google.auto.value.AutoValue; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -12,9 +11,10 @@ import com.mapbox.api.directions.v5.DirectionsCriteria; import com.mapbox.api.directions.v5.WalkingOptions; import com.mapbox.api.directions.v5.WalkingOptionsAdapterFactory; +import com.mapbox.api.directions.v5.utils.FormatUtils; +import com.mapbox.api.directions.v5.utils.ParseUtils; import com.mapbox.geojson.Point; import com.mapbox.geojson.PointAsCoordinatesTypeAdapter; - import java.util.List; /** @@ -65,20 +65,26 @@ public static Builder builder() { public abstract String user(); /** + * The routing profile to use. Possible values are + * {@link DirectionsCriteria#PROFILE_DRIVING_TRAFFIC}, {@link DirectionsCriteria#PROFILE_DRIVING}, + * {@link DirectionsCriteria#PROFILE_WALKING}, or {@link DirectionsCriteria#PROFILE_CYCLING}. * The same profile which was used during the request that resulted in this root directions * response. MapboxDirections.Builder ensures that a profile is always set even if the * MapboxDirections requesting object doesn't specifically set a profile. * - * @return string value representing the profile + * @return string value representing the profile defined in + * {@link DirectionsCriteria.ProfileCriteria} * @since 3.0.0 */ @NonNull public abstract String profile(); /** - * The coordinates used for the routes origin, destination, and optionally, waypoints. Note that - * these coordinates are different than the direction responses {@link DirectionsWaypoint}s in - * that these are the non-snapped coordinates. + * A list of Points to visit in order. + * There can be between two and 25 coordinates for most requests, or up to three coordinates for + * {@link DirectionsCriteria#PROFILE_DRIVING_TRAFFIC} requests. + * Note that these coordinates are different than the direction responses + * {@link DirectionsWaypoint}s that these are the non-snapped coordinates. * * @return a list of {@link Point}s which represent the route origin, destination, * and optionally, waypoints @@ -88,8 +94,11 @@ public static Builder builder() { public abstract List coordinates(); /** - * The same alternative setting which were used during the request that resulted in this root - * directions response. + * Whether to try to return alternative routes (true) or not (false, default). An alternative + * route is a route that is significantly different than the fastest route, but also still + * reasonably fast. Such a route does not exist in all circumstances. Up to two alternatives may + * be returned. This is available for {@link DirectionsCriteria#PROFILE_DRIVING_TRAFFIC}, + * {@link DirectionsCriteria#PROFILE_DRIVING}, {@link DirectionsCriteria#PROFILE_CYCLING}. * * @return boolean object representing the setting for alternatives * @since 3.0.0 @@ -98,8 +107,8 @@ public static Builder builder() { public abstract Boolean alternatives(); /** - * The same language which was used during the request that resulted in this root directions - * response. + * The language of returned turn-by-turn text instructions. The default is en (English). + * Must be used in conjunction with {@link RouteOptions.Builder#steps(Boolean)}. * * @return the language as a string used during the request, * if english, this will most likely be empty @@ -109,28 +118,73 @@ public static Builder builder() { public abstract String language(); /** - * The same radiuses were used during the request that resulted in this root directions response. + * The maximum distance a coordinate can be moved to snap to the road network in meters. There + * must be as many radiuses as there are coordinates in the request, each separated by ;. + * Values can be any number greater than 0, the string unlimited or empty string. * - * @return a string representing the radiuses + * @return a string representing the radiuses separated by ;. * @since 3.0.0 + * @deprecated use {@link #radiusesList()} ()} + */ + @Nullable + @Deprecated + public String radiuses() { + return FormatUtils.formatRadiuses(radiusesList()); + } + + /** + * The maximum distance a coordinate can be moved to snap to the road network in meters. There + * must be as many radiuses as there are coordinates in the request. + * Values can be any number greater than 0, the string unlimited, or null. + * + * @return a list of radiuses */ @Nullable - public abstract String radiuses(); + public abstract List radiusesList(); /** - * The same bearings which were used during the request that resulted in this root directions - * response. Note that even though these are saved. it's a good idea to recalculate any bearings - * being used and use the newer values for the directions request. + * Influences the direction in which a route starts from a waypoint. Used to filter the road + * segment the waypoint will be placed on by direction. This is useful for making sure the new + * routes of rerouted vehicles continue traveling in their current direction. A request that does + * this would provide bearing and radius values for the first waypoint and leave the remaining + * values empty. Returns two comma-separated values per waypoint: an angle clockwise from true + * north between 0 and 360, and the range of degrees by which the angle can deviate (recommended + * value is 45° or 90°), formatted as {angle, degrees}. If provided, the list of bearings must be + * the same length as the list of coordinates. * - * @return a string representing the bearings used in the original request + * @return a string representing the bearings with the ; separator. Angle and degrees for every + * bearing value are comma-separated. * @since 3.0.0 + * @deprecated use {@link #bearingsList()} */ @Nullable - public abstract String bearings(); + @Deprecated + public String bearings() { + return FormatUtils.formatBearings(bearingsList()); + } /** - * The same continueStraight setting which was used during the request that resulted in this root - * directions response. + * Influences the direction in which a route starts from a waypoint. Used to filter the road + * segment the waypoint will be placed on by direction. This is useful for making sure the new + * routes of rerouted vehicles continue traveling in their current direction. A request that does + * this would provide bearing and radius values for the first waypoint and leave the remaining + * values empty. Returns a list of values, each value is a list of an angle clockwise from true + * north between 0 and 360, and the range of degrees by which the angle can deviate (recommended + * value is 45° or 90°). + * If provided, the list of bearings must be the same length as the list of coordinates. + * + * @return a List of list of doubles representing the bearings used in the original request. + * The first value in the list is the angle, the second one is the degrees. + */ + @SerializedName("bearings") + @Nullable + public abstract List> bearingsList(); + + /** + * The allowed direction of travel when departing intermediate waypoints. If true, the route + * will continue in the same direction of travel. If false, the route may continue in the opposite + * direction of travel. Defaults to true for {@link DirectionsCriteria#PROFILE_DRIVING} and false + * for {@link DirectionsCriteria#PROFILE_WALKING} and {@link DirectionsCriteria#PROFILE_CYCLING}. * * @return a boolean value representing whether or not continueStraight was enabled or * not during the initial request @@ -141,8 +195,11 @@ public static Builder builder() { public abstract Boolean continueStraight(); /** - * This is set to true if you want to enable instructions while exiting roundabouts - * and rotaries. + * Whether to emit instructions at roundabout exits (true) or not (false, default). Without + * this parameter, roundabout maneuvers are given as a single instruction that includes both + * entering and exiting the roundabout. With roundabout_exits=true, this maneuver becomes two + * instructions, one for entering the roundabout and one for exiting it. Must be used in + * conjunction with {@link RouteOptions#steps()}=true. * * @return a boolean value representing whether or not roundaboutExits was enabled or disabled * during the initial route request @@ -153,26 +210,36 @@ public static Builder builder() { public abstract Boolean roundaboutExits(); /** - * Geometry type used to make the initial directions request. + * The format of the returned geometry. Allowed values are: + * {@link DirectionsCriteria#GEOMETRY_POLYLINE} (default, a polyline with a precision of five + * decimal places), {@link DirectionsCriteria#GEOMETRY_POLYLINE6} (a polyline with a precision + * of six decimal places). * - * @return String geometry type used to make the initial directions request. + * @return String geometry type from {@link DirectionsCriteria.GeometriesCriteria}. * @since 3.1.0 */ + @Nullable public abstract String geometries(); /** - * Type of returned overview geometry that was used to make the initial directions request. + * Displays the requested type of overview geometry. Can be + * {@link DirectionsCriteria#OVERVIEW_FULL} (the most detailed geometry + * available), {@link DirectionsCriteria#OVERVIEW_SIMPLIFIED} (default, a simplified version of + * the full geometry), or {@link DirectionsCriteria#OVERVIEW_FALSE} (no overview geometry). * - * @return null or one of the options found in - * {@link DirectionsCriteria.OverviewCriteria} + * @return null or one of the options found in {@link DirectionsCriteria.OverviewCriteria} * @since 3.1.0 */ @Nullable public abstract String overview(); /** - * Boolean value used to determine whether to return steps and turn-by-turn instructions in the - * initial directions request. + * Whether to return steps and turn-by-turn instructions (true) or not (false, default). + * If steps is set to true, the following guidance-related parameters will be available: + * {@link RouteOptions#bannerInstructions()}, {@link RouteOptions#language()}, + * {@link RouteOptions#roundaboutExits()}, {@link RouteOptions#voiceInstructions()}, + * {@link RouteOptions#voiceUnits()}, {@link RouteOptions#waypointNamesList()}, + * {@link RouteOptions#waypointTargetsList()}, waypoints from {@link RouteOptions#coordinates()} * * @return true if you'd like step information, false otherwise * @since 3.1.0 @@ -181,26 +248,69 @@ public static Builder builder() { public abstract Boolean steps(); /** - * The same annotations in String format which were used during the request that resulted in this - * root directions response. + * A comma-separated list of annotations. Defines whether to return additional metadata along the + * route. Possible values are: + * {@link DirectionsCriteria#ANNOTATION_DURATION} + * {@link DirectionsCriteria#ANNOTATION_DISTANCE} + * {@link DirectionsCriteria#ANNOTATION_SPEED} + * {@link DirectionsCriteria#ANNOTATION_CONGESTION} + * {@link DirectionsCriteria#ANNOTATION_MAXSPEED} + * See the {@link RouteLeg} object for more details on what is included with annotations. + * Must be used in conjunction with overview=full. * * @return a string containing any of the annotations that were used during the request * @since 3.0.0 + * + * @deprecated use {@link #annotationsList()} */ + @Deprecated @Nullable - public abstract String annotations(); + public String annotations() { + return FormatUtils.join(";", annotationsList(), true); + } /** - * The same exclusions the user originally made when the request was made. + * A list of annotations. Defines whether to return additional metadata along the + * route. Possible values are: + * {@link DirectionsCriteria#ANNOTATION_DURATION} + * {@link DirectionsCriteria#ANNOTATION_DISTANCE} + * {@link DirectionsCriteria#ANNOTATION_SPEED} + * {@link DirectionsCriteria#ANNOTATION_CONGESTION} + * {@link DirectionsCriteria#ANNOTATION_MAXSPEED} + * See the {@link RouteLeg} object for more details on what is included with annotations. + * Must be used in conjunction with overview=full. + * + * @return a list of annotations that were used during the request + */ + @SerializedName("annotations") + @Nullable + public abstract List annotationsList(); + + /** + * Exclude certain road types from routing. The default is to not exclude anything from the + * profile selected. The following exclude flags are available for each profile: + * + * {@link DirectionsCriteria#PROFILE_DRIVING}: One of {@link DirectionsCriteria#EXCLUDE_TOLL}, + * {@link DirectionsCriteria#EXCLUDE_MOTORWAY}, or {@link DirectionsCriteria#EXCLUDE_FERRY}. + * + * {@link DirectionsCriteria#PROFILE_DRIVING_TRAFFIC}: One of + * {@link DirectionsCriteria#EXCLUDE_TOLL}, {@link DirectionsCriteria#EXCLUDE_MOTORWAY}, or + * {@link DirectionsCriteria#EXCLUDE_FERRY}. + * + * {@link DirectionsCriteria#PROFILE_WALKING}: No excludes supported + * + * {@link DirectionsCriteria#PROFILE_CYCLING}: {@link DirectionsCriteria#EXCLUDE_FERRY} * - * @return a string matching one of the {@link DirectionsCriteria} exclusions + * @return a string matching one of the {@link DirectionsCriteria.ExcludeCriteria} exclusions * @since 3.0.0 */ @Nullable public abstract String exclude(); /** - * Whether or not the request had voice instructions set to true or not. + * Whether to return SSML marked-up text for voice guidance along the route (true) or not + * (false, default). + * Must be used in conjunction with {@link RouteOptions#steps()}=true. * * @return true if the original request included voice instructions * @since 3.0.0 @@ -210,7 +320,8 @@ public static Builder builder() { public abstract Boolean voiceInstructions(); /** - * Whether or not the request had banner instructions set to true or not. + * Whether to return banner objects associated with the route steps (true) or not + * (false, default). Must be used in conjunction with {@link RouteOptions#steps()}=true * * @return true if the original request included banner instructions * @since 3.0.0 @@ -220,7 +331,10 @@ public static Builder builder() { public abstract Boolean bannerInstructions(); /** - * Whether or not the units used inside the voice instruction's string are in imperial or metric. + * A type of units to return in the text for voice instructions. + * Can be {@link DirectionsCriteria#IMPERIAL} (default) or {@link DirectionsCriteria#METRIC}. + * Must be used in conjunction with {@link RouteOptions#steps()}=true and + * {@link RouteOptions#voiceInstructions()} ()}=true. * * @return a string matching either imperial or metric * @since 3.0.0 @@ -248,25 +362,46 @@ public static Builder builder() { * @since 3.0.0 */ @SerializedName("uuid") - @NonNull + @Nullable public abstract String requestUuid(); /** * Indicates from which side of the road to approach a waypoint. - * Accepts unrestricted (default) or curb . If set to unrestricted , - * the route can approach waypoints from either side of the road. - * If set to curb, the route will be returned so that on arrival, - * the waypoint will be found on the side that corresponds with the driving_side of the region - * in which the returned route is located. + * Accepts {@link DirectionsCriteria#APPROACH_UNRESTRICTED} (default) or + * {@link DirectionsCriteria#APPROACH_CURB} . + * If set to {@link DirectionsCriteria#APPROACH_UNRESTRICTED}, the route can approach waypoints + * from either side of the road. + * If set to {@link DirectionsCriteria#APPROACH_CURB}, the route will be returned so that on + * arrival, the waypoint will be found on the side that corresponds with the driving_side of the + * region in which the returned route is located. * If provided, the list of approaches must be the same length as the list of waypoints. - * However, you can skip a coordinate and show its position in the list with the ; separator. * * @return a string representing approaches for each waypoint * @since 3.2.0 + * @deprecated use {@link #approachesList()} */ + @Deprecated + @Nullable + public String approaches() { + return FormatUtils.formatApproaches(approachesList()); + } + /** + * Indicates from which side of the road to approach a waypoint. + * Accepts {@link DirectionsCriteria#APPROACH_UNRESTRICTED} (default) or + * {@link DirectionsCriteria#APPROACH_CURB} . + * If set to {@link DirectionsCriteria#APPROACH_UNRESTRICTED}, the route can approach waypoints + * from either side of the road. + * If set to {@link DirectionsCriteria#APPROACH_CURB}, the route will be returned so that on + * arrival, the waypoint will be found on the side that corresponds with the driving_side of the + * region in which the returned route is located. + * If provided, the list of approaches must be the same length as the list of waypoints. + * + * @return a list of strings representing approaches for each waypoint + */ + @SerializedName("approaches") @Nullable - public abstract String approaches(); + public abstract List approachesList(); /** * Indicates which input coordinates should be treated as waypoints. @@ -279,22 +414,61 @@ public static Builder builder() { * * @return a string representing indices to be used as waypoints * @since 4.4.0 + * @deprecated use {@link #waypointIndicesList()} + */ + @Nullable + @Deprecated + public String waypointIndices() { + return FormatUtils.join(";", waypointIndicesList()); + } + + /** + * Indicates which input coordinates should be treated as waypoints. + *

+ * Most useful in combination with steps=true and requests based on traces + * with high sample rates. Can be an index corresponding to any of the input coordinates, + * but must contain the first ( 0 ) and last coordinates' index. + * {@link #steps()} + *

+ * + * @return a List of Integers representing indices to be used as waypoints */ @SerializedName("waypoints") @Nullable - public abstract String waypointIndices(); + public abstract List waypointIndicesList(); /** - * Custom names for waypoints used for the arrival instruction in banners and voice instructions, - * each separated by ; . Values can be any string and total number of all characters cannot - * exceed 500. If provided, the list of waypoint_names must be the same length as the list of - * coordinates, but you can skip a coordinate and show its position with the ; separator. + * A semicolon-separated list of custom names for entries in the list of + * {@link RouteOptions#coordinates()}, used for the arrival instruction in banners and voice + * instructions. Values can be any string, and the total number of all characters cannot exceed + * 500. If provided, the list of waypoint_names must be the same length as the list of + * coordinates. The first value in the list corresponds to the route origin, not the first + * destination. + * Must be used in conjunction with {@link RouteOptions#steps()} = true. * @return a string representing names for each waypoint * @since 3.3.0 + * @deprecated use {@link #waypointNamesList()} + */ + @Nullable + @Deprecated + public String waypointNames() { + return FormatUtils.formatWaypointNames(waypointNamesList()); + } + + /** + * A semicolon-separated list of custom names for entries in the list of + * {@link RouteOptions#coordinates()}, used for the arrival instruction in banners and voice + * instructions. Values can be any string, and the total number of all characters cannot exceed + * 500. If provided, the list of waypoint_names must be the same length as the list of + * coordinates. The first value in the list corresponds to the route origin, not the first + * destination. + * Must be used in conjunction with {@link RouteOptions#steps()} = true. + * + * @return a list of strings representing names for each waypoint */ @SerializedName("waypoint_names") @Nullable - public abstract String waypointNames(); + public abstract List waypointNamesList(); /** @@ -303,15 +477,31 @@ public static Builder builder() { * If this parameter is provided, the Directions API will compute the side of the street, * left or right, for each target based on the waypoint_targets and the driving direction. * The maneuver.modifier, banner and voice instructions will be updated with the computed - * side of street. The number of waypoint targets must be the same as the number of coordinates, - * but you can skip a coordinate pair and show its position in the list with the ; separator. - * Must be used with steps=true. - * @return a string representing coordinate pairs for drop-off locations + * side of street. The number of waypoint targets must be the same as the number of coordinates. + * Must be used with {@link RouteOptions#steps()} = true. + * @return a list of Points representing coordinate pairs for drop-off locations * @since 4.3.0 + * @deprecated use {@link #waypointTargetsList()} + */ + @Nullable + @Deprecated + public String waypointTargets() { + return FormatUtils.formatPointsList(waypointTargetsList()); + } + + /** + * A list of points used to specify drop-off + * locations that are distinct from the locations specified in coordinates. + * If this parameter is provided, the Directions API will compute the side of the street, + * left or right, for each target based on the waypoint_targets and the driving direction. + * The maneuver.modifier, banner and voice instructions will be updated with the computed + * side of street. The number of waypoint targets must be the same as the number of coordinates. + * Must be used with {@link RouteOptions#steps()} = true. + * @return a list of Points representing coordinate pairs for drop-off locations */ @SerializedName("waypoint_targets") @Nullable - public abstract String waypointTargets(); + public abstract List waypointTargetsList(); /** * To be used to specify settings for use with the walking profile. @@ -341,6 +531,7 @@ public static TypeAdapter typeAdapter(Gson gson) { * method * @since 3.4.0 */ + @NonNull public static RouteOptions fromJson(String json) { GsonBuilder gson = new GsonBuilder(); gson.registerTypeAdapterFactory(DirectionsAdapterFactory.create()); @@ -388,20 +579,27 @@ public abstract static class Builder { public abstract Builder user(@NonNull String user); /** - * The directions profile that was used during the request time and resulted in this responses - * result. + * The routing profile to use. Possible values are + * {@link DirectionsCriteria#PROFILE_DRIVING_TRAFFIC}, + * {@link DirectionsCriteria#PROFILE_DRIVING}, {@link DirectionsCriteria#PROFILE_WALKING}, or + * {@link DirectionsCriteria#PROFILE_CYCLING}. + * The same profile which was used during the request that resulted in this root directions + * response. MapboxDirections.Builder ensures that a profile is always set even if the + * MapboxDirections requesting object doesn't specifically set a profile. * * @param profile One of the direction profiles defined in - * {@link DirectionsCriteria#DirectionsCriteria()} + * {@link DirectionsCriteria.ProfileCriteria} * @return this builder for chaining options together * @since 3.0.0 */ public abstract Builder profile(@NonNull @DirectionsCriteria.ProfileCriteria String profile); /** - * The coordinates used for the routes origin, destination, and optionally, waypoints. Note that - * these coordinates are different than the direction responses {@link DirectionsWaypoint}s in - * that these are the non-snapped coordinates. + * A list of Points to visit in order. + * There can be between two and 25 coordinates for most requests, or up to three coordinates for + * {@link DirectionsCriteria#PROFILE_DRIVING_TRAFFIC} requests. + * Note that these coordinates are different than the direction responses + * {@link DirectionsWaypoint}s that these are the non-snapped coordinates. * * @param coordinates a list of {@link Point}s which represent the route origin, destination, * and optionally, waypoints @@ -411,133 +609,247 @@ public abstract static class Builder { public abstract Builder coordinates(@NonNull List coordinates); /** - * Whether the alternatives value was set to true or not. + * Whether to try to return alternative routes (true) or not (false, default). An alternative + * route is a route that is significantly different than the fastest route, but also still + * reasonably fast. Such a route does not exist in all circumstances. Up to two alternatives may + * be returned. This is available for{@link DirectionsCriteria#PROFILE_DRIVING_TRAFFIC}, + * {@link DirectionsCriteria#PROFILE_DRIVING}, {@link DirectionsCriteria#PROFILE_CYCLING}. * * @param alternatives true if the request contained additional route request, otherwise false * @return this builder for chaining options together * @since 3.0.0 */ - public abstract Builder alternatives(@Nullable Boolean alternatives); + public abstract Builder alternatives(@NonNull Boolean alternatives); /** - * The language for instructions to be in when the response is given. + * The language of returned turn-by-turn text instructions. The default is en (English). + * Must be used in conjunction with {@link RouteOptions#steps()} = true. * * @param language a string with the language which was requested in the url * @return this builder for chaining options together * @since 3.0.0 */ - public abstract Builder language(String language); + public abstract Builder language(@NonNull String language); /** - * The radiuses in string format that were used during the original request. + * The maximum distance a coordinate can be moved to snap to the road network in meters. There + * must be as many radiuses as there are coordinates in the request, each separated by ;. + * Values can be any number greater than 0 or the string unlimited. * - * @param radiuses radiuses values separated by comma + * @param radiuses a String of radius values, each separated by ;. * @return this builder for chaining options together * @since 3.0.0 + * @deprecated use {@link #radiusesList(List)} */ - public abstract Builder radiuses(String radiuses); + @Deprecated + public Builder radiuses(@NonNull String radiuses) { + List radiusesList = ParseUtils.parseToDoubles(radiuses); + if (radiusesList != null) { + radiusesList(radiusesList); + } + return this; + } /** - * The bearing values the user used for the original request which resulted in this response. - * It is best to recalculate these values since they are probably outdated already. + * The maximum distance a coordinate can be moved to snap to the road network in meters. There + * must be as many radiuses as there are coordinates in the request. + * Values can be any number greater than 0 or {@link Double#POSITIVE_INFINITY}. * - * @param bearings number values representing the bearings separated by commas + * @param radiuses a list of radius values + * @return this builder for chaining options together + */ + public abstract Builder radiusesList(@NonNull List radiuses); + + /** + * Influences the direction in which a route starts from a waypoint. Used to filter the road + * segment the waypoint will be placed on by direction. This is useful for making sure the new + * routes of rerouted vehicles continue traveling in their current direction. A request that + * does this would provide bearing and radius values for the first waypoint and leave the + * remaining values empty. Takes two comma-separated values per waypoint: an angle clockwise + * from true north between 0 and 360, and the range of degrees by which the angle can deviate + * (recommended value is 45° or 90°), formatted as {angle, degrees}. If provided, the list of + * bearings must be the same length as the list of coordinates. However, you can skip a + * coordinate and show its position in the list with the ; separator. + * + * @param bearings a string representing the bearings with the ; separator. Angle and degrees + * for every bearing value are comma-separated. * @return this builder for chaining options together * @since 3.0.0 + * @deprecated use {@link #bearingsList(List)} + */ + @Deprecated + public Builder bearings(@NonNull String bearings) { + List> bearingsList = ParseUtils.parseToListOfListOfDoubles(bearings); + if (bearingsList != null) { + bearingsList(bearingsList); + } + return this; + } + + /** + * Influences the direction in which a route starts from a waypoint. Used to filter the road + * segment the waypoint will be placed on by direction. This is useful for making sure the new + * routes of rerouted vehicles continue traveling in their current direction. A request that + * does this would provide bearing and radius values for the first waypoint and leave the + * remaining values empty. Takes a List of list of doubles: the first value in the list is an + * angle clockwise from true north between 0 and 360, the second value is the range of degrees + * by which the angle can deviate (recommended value is 45° or 90°). + * If provided, the list of bearings must be the same length as the list of coordinates. + * However, you can skip a coordinate and show its position in the list with the null value. + * + * @param bearings a List of list of doubles representing the bearings used in the original + * request. The first value in the list is the angle, the second one is the + * degrees. + * @return this builder for chaining options together */ - public abstract Builder bearings(String bearings); + public abstract Builder bearingsList(@NonNull List> bearings); /** - * Whether the original request wanted continueStraight enabled or not. + * Sets the allowed direction of travel when departing intermediate waypoints. If true, the + * route will continue in the same direction of travel. If false, the route may continue in the + * opposite direction of travel. Defaults to true for {@link DirectionsCriteria#PROFILE_DRIVING} + * and false for {@link DirectionsCriteria#PROFILE_WALKING} and + * {@link DirectionsCriteria#PROFILE_CYCLING}. * * @param continueStraight true if you'd like the user to continue straight from the starting * point * @return this builder for chaining options together * @since 3.0.0 */ - public abstract Builder continueStraight(Boolean continueStraight); + public abstract Builder continueStraight(@NonNull Boolean continueStraight); /** - * This is set to true if you want to enable instructions while exiting roundabouts - * and rotaries. + * Whether to emit instructions at roundabout exits (true) or not (false, default). Without + * this parameter, roundabout maneuvers are given as a single instruction that includes both + * entering and exiting the roundabout. With roundabout_exits=true, this maneuver becomes two + * instructions, one for entering the roundabout and one for exiting it. Must be used in + * conjunction with {@link RouteOptions.Builder#steps(Boolean)} * * @param roundaboutExits true if you'd like extra roundabout instructions * @return this builder for chaining options together * @since 3.1.0 */ - public abstract Builder roundaboutExits(@Nullable Boolean roundaboutExits); + public abstract Builder roundaboutExits(@NonNull Boolean roundaboutExits); /** - * alter the default geometry being returned for the directions route. A null value will reset - * this field to the APIs default value vs this SDKs default value of + * The format of the returned geometry. Allowed values are: + * {@link DirectionsCriteria#GEOMETRY_POLYLINE} (default, a polyline with a precision of five + * decimal places), {@link DirectionsCriteria#GEOMETRY_POLYLINE6} (a polyline with a precision + * of six decimal places). + * A null value will reset this field to the APIs default value vs this SDKs default value of * {@link DirectionsCriteria#GEOMETRY_POLYLINE6}. * - * @param geometries null if you'd like the default geometry, else one of the options found in - * {@link DirectionsCriteria.GeometriesCriteria}. + * @param geometries one of the options found in {@link DirectionsCriteria.GeometriesCriteria}. * @return this builder for chaining options together * @since 3.1.0 */ - public abstract Builder geometries(@DirectionsCriteria.GeometriesCriteria String geometries); + public abstract Builder geometries( + @NonNull @DirectionsCriteria.GeometriesCriteria String geometries); /** - * Type of returned overview geometry that was used to make the initial directions request. + * Displays the requested type of overview geometry. Can be + * {@link DirectionsCriteria#OVERVIEW_FULL} (the most detailed geometry + * available), {@link DirectionsCriteria#OVERVIEW_SIMPLIFIED} (default, a simplified version of + * the full geometry), or {@link DirectionsCriteria#OVERVIEW_FALSE} (no overview geometry). * - * @param overview null or one of the options found in - * {@link DirectionsCriteria.OverviewCriteria} + * @param overview one of the options found in {@link DirectionsCriteria.OverviewCriteria} * @return this builder for chaining options together * @since 3.1.0 */ public abstract Builder overview( - @Nullable @DirectionsCriteria.OverviewCriteria String overview + @NonNull @DirectionsCriteria.OverviewCriteria String overview ); /** - * Boolean value used to determine whether to return steps and turn-by-turn instructions in the - * initial directions request. + * Whether to return steps and turn-by-turn instructions (true) or not (false, default). + * If steps is set to true, the following guidance-related parameters will be available: + * {@link RouteOptions#bannerInstructions()}, {@link RouteOptions#language()}, + * {@link RouteOptions#roundaboutExits()}, {@link RouteOptions#voiceInstructions()}, + * {@link RouteOptions#voiceUnits()}, {@link RouteOptions#waypointNamesList()}, + * {@link RouteOptions#waypointTargetsList()}, waypoints from {@link RouteOptions#coordinates()} * * @param steps true if you'd like step information, false otherwise * @return this builder for chaining options together * @since 3.1.0 */ - public abstract Builder steps(@Nullable Boolean steps); + public abstract Builder steps(@NonNull Boolean steps); /** - * The annotation which were used during the request process. + * Whether to return additional metadata along the route. Possible values are: + * {@link DirectionsCriteria#ANNOTATION_DURATION} + * {@link DirectionsCriteria#ANNOTATION_DISTANCE} + * {@link DirectionsCriteria#ANNOTATION_SPEED} + * {@link DirectionsCriteria#ANNOTATION_CONGESTION} + * {@link DirectionsCriteria#ANNOTATION_MAXSPEED} + * You can include several annotations as a comma-separated list. See the + * {@link RouteLeg} object for more details on what is included with annotations. + * Must be used in conjunction with overview=full. * * @param annotations in string format and separated by commas if more than one annotation was * requested * @return this builder for chaining options together * @since 3.0.0 + * @deprecated use {@link #annotationsList(List)} + */ + @Deprecated + public Builder annotations(@NonNull String annotations) { + List annotationsList = ParseUtils.parseToStrings(annotations); + if (annotationsList != null) { + annotationsList(annotationsList); + } + return this; + } + + /** + * Whether to return additional metadata along the route. Possible values are: + * {@link DirectionsCriteria#ANNOTATION_DURATION} + * {@link DirectionsCriteria#ANNOTATION_DISTANCE} + * {@link DirectionsCriteria#ANNOTATION_SPEED} + * {@link DirectionsCriteria#ANNOTATION_CONGESTION} + * {@link DirectionsCriteria#ANNOTATION_MAXSPEED} + * + * See the {@link RouteLeg} object for more details on what is included with + * annotations. + * Must be used in conjunction with overview=full. + * + * @param annotations a list of annotations + * @return this builder for chaining options together */ - public abstract Builder annotations(String annotations); + public abstract Builder annotationsList(@NonNull List annotations); /** - * Whether or not the request had voice instructions set to true or not. + * Whether to return SSML marked-up text for voice guidance along the route (true) or not + * (false, default). + * Must be used in conjunction with {@link RouteOptions.Builder#steps(Boolean)}. * * @param voiceInstructions true if the original request included voice instructions * @return this builder for chaining options together * @since 3.0.0 */ - public abstract Builder voiceInstructions(Boolean voiceInstructions); + public abstract Builder voiceInstructions(@NonNull Boolean voiceInstructions); /** - * Whether or not the request had banner instructions set to true or not. + * Whether to return banner objects associated with the route steps (true) or not + * (false, default). Must be used in conjunction with + * {@link RouteOptions.Builder#steps(Boolean)} * * @param bannerInstructions true if the original request included banner instructions * @return this builder for chaining options together * @since 3.0.0 */ - public abstract Builder bannerInstructions(Boolean bannerInstructions); + public abstract Builder bannerInstructions(@NonNull Boolean bannerInstructions); /** - * Whether or not the units used inside the voice instruction's string are - * in imperial or metric. + * Specify which type of units to return in the text for voice instructions. + * Can be {@link DirectionsCriteria#IMPERIAL} (default) or {@link DirectionsCriteria#METRIC}. + * Must be used in conjunction with {@link RouteOptions.Builder#steps(Boolean)}=true and + * {@link RouteOptions.Builder#voiceInstructions(Boolean)}=true. * * @param voiceUnits string matching either imperial or metric * @return this builder for chaining options together * @since 3.0.0 */ - public abstract Builder voiceUnits(@Nullable String voiceUnits); + public abstract Builder voiceUnits(@NonNull String voiceUnits); /** * A valid Mapbox access token used to making the request. @@ -559,49 +871,190 @@ public abstract Builder overview( public abstract Builder requestUuid(@NonNull String requestUuid); /** - * The same exclusions the user originally made when the request was made. + * Exclude certain road types from routing. The default is to not exclude anything from the + * profile selected. The following exclude flags are available for each profile: * - * @param exclude a string matching one of the {@link DirectionsCriteria} exclusions + * {@link DirectionsCriteria#PROFILE_DRIVING}: One of {@link DirectionsCriteria#EXCLUDE_TOLL}, + * {@link DirectionsCriteria#EXCLUDE_MOTORWAY}, or {@link DirectionsCriteria#EXCLUDE_FERRY}. + * + * {@link DirectionsCriteria#PROFILE_DRIVING_TRAFFIC}: One of + * {@link DirectionsCriteria#EXCLUDE_TOLL}, {@link DirectionsCriteria#EXCLUDE_MOTORWAY}, or + * {@link DirectionsCriteria#EXCLUDE_FERRY}. + * + * {@link DirectionsCriteria#PROFILE_WALKING}: No excludes supported + * + * {@link DirectionsCriteria#PROFILE_CYCLING}: {@link DirectionsCriteria#EXCLUDE_FERRY} + * + * @param exclude a string matching one of the {@link DirectionsCriteria.ExcludeCriteria} + * exclusions * @return this builder for chaining options together * @since 3.0.0 */ public abstract Builder exclude(@NonNull String exclude); /** + * Indicates from which side of the road to approach a waypoint. + * Accepts {@link DirectionsCriteria#APPROACH_UNRESTRICTED} (default) or + * {@link DirectionsCriteria#APPROACH_CURB} . + * If set to {@link DirectionsCriteria#APPROACH_UNRESTRICTED}, the route can approach waypoints + * from either side of the road. + * If set to {@link DirectionsCriteria#APPROACH_CURB}, the route will be returned so that on + * arrival, the waypoint will be found on the side that corresponds with the driving_side of the + * region in which the returned route is located. + * If provided, the list of approaches must be the same length as the list of waypoints. + * However, you can skip a coordinate and show its position in the list with the ; separator. * The same approaches the user originally made when the request was made. * * @param approaches unrestricted, curb or omitted (;) * @return this builder for chaining options together * @since 3.2.0 + * @deprecated use {@link #approachesList(List)} + */ + @Deprecated + public Builder approaches(@NonNull String approaches) { + List approachesList = ParseUtils.parseToStrings(approaches); + if (approachesList != null) { + approachesList(approachesList); + } + return this; + } + + /** + * Indicates from which side of the road to approach a waypoint. + * Accepts {@link DirectionsCriteria#APPROACH_UNRESTRICTED} (default) or + * {@link DirectionsCriteria#APPROACH_CURB} . + * If set to {@link DirectionsCriteria#APPROACH_UNRESTRICTED}, the route can approach waypoints + * from either side of the road. + * If set to {@link DirectionsCriteria#APPROACH_CURB}, the route will be returned so that on + * arrival, the waypoint will be found on the side that corresponds with the driving_side of the + * region in which the returned route is located. + * If provided, the list of approaches must be the same length as the list of waypoints. + * However, you can skip a coordinate and show its position in the list with null value. + * The same approaches the user originally made when the request was made. + * + * @param approaches a list of Strings + * @return this builder for chaining options together */ - public abstract Builder approaches(String approaches); + public abstract Builder approachesList(@NonNull List approaches); /** + * Indicates which input coordinates should be treated as waypoints. + *

+ * Most useful in combination with steps=true and requests based on traces + * with high sample rates. Can be an index corresponding to any of the input coordinates, + * but must contain the first ( 0 ) and last coordinates' index separated by ; . + * {@link #steps()} + *

* The same waypoint indices the user originally made when the request was made. * - * @param indices to be used as waypoints + * @param waypointIndices to be used as waypoints * @return this builder for chaining options together * @since 4.4.0 + * @deprecated use {@link #waypointIndicesList(List)} */ - public abstract Builder waypointIndices(@Nullable String indices); + @Deprecated + public Builder waypointIndices(@NonNull String waypointIndices) { + List indices = ParseUtils.parseToIntegers(waypointIndices); + if (indices != null) { + waypointIndicesList(indices); + } + return this; + } /** - * The same waypoint names the user originally made when the request was made. + * Indicates which input coordinates should be treated as waypoints. + *

+ * Most useful in combination with steps=true and requests based on traces + * with high sample rates. Can be an index corresponding to any of the input coordinates, + * but must contain the first ( 0 ) and last coordinates'. + * {@link #steps()} + *

+ * The same waypoint indices the user originally made when the request was made. + * + * @param indices a list to be used as waypoints + * @return this builder for chaining options together + */ + public abstract Builder waypointIndicesList(@NonNull List indices); + + /** + * A semicolon-separated list of custom names for entries in the list of + * {@link RouteOptions#coordinates()}, used for the arrival instruction in banners and voice + * instructions. Values can be any string, and the total number of all characters cannot exceed + * 500. If provided, the list of waypoint_names must be the same length as the list of + * coordinates, but you can skip a coordinate pair and show its position in the list with the ; + * separator. The first value in the list corresponds to the route origin, not the first + * destination. To leave the origin unnamed, begin the list with a semicolon. + * Must be used in conjunction with {@link RouteOptions.Builder#steps(Boolean)} = true. * * @param waypointNames unrestricted, curb or omitted (;) * @return this builder for chaining options together * @since 3.3.0 + * @deprecated use {@link #waypointNamesList(List)} + */ + @Deprecated + public Builder waypointNames(@NonNull String waypointNames) { + List names = ParseUtils.parseToStrings(waypointNames); + if (names != null) { + waypointNamesList(names); + } + return this; + } + + /** + * A semicolon-separated list of custom names for entries in the list of + * {@link RouteOptions#coordinates()}, used for the arrival instruction in banners and voice + * instructions. Values can be any string, and the total number of all characters cannot exceed + * 500. If provided, the list of waypoint_names must be the same length as the list of + * coordinates, but you can skip a coordinate pair and show its position in the list with the + * null value. The first value in the list corresponds to the route origin, not the first + * destination. To leave the origin unnamed, begin the list with a null value. + * Must be used in conjunction with {@link RouteOptions.Builder#steps(Boolean)} = true. + * + * @param waypointNames a list of Strings + * @return this builder for chaining options together */ - public abstract Builder waypointNames(@Nullable String waypointNames); + public abstract Builder waypointNamesList(@NonNull List waypointNames); /** + * A semicolon-separated list of coordinate pairs used to specify drop-off + * locations that are distinct from the locations specified in coordinates. + * If this parameter is provided, the Directions API will compute the side of the street, + * left or right, for each target based on the waypoint_targets and the driving direction. + * The maneuver.modifier, banner and voice instructions will be updated with the computed + * side of street. The number of waypoint targets must be the same as the number of coordinates, + * but you can skip a coordinate pair and show its position in the list with the ; separator. + * Must be used with {@link RouteOptions.Builder#steps(Boolean)} = true. * The same waypoint targets the user originally made when the request was made. * * @param waypointTargets list of coordinate pairs for drop-off locations (;) * @return this builder for chaining options together * @since 4.3.0 + * @deprecated use {@link #waypointTargetsList(List)} + */ + @Deprecated + public Builder waypointTargets(@NonNull String waypointTargets) { + List targets = ParseUtils.parseToPoints(waypointTargets); + if (targets != null) { + waypointTargetsList(targets); + } + return this; + } + + /** + * A list of coordinate pairs used to specify drop-off + * locations that are distinct from the locations specified in coordinates. + * If this parameter is provided, the Directions API will compute the side of the street, + * left or right, for each target based on the waypoint_targets and the driving direction. + * The maneuver.modifier, banner and voice instructions will be updated with the computed + * side of street. The number of waypoint targets must be the same as the number of coordinates, + * but you can skip a coordinate pair and show its position in the list with the null value. + * Must be used with {@link RouteOptions.Builder#steps(Boolean)} = true. + * The same waypoint targets the user originally made when the request was made. + * + * @param waypointTargets list of Points for drop-off locations + * @return this builder for chaining options together */ - public abstract Builder waypointTargets(@Nullable String waypointTargets); + public abstract Builder waypointTargetsList(@NonNull List waypointTargets); /** * To be used to specify settings for use with the walking profile. diff --git a/services-directions-models/src/main/java/com/mapbox/api/directions/v5/utils/FormatUtils.java b/services-directions-models/src/main/java/com/mapbox/api/directions/v5/utils/FormatUtils.java new file mode 100644 index 000000000..fc395fe84 --- /dev/null +++ b/services-directions-models/src/main/java/com/mapbox/api/directions/v5/utils/FormatUtils.java @@ -0,0 +1,243 @@ +package com.mapbox.api.directions.v5.utils; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.mapbox.geojson.Point; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +/** + * Methods to convert models to Strings. + */ +public class FormatUtils { + /** + * Returns a string containing the tokens joined by delimiters. Doesn't remove trailing nulls. + * + * @param delimiter the delimiter on which to split. + * @param tokens A list of objects to be joined. Strings will be formed from the objects by + * calling object.toString(). + * @return {@link String} + */ + @Nullable + public static String join(@NonNull CharSequence delimiter, @Nullable List tokens) { + return join(delimiter, tokens, false); + } + + /** + * Returns a string containing the tokens joined by delimiters. + * + * @param delimiter the delimiter on which to split. + * @param tokens A list of objects to be joined. Strings will be formed from the objects by + * calling object.toString(). + * @param removeTrailingNulls true if trailing nulls should be removed. + * @return {@link String} + */ + @Nullable + public static String join(@NonNull CharSequence delimiter, @Nullable List tokens, + boolean removeTrailingNulls) { + if (tokens == null || tokens.size() < 1) { + return null; + } + + int lastNonNullToken = tokens.size() - 1; + if (removeTrailingNulls) { + for (int i = tokens.size() - 1; i >= 0; i--) { + Object token = tokens.get(i); + if (token != null) { + break; + } else { + lastNonNullToken--; + } + } + } + + StringBuilder sb = new StringBuilder(); + boolean firstTime = true; + for (int i = 0; i <= lastNonNullToken; i++) { + if (firstTime) { + firstTime = false; + } else { + sb.append(delimiter); + } + Object token = tokens.get(i); + if (token != null) { + sb.append(token); + } + } + return sb.toString(); + } + + /** + * Useful to remove any trailing zeros and prevent a coordinate being over 7 significant figures. + * + * @param coordinate a double value representing a coordinate. + * @return a formatted string. + */ + @NonNull + public static String formatCoordinate(double coordinate) { + DecimalFormat decimalFormat = new DecimalFormat("0.######", + new DecimalFormatSymbols(Locale.US)); + return String.format(Locale.US, "%s", + decimalFormat.format(coordinate)); + } + + /** + * Used in various APIs to format the user provided radiuses to a String matching the APIs + * format. + * + * @param radiuses a list of doubles represents the radius values + * @return a String ready for being passed into the Retrofit call + */ + @Nullable + public static String formatRadiuses(@Nullable List radiuses) { + if (radiuses == null || radiuses.size() == 0) { + return null; + } + + List radiusesToJoin = new ArrayList<>(); + for (Double radius : radiuses) { + if (radius == null) { + radiusesToJoin.add(null); + } else if (radius == Double.POSITIVE_INFINITY) { + radiusesToJoin.add("unlimited"); + } else { + radiusesToJoin.add(String.format(Locale.US, "%s", formatCoordinate(radius))); + } + } + return join(";", radiusesToJoin); + } + + /** + * Formats the bearing variables from the raw values to a string which can than be used for the + * request URL. + * + * @param bearings a List of list of doubles representing bearing values + * @return a string with the bearing values + */ + @Nullable + public static String formatBearings(@Nullable List> bearings) { + if (bearings == null || bearings.isEmpty()) { + return null; + } + + List bearingsToJoin = new ArrayList<>(); + for (List bearing : bearings) { + if (bearing == null || bearing.size() == 0) { + bearingsToJoin.add(null); + } else { + bearingsToJoin.add(String.format(Locale.US, "%s,%s", + formatCoordinate(bearing.get(0)), + formatCoordinate(bearing.get(1)))); + } + } + return join(";", bearingsToJoin); + } + + /** + * Converts the list of integer arrays to a string ready for API consumption. + * + * @param distributions the list of integer arrays representing the distribution + * @return a string with the distribution values + */ + @Nullable + public static String formatDistributions(@Nullable List distributions) { + if (distributions == null || distributions.isEmpty()) { + return null; + } + + List distributionsToJoin = new ArrayList<>(); + for (Integer[] array : distributions) { + if (array.length == 0) { + distributionsToJoin.add(null); + } else { + distributionsToJoin.add(String.format(Locale.US, "%s,%s", + formatCoordinate(array[0]), + formatCoordinate(array[1]))); + } + } + return join(";", distributionsToJoin); + } + + /** + * Converts String list with approaches values to a string ready for API consumption. An approach + * could be unrestricted, curb or null. + * + * @param approaches a list representing approaches to each coordinate. + * @return a formatted string. + */ + @Nullable + public static String formatApproaches(@Nullable List approaches) { + if (approaches == null || approaches.isEmpty()) { + return null; + } + + for (String approach : approaches) { + if (approach != null && !approach.equals("unrestricted") && !approach.equals("curb") + && !approach.isEmpty()) { + return null; + } + } + return join(";", approaches); + } + + /** + * Converts String list with waypoint_names values to a string ready for API consumption. + * + * @param waypointNames a string representing approaches to each coordinate. + * @return a formatted string. + */ + @Nullable + public static String formatWaypointNames(@Nullable List waypointNames) { + if (waypointNames == null || waypointNames.isEmpty()) { + return null; + } + + return join(";", waypointNames); + } + + /** + * Converts a list of Points to String. + * + * @param coordinates a list of coordinates. + * @return a formatted string. + */ + @Nullable + public static String formatCoordinates(@NonNull List coordinates) { + List coordinatesToJoin = new ArrayList<>(); + for (Point point : coordinates) { + coordinatesToJoin.add(String.format(Locale.US, "%s,%s", + formatCoordinate(point.longitude()), + formatCoordinate(point.latitude()))); + } + + return join(";", coordinatesToJoin); + } + + /** + * Converts array of Points with waypoint_targets values to a string ready for API consumption. + * + * @param points a list representing approaches to each coordinate. + * @return a formatted string. + */ + @Nullable + public static String formatPointsList(@Nullable List points) { + if (points == null || points.isEmpty()) { + return null; + } + + List coordinatesToJoin = new ArrayList<>(); + for (Point point : points) { + if (point == null) { + coordinatesToJoin.add(null); + } else { + coordinatesToJoin.add(String.format(Locale.US, "%s,%s", + formatCoordinate(point.longitude()), + formatCoordinate(point.latitude()))); + } + } + return join(";", coordinatesToJoin); + } +} diff --git a/services-directions-models/src/main/java/com/mapbox/api/directions/v5/utils/ParseUtils.java b/services-directions-models/src/main/java/com/mapbox/api/directions/v5/utils/ParseUtils.java new file mode 100644 index 000000000..7ed57b1f5 --- /dev/null +++ b/services-directions-models/src/main/java/com/mapbox/api/directions/v5/utils/ParseUtils.java @@ -0,0 +1,155 @@ +package com.mapbox.api.directions.v5.utils; + +import androidx.annotation.Nullable; +import com.mapbox.geojson.Point; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Methods to convert Strings to Lists of objects. + */ +public class ParseUtils { + private static final String SEMICOLON = ";"; + private static final String COMMA = ","; + private static final String UNLIMITED = "unlimited"; + + /** + * Parse a String to a list of Integers. + * + * @param original an original String. + * @return List of Integers + */ + @Nullable + public static List parseToIntegers(@Nullable String original) { + if (original == null) { + return null; + } + + List integers = new ArrayList<>(); + String[] strings = original.split(SEMICOLON); + for (String index : strings) { + if (index != null) { + if (index.isEmpty()) { + integers.add(null); + } else { + integers.add(Integer.valueOf(index)); + } + } + } + + return integers; + } + + /** + * Parse a String to a list of Strings. + * + * @param original an original String. + * @return List of Strings + */ + @Nullable + public static List parseToStrings(@Nullable String original) { + if (original == null) { + return null; + } + + List result = new ArrayList<>(); + String[] strings = original.split(SEMICOLON); + for (String str : strings) { + if (str != null) { + if (str.isEmpty()) { + result.add(null); + } else { + result.add(str); + } + } + } + + return result; + } + + /** + * Parse a String to a list of Points. + * + * @param original an original String. + * @return List of Points + */ + @Nullable + public static List parseToPoints(@Nullable String original) { + if (original == null) { + return null; + } + + List points = new ArrayList<>(); + String[] targets = original.split(SEMICOLON); + for (String target : targets) { + if (target != null) { + if (target.isEmpty()) { + points.add(null); + } else { + String[] point = target.split(COMMA); + points.add(Point.fromLngLat(Double.valueOf(point[0]), Double.valueOf(point[1]))); + } + } + } + + return points; + } + + /** + * Parse a String to a list of Points. + * + * @param original an original String. + * @return List of Doubles + */ + @Nullable + public static List parseToDoubles(@Nullable String original) { + if (original == null) { + return null; + } + + List doubles = new ArrayList<>(); + String[] strings = original.split(SEMICOLON); + for (String str : strings) { + if (str != null) { + if (str.isEmpty()) { + doubles.add(null); + } else if (str.equals(UNLIMITED)) { + doubles.add(Double.POSITIVE_INFINITY); + } else { + doubles.add(Double.valueOf(str)); + } + } + } + + return doubles; + } + + /** + * Parse a String to a list of list of Doubles. + * + * @param original an original String. + * @return List of List of Doubles + */ + @Nullable + public static List> parseToListOfListOfDoubles(@Nullable String original) { + if (original == null) { + return null; + } + + List> result = new ArrayList<>(); + String[] pairs = original.split(SEMICOLON); + for (String pair : pairs) { + if (pair.isEmpty()) { + result.add(null); + } else { + String[] values = pair.split(COMMA); + if (values.length == 2) { + result.add(Arrays.asList(Double.valueOf(values[0]), Double.valueOf(values[1]))); + } + } + } + + return result; + } +} diff --git a/services-directions-models/src/main/java/com/mapbox/api/directions/v5/utils/package-info.java b/services-directions-models/src/main/java/com/mapbox/api/directions/v5/utils/package-info.java new file mode 100644 index 000000000..c5b25548b --- /dev/null +++ b/services-directions-models/src/main/java/com/mapbox/api/directions/v5/utils/package-info.java @@ -0,0 +1,5 @@ +/** + * Contains classes with utilities useful for model classes. + * + */ +package com.mapbox.api.directions.v5.utils; diff --git a/services-directions-models/src/test/java/com/mapbox/api/directions/v5/models/RouteOptionsTest.java b/services-directions-models/src/test/java/com/mapbox/api/directions/v5/models/RouteOptionsTest.java index 4836c33f5..5f55aa4de 100644 --- a/services-directions-models/src/test/java/com/mapbox/api/directions/v5/models/RouteOptionsTest.java +++ b/services-directions-models/src/test/java/com/mapbox/api/directions/v5/models/RouteOptionsTest.java @@ -1,52 +1,337 @@ package com.mapbox.api.directions.v5.models; import com.mapbox.geojson.Point; - -import org.junit.Test; - import java.util.ArrayList; import java.util.List; +import org.junit.Test; +import static com.mapbox.api.directions.v5.DirectionsCriteria.ANNOTATION_CONGESTION; +import static com.mapbox.api.directions.v5.DirectionsCriteria.ANNOTATION_DISTANCE; +import static com.mapbox.api.directions.v5.DirectionsCriteria.ANNOTATION_DURATION; +import static com.mapbox.api.directions.v5.DirectionsCriteria.ANNOTATION_MAXSPEED; +import static com.mapbox.api.directions.v5.DirectionsCriteria.ANNOTATION_SPEED; +import static com.mapbox.api.directions.v5.DirectionsCriteria.APPROACH_CURB; +import static com.mapbox.api.directions.v5.DirectionsCriteria.APPROACH_UNRESTRICTED; import static org.junit.Assert.assertEquals; public class RouteOptionsTest { - @Test - public void toBuilder() { - RouteOptions routeOptions = routeOptions(); - - String language = "ru"; - String url = "new_base_url"; - - RouteOptions updatedOptions = routeOptions.toBuilder() - .language(language) - .baseUrl(url) - .build(); - - assertEquals(language, updatedOptions.language()); - assertEquals(url, updatedOptions.baseUrl()); - assertEquals(routeOptions.accessToken(), updatedOptions.accessToken()); - assertEquals(routeOptions.coordinates(), updatedOptions.coordinates()); - assertEquals(routeOptions.user(), updatedOptions.user()); - assertEquals(routeOptions.profile(), updatedOptions.profile()); - assertEquals(routeOptions.geometries(), updatedOptions.geometries()); - assertEquals(routeOptions.requestUuid(), updatedOptions.requestUuid()); - } - - private RouteOptions routeOptions() { - List coordinates = new ArrayList<>(); - coordinates.add(Point.fromLngLat(1.0, 2.0)); - coordinates.add(Point.fromLngLat(3.0, 4.0)); - - return RouteOptions.builder() - .accessToken("token") - .baseUrl("base_url") - .language("en") - .coordinates(coordinates) - .user("user") - .profile("profile") - .geometries("geometries") - .requestUuid("requestUuid") - .build(); - } + @Test + public void toBuilder() { + RouteOptions routeOptions = routeOptions(); + + String language = "ru"; + String url = "new_base_url"; + + RouteOptions updatedOptions = routeOptions.toBuilder() + .language(language) + .baseUrl(url) + .build(); + + assertEquals(language, updatedOptions.language()); + assertEquals(url, updatedOptions.baseUrl()); + assertEquals(routeOptions.accessToken(), updatedOptions.accessToken()); + assertEquals(routeOptions.coordinates(), updatedOptions.coordinates()); + assertEquals(routeOptions.user(), updatedOptions.user()); + assertEquals(routeOptions.profile(), updatedOptions.profile()); + assertEquals(routeOptions.geometries(), updatedOptions.geometries()); + assertEquals(routeOptions.requestUuid(), updatedOptions.requestUuid()); + } + + @Test + public void radiusesString() { + String radiusesStr = ";5.1;;7.4;;"; + + RouteOptions routeOptions = routeOptions() + .toBuilder() + .radiuses(radiusesStr) + .build(); + + assertEquals(";5.1;;7.4", routeOptions.radiuses()); + + List radiuses = routeOptions.radiusesList(); + assertEquals(4, radiuses.size()); + assertEquals(null, radiuses.get(0)); + assertEquals(Double.valueOf(5.1), radiuses.get(1)); + assertEquals(null, radiuses.get(2)); + assertEquals(Double.valueOf(7.4), radiuses.get(3)); + } + + @Test + public void radiusesList() { + List radiuses = new ArrayList<>(); + radiuses.add(null); + radiuses.add(null); + radiuses.add(5.7); + radiuses.add(null); + radiuses.add(4.4); + radiuses.add(9.9); + radiuses.add(null); + radiuses.add(null); + + RouteOptions routeOptions = routeOptions() + .toBuilder() + .radiusesList(radiuses) + .build(); + + assertEquals(radiuses, routeOptions.radiusesList()); + assertEquals(";;5.7;;4.4;9.9;;", routeOptions.radiuses()); + } + + @Test + public void bearingsString() { + String bearingsString = ";5.1,7.4;;"; + + RouteOptions routeOptions = routeOptions() + .toBuilder() + .bearings(bearingsString) + .build(); + + List bearing = new ArrayList<>(); + bearing.add(5.1); + bearing.add(7.4); + + assertEquals(";5.1,7.4", routeOptions.bearings()); + + List> bearings = routeOptions.bearingsList(); + assertEquals(2, bearings.size()); + assertEquals(null, bearings.get(0)); + assertEquals(bearing, bearings.get(1)); + } + + @Test + public void bearingsList() { + List bearing1 = new ArrayList<>(); + bearing1.add(1.1); + bearing1.add(2.2); + + List bearing2 = new ArrayList<>(); + bearing2.add(7.7); + bearing2.add(8.8); + + List> bearings = new ArrayList<>(); + bearings.add(null); + bearings.add(null); + bearings.add(bearing1); + bearings.add(bearing2); + bearings.add(null); + + RouteOptions routeOptions = routeOptions() + .toBuilder() + .bearingsList(bearings) + .build(); + + assertEquals(bearings, routeOptions.bearingsList()); + assertEquals(";;1.1,2.2;7.7,8.8;", routeOptions.bearings()); + } + + @Test + public void approachesString() { + String approachesStr = ";" + APPROACH_CURB + ";" + ";" + APPROACH_UNRESTRICTED + ";"; + + RouteOptions routeOptions = routeOptions() + .toBuilder() + .approaches(approachesStr) + .build(); + + assertEquals(";" + APPROACH_CURB + ";" + ";" + APPROACH_UNRESTRICTED, + routeOptions.approaches()); + + List approaches = routeOptions.approachesList(); + assertEquals(4, approaches.size()); + assertEquals(null, approaches.get(0)); + assertEquals(APPROACH_CURB, approaches.get(1)); + assertEquals(null, approaches.get(2)); + assertEquals(APPROACH_UNRESTRICTED, approaches.get(3)); + } + + @Test + public void approachesList() { + List approaches = new ArrayList<>(); + approaches.add(APPROACH_CURB); + approaches.add(null); + approaches.add(null); + approaches.add(APPROACH_UNRESTRICTED); + approaches.add(APPROACH_UNRESTRICTED); + approaches.add(null); + approaches.add(null); + approaches.add(APPROACH_CURB); + approaches.add(null); + + RouteOptions routeOptions = routeOptions() + .toBuilder() + .approachesList(approaches) + .build(); + + assertEquals(approaches, routeOptions.approachesList()); + assertEquals("curb;;;unrestricted;unrestricted;;;curb;", routeOptions.approaches()); + } + + @Test + public void waypointIndicesString() { + String indicesStr = "1;4;5;7;8"; + + RouteOptions routeOptions = routeOptions() + .toBuilder() + .waypointIndices(indicesStr) + .build(); + + assertEquals("1;4;5;7;8", routeOptions.waypointIndices()); + + List indices = routeOptions.waypointIndicesList(); + assertEquals(5, indices.size()); + assertEquals(Integer.valueOf(1), indices.get(0)); + assertEquals(Integer.valueOf(4), indices.get(1)); + assertEquals(Integer.valueOf(5), indices.get(2)); + assertEquals(Integer.valueOf(7), indices.get(3)); + assertEquals(Integer.valueOf(8), indices.get(4)); + } + + @Test + public void waypointIndicesList() { + List indices = new ArrayList<>(); + indices.add(1); + indices.add(5); + indices.add(7); + + RouteOptions routeOptions = routeOptions() + .toBuilder() + .waypointIndicesList(indices) + .build(); + + assertEquals(indices, routeOptions.waypointIndicesList()); + assertEquals("1;5;7", routeOptions.waypointIndices()); + } + + @Test + public void waypointNamesString() { + String namesStr = "ab;;;cd;ef;;;gh;ij"; + + RouteOptions routeOptions = routeOptions() + .toBuilder() + .waypointNames(namesStr) + .build(); + + assertEquals(namesStr, routeOptions.waypointNames()); + + List names = routeOptions.waypointNamesList(); + assertEquals("ab", names.get(0)); + assertEquals(null, names.get(1)); + assertEquals(null, names.get(2)); + assertEquals("cd", names.get(3)); + assertEquals("ef", names.get(4)); + assertEquals(null, names.get(5)); + assertEquals(null, names.get(6)); + assertEquals("gh", names.get(7)); + assertEquals("ij", names.get(8)); + } + + @Test + public void waypointNamesList() { + List names = new ArrayList<>(); + names.add(null); + names.add(null); + names.add("abc"); + names.add(null); + names.add("def"); + names.add(null); + names.add(null); + + RouteOptions routeOptions = routeOptions() + .toBuilder() + .waypointNamesList(names) + .build(); + + assertEquals(names, routeOptions.waypointNamesList()); + assertEquals(";;abc;;def;;", routeOptions.waypointNames()); + } + + @Test + public void waypointTargetsString() { + String targetsStr = "1.2,3.4;;;5.65,7.123;;;"; + + RouteOptions routeOptions = routeOptions() + .toBuilder() + .waypointTargets(targetsStr) + .build(); + + assertEquals("1.2,3.4;;;5.65,7.123", routeOptions.waypointTargets()); + + List targets = routeOptions.waypointTargetsList(); + assertEquals(4, targets.size()); + assertEquals(Point.fromLngLat(1.2, 3.4), targets.get(0)); + assertEquals(null, targets.get(1)); + assertEquals(null, targets.get(2)); + assertEquals(Point.fromLngLat(5.65, 7.123), targets.get(3)); + } + + @Test + public void waypointTargetsList() { + List targets = new ArrayList<>(); + targets.add(null); + targets.add(null); + targets.add(Point.fromLngLat(5.55, 7.77)); + targets.add(Point.fromLngLat(1.22, 3.44)); + targets.add(null); + targets.add(Point.fromLngLat(1.55, 8.99)); + + RouteOptions routeOptions = routeOptions() + .toBuilder() + .waypointTargetsList(targets) + .build(); + + assertEquals(targets, routeOptions.waypointTargetsList()); + assertEquals(";;5.55,7.77;1.22,3.44;;1.55,8.99", routeOptions.waypointTargets()); + } + + @Test + public void annotationsString() { + String annotationsStr = ANNOTATION_MAXSPEED + ";" + ANNOTATION_DURATION + ";" + ";"; + + RouteOptions routeOptions = routeOptions() + .toBuilder() + .annotations(annotationsStr) + .build(); + + assertEquals(ANNOTATION_MAXSPEED + ";" + ANNOTATION_DURATION, routeOptions.annotations()); + + List annotations = routeOptions.annotationsList(); + assertEquals(2, annotations.size()); + assertEquals(ANNOTATION_MAXSPEED, annotations.get(0)); + assertEquals(ANNOTATION_DURATION, annotations.get(1)); + } + + @Test + public void annotationsList() { + List annotations = new ArrayList<>(); + annotations.add(ANNOTATION_CONGESTION); + annotations.add(ANNOTATION_DISTANCE); + annotations.add(ANNOTATION_MAXSPEED); + annotations.add(ANNOTATION_SPEED); + annotations.add(null); + annotations.add(null); + + RouteOptions routeOptions = routeOptions() + .toBuilder() + .annotationsList(annotations) + .build(); + + assertEquals(annotations, routeOptions.annotationsList()); + assertEquals("congestion;distance;maxspeed;speed", routeOptions.annotations()); + } + + private RouteOptions routeOptions() { + List coordinates = new ArrayList<>(); + coordinates.add(Point.fromLngLat(1.0, 2.0)); + coordinates.add(Point.fromLngLat(3.0, 4.0)); + + return RouteOptions.builder() + .accessToken("token") + .baseUrl("base_url") + .coordinates(coordinates) + .user("user") + .profile("profile") + .requestUuid("requestUuid") + .build(); + } } diff --git a/services-directions/src/main/java/com/mapbox/api/directions/v5/DirectionsResponseFactory.java b/services-directions/src/main/java/com/mapbox/api/directions/v5/DirectionsResponseFactory.java index 2a4eab099..a11a30161 100644 --- a/services-directions/src/main/java/com/mapbox/api/directions/v5/DirectionsResponseFactory.java +++ b/services-directions/src/main/java/com/mapbox/api/directions/v5/DirectionsResponseFactory.java @@ -3,6 +3,7 @@ import com.mapbox.api.directions.v5.models.DirectionsResponse; import com.mapbox.api.directions.v5.models.DirectionsRoute; import com.mapbox.api.directions.v5.models.RouteOptions; +import com.mapbox.api.directions.v5.utils.ParseUtils; import java.util.ArrayList; import java.util.List; @@ -54,16 +55,16 @@ private List generateRouteOptions(Response RouteOptions.builder() .profile(mapboxDirections.profile()) .coordinates(mapboxDirections.coordinates()) - .waypointIndices(mapboxDirections.waypointIndices()) - .waypointNames(mapboxDirections.waypointNames()) - .waypointTargets(mapboxDirections.waypointTargets()) + .waypointIndicesList(ParseUtils.parseToIntegers(mapboxDirections.waypointIndices())) + .waypointNamesList(ParseUtils.parseToStrings(mapboxDirections.waypointNames())) + .waypointTargetsList(ParseUtils.parseToPoints(mapboxDirections.waypointTargets())) .continueStraight(mapboxDirections.continueStraight()) .annotations(mapboxDirections.annotation()) - .approaches(mapboxDirections.approaches()) - .bearings(mapboxDirections.bearing()) + .approachesList(ParseUtils.parseToStrings(mapboxDirections.approaches())) + .bearingsList(ParseUtils.parseToListOfListOfDoubles(mapboxDirections.bearing())) .alternatives(mapboxDirections.alternatives()) .language(mapboxDirections.language()) - .radiuses(mapboxDirections.radius()) + .radiusesList(ParseUtils.parseToDoubles(mapboxDirections.radius())) .user(mapboxDirections.user()) .voiceInstructions(mapboxDirections.voiceInstructions()) .bannerInstructions(mapboxDirections.bannerInstructions()) diff --git a/services-directions/src/main/java/com/mapbox/api/directions/v5/MapboxDirections.java b/services-directions/src/main/java/com/mapbox/api/directions/v5/MapboxDirections.java index 4484253e9..05daa70a3 100644 --- a/services-directions/src/main/java/com/mapbox/api/directions/v5/MapboxDirections.java +++ b/services-directions/src/main/java/com/mapbox/api/directions/v5/MapboxDirections.java @@ -15,16 +15,17 @@ import com.mapbox.api.directions.v5.DirectionsCriteria.VoiceUnitCriteria; import com.mapbox.api.directions.v5.models.DirectionsResponse; import com.mapbox.api.directions.v5.models.RouteLeg; +import com.mapbox.api.directions.v5.utils.FormatUtils; import com.mapbox.core.MapboxService; import com.mapbox.core.constants.Constants; import com.mapbox.core.exceptions.ServicesException; import com.mapbox.core.utils.ApiCallHelper; import com.mapbox.core.utils.MapboxUtils; -import com.mapbox.core.utils.TextUtils; import com.mapbox.geojson.Point; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Locale; @@ -87,7 +88,7 @@ private Call get() { ApiCallHelper.getHeaderUserAgent(clientAppName()), user(), profile(), - formatCoordinates(coordinates()), + FormatUtils.formatCoordinates(coordinates()), accessToken(), alternatives(), geometries(), @@ -119,7 +120,7 @@ private Call post() { ApiCallHelper.getHeaderUserAgent(clientAppName()), user(), profile(), - formatCoordinates(coordinates()), + FormatUtils.formatCoordinates(coordinates()), accessToken(), alternatives(), geometries(), @@ -219,41 +220,6 @@ protected synchronized OkHttpClient getOkHttpClient() { return okHttpClient; } - private static String formatCoordinates(List coordinates) { - String[] coordinatesFormatted = new String[coordinates.size()]; - int index = 0; - for (Point point : coordinates) { - coordinatesFormatted[index++] = String.format(Locale.US, "%s,%s", - TextUtils.formatCoordinate(point.longitude()), - TextUtils.formatCoordinate(point.latitude())); - } - - return TextUtils.join(";", coordinatesFormatted); - } - - /** - * Converts array of Points with waypoint_targets values - * to a string ready for API consumption. - * - * @param waypointTargets a string representing approaches to each coordinate. - * @return a formatted string. - * @since 4.3.0 - */ - private static String formatWaypointTargets(Point[] waypointTargets) { - String[] coordinatesFormatted = new String[waypointTargets.length]; - int index = 0; - for (Point target : waypointTargets) { - if (target == null) { - coordinatesFormatted[index++] = ""; - } else { - coordinatesFormatted[index++] = String.format(Locale.US, "%s,%s", - TextUtils.formatCoordinate(target.longitude()), - TextUtils.formatCoordinate(target.latitude())); - } - } - return TextUtils.join(";", coordinatesFormatted); - } - @NonNull abstract String user(); @@ -416,17 +382,17 @@ public static Builder builder() { */ @AutoValue.Builder public abstract static class Builder { - - private List bearings = new ArrayList<>(); + //TODO change List to a custom model or to a Pair + private List> bearings = new ArrayList<>(); private List coordinates = new ArrayList<>(); - private String[] annotations; - private double[] radiuses; + private List annotations = new ArrayList<>(); + private List radiuses = new ArrayList<>(); private Point destination; private Point origin; - private String[] approaches; - private Integer[] waypointIndices; - private String[] waypointNames; - private Point[] waypointTargets; + private List approaches = new ArrayList<>(); + private List waypointIndices = new ArrayList<>(); + private List waypointNames = new ArrayList<>(); + private List waypointTargets = new ArrayList<>(); /** * The username for the account that the directions engine runs on. In most cases, this should @@ -484,8 +450,7 @@ public Builder destination(@NonNull Point destination) { * in the request is currently limited to 1. * * @param waypoint a {@link Point} which represents the pit-stop or waypoint where you'd like - * one of the {@link RouteLeg} to - * navigate the user to + * one of the {@link RouteLeg} to navigate the user to * @return this builder for chaining options together * @since 3.0.0 */ @@ -494,6 +459,21 @@ public Builder addWaypoint(@NonNull Point waypoint) { return this; } + /** + * This can be used to set up to 23 additional in-between points which will act as pit-stops + * along the users route. Note that if you are using the + * {@link DirectionsCriteria#PROFILE_DRIVING_TRAFFIC} that the max number of waypoints allowed + * in the request is currently limited to 1. + * + * @param waypoints a list which represents the pit-stops or waypoints where + * you'd like one of the {@link RouteLeg} to navigate the user to + * @return this builder for chaining options together + */ + public Builder waypoints(@NonNull List waypoints) { + coordinates = waypoints; + return this; + } + /** * Optionally set whether to try to return alternative routes. An alternative is classified as a * route that is significantly different then the fastest route, but also still reasonably fast. @@ -597,7 +577,6 @@ public Builder language(@Nullable Locale language) { /** * Whether or not to return additional metadata along the route. Possible values are: * {@link DirectionsCriteria#ANNOTATION_DISTANCE}, - * {@link DirectionsCriteria#ANNOTATION_DURATION}, * {@link DirectionsCriteria#ANNOTATION_DURATION} and * {@link DirectionsCriteria#ANNOTATION_CONGESTION}. Several annotation can be used by * separating them with {@code ,}. @@ -609,18 +588,50 @@ public Builder language(@Nullable Locale language) { * @see RouteLeg object * documentation * @since 2.1.0 + * @deprecated use {@link #annotations(List)} + */ + @Deprecated + public Builder annotations(@AnnotationCriteria @NonNull String... annotations) { + return annotations(Arrays.asList(annotations)); + } + + /** + * Whether or not to return additional metadata along the route. Possible values are: + * {@link DirectionsCriteria#ANNOTATION_DISTANCE}, + * {@link DirectionsCriteria#ANNOTATION_DURATION} and + * {@link DirectionsCriteria#ANNOTATION_CONGESTION} + * + * @param annotations a list of annotations + * @return this builder for chaining options together */ - public Builder annotations(@Nullable @AnnotationCriteria String... annotations) { + public Builder annotations(@NonNull List annotations) { this.annotations = annotations; return this; } + /** + * Whether or not to return additional metadata along the route. Possible values are: + * {@link DirectionsCriteria#ANNOTATION_DISTANCE}, + * {@link DirectionsCriteria#ANNOTATION_DURATION} and + * {@link DirectionsCriteria#ANNOTATION_CONGESTION} + * + * @param annotation string referencing one of the annotation direction criteria's. + * @return this builder for chaining options together + * @see RouteLeg object + * documentation + * @since 2.1.0 + */ + public Builder addAnnotation(@AnnotationCriteria @NonNull String annotation) { + this.annotations.add(annotation); + return this; + } + abstract Builder annotation(@Nullable String annotation); /** * Optionally, Use to filter the road segment the waypoint will be placed on by direction and * dictates the angle of approach. This option should always be used in conjunction with the - * {@link #radiuses(double...)} parameter. + * {@link #radiuses} parameter. *

* The parameter takes two values per waypoint: the first is an angle clockwise from true north * between 0 and 360. The second is the range of degrees the angle can deviate by. We recommend @@ -648,13 +659,47 @@ public Builder annotations(@Nullable @AnnotationCriteria String... annotations) public Builder addBearing(@Nullable @FloatRange(from = 0, to = 360) Double angle, @Nullable @FloatRange(from = 0, to = 360) Double tolerance) { if (angle == null || tolerance == null) { - bearings.add(new Double[0]); + bearings.add(new ArrayList()); } else { - bearings.add(new Double[] {angle, tolerance}); + bearings.add(Arrays.asList(angle, tolerance)); } return this; } + /** + * Optionally, Use to filter the road segment the waypoint will be placed on by direction and + * dictates the angle of approach. This option should always be used in conjunction with the + * {@link #radiuses} parameter. + * + * @param bearings a list of list of doubles. Every list has two values: + * the first is an angle clockwise from true north between 0 and 360. The + * second is the range of degrees the angle can deviate by. We recommend + * a value of 45 degrees or 90 degrees for the range, as bearing measurements + * tend to be inaccurate. + * @return this builder for chaining options together + */ + public Builder bearings(@NonNull List> bearings) { + List> newBearings = new ArrayList<>(); + for (List bearing : bearings) { + if (bearing.size() != 2) { + throw new ServicesException("Bearing size should be 2."); + } + Double angle = bearing.get(0); + Double tolerance = bearing.get(1); + if (angle == null || tolerance == null) { + newBearings.add(new ArrayList()); + } else { + if (angle < 0 || angle > 360 || tolerance < 0 || tolerance > 360) { + throw new ServicesException("Angle and tolerance have to be from 0 to 360."); + } + newBearings.add(Arrays.asList(angle, tolerance)); + } + } + + this.bearings = newBearings; + return this; + } + abstract Builder bearing(@Nullable String bearings); /** @@ -669,12 +714,48 @@ public Builder addBearing(@Nullable @FloatRange(from = 0, to = 360) Double angle * @param radiuses double array containing the radiuses defined in unit meters. * @return this builder for chaining options together * @since 1.0.0 + * @deprecated use {@link #radiuses(List)} */ - public Builder radiuses(@FloatRange(from = 0) double... radiuses) { + @Deprecated + public Builder radiuses(@NonNull @FloatRange(from = 0) Double... radiuses) { + return radiuses(Arrays.asList(radiuses)); + } + + /** + * Optionally, set the maximum distance in meters that each coordinate is allowed to move when + * snapped to a nearby road segment. There must be as many radiuses as there are coordinates in + * the request. Values can be any number greater than 0 or they can be unlimited simply by + * passing {@link Double#POSITIVE_INFINITY}. + *

+ * If no routable road is found within the radius, a {@code NoSegment} error is returned. + *

+ * + * @param radiuses a list with radiuses defined in unit meters. + * @return this builder for chaining options together + */ + public Builder radiuses(@NonNull List radiuses) { this.radiuses = radiuses; return this; } + /** + * Optionally, set the maximum distance in meters that each coordinate is allowed to move when + * snapped to a nearby road segment. There must be as many radiuses as there are coordinates in + * the request. Values can be any number greater than 0 or they can be unlimited simply by + * passing {@link Double#POSITIVE_INFINITY}. + *

+ * If no routable road is found within the radius, a {@code NoSegment} error is returned. + *

+ * + * @param radius double param defined in unit meters. + * @return this builder for chaining options together + * @since 1.0.0 + */ + public Builder addRadius(@NonNull @FloatRange(from = 0) Double radius) { + this.radiuses.add(radius); + return this; + } + abstract Builder radius(@Nullable String radiuses); /** @@ -793,29 +874,87 @@ public Builder radiuses(@FloatRange(from = 0) double... radiuses) { * {@link com.mapbox.api.directions.v5.DirectionsCriteria.ApproachesCriteria}. * @return this builder for chaining options together * @since 3.2.0 + * @deprecated use {@link #approaches(List)} */ - public Builder addApproaches(String... approaches) { + @Deprecated + public Builder addApproaches(@NonNull String... approaches) { + return approaches(Arrays.asList(approaches)); + } + + /** + * Indicates from which side of the road to approach a waypoint. + * Accepts unrestricted (default), curb or null. + * If set to unrestricted , the route can approach waypoints + * from either side of the road. If set to curb , the route will be returned + * so that on arrival, the waypoint will be found on the side that corresponds with the + * driving_side of the region in which the returned route is located. + * If provided, the list of approaches must be the same length as the list of waypoints. + * + * @param approaches empty if you'd like the default approaches, + * else one of the options found in + * {@link com.mapbox.api.directions.v5.DirectionsCriteria.ApproachesCriteria}. + * @return this builder for chaining options together + */ + public Builder approaches(@NonNull List approaches) { this.approaches = approaches; return this; } abstract Builder approaches(@Nullable String approaches); + /** + * Indicates from which side of the road to approach a waypoint. + * Accepts unrestricted (default), curb or null. + * If set to unrestricted , the route can approach waypoints + * from either side of the road. If set to curb , the route will be returned + * so that on arrival, the waypoint will be found on the side that corresponds with the + * driving_side of the region in which the returned route is located. + * If provided, the list of approaches must be the same length as the list of waypoints. + * + * @param approach null if you'd like the default approaches, else one of the options found in + * {@link com.mapbox.api.directions.v5.DirectionsCriteria.ApproachesCriteria}. + * @return this builder for chaining options together + * @since 3.2.0 + */ + public Builder addApproach(@Nullable String approach) { + this.approaches.add(approach); + return this; + } + /** * Optionally, set which input coordinates should be treated as waypoints / separate legs. * Note: coordinate indices not added here act as silent waypoints *

* Most useful in combination with steps=true and requests based on traces * with high sample rates. Can be an index corresponding to any of the input coordinates, - * but must contain the first ( 0 ) and last coordinates' index separated by ; . + * but must contain the first ( 0 ) and last coordinates' indices. * {@link #steps()} *

* * @param waypointIndices integer array of coordinate indices to be used as waypoints * @return this builder for chaining options together * @since 4.4.0 + * @deprecated use {@link #waypointIndices(List)} + */ + @Deprecated + public Builder addWaypointIndices(@NonNull @IntRange(from = 0) Integer... waypointIndices) { + return waypointIndices(Arrays.asList(waypointIndices)); + } + + /** + * Optionally, set which input coordinates should be treated as waypoints / separate legs. + * Note: coordinate indices not added here act as silent waypoints + *

+ * Most useful in combination with steps=true and requests based on traces + * with high sample rates. Can be an index corresponding to any of the input coordinates, + * but must contain the first ( 0 ) and last coordinates' indices. + * {@link #steps()} + *

+ * + * @param waypointIndices a list of coordinate indices to be used as waypoints + * @return this builder for chaining options together */ - public Builder addWaypointIndices(@Nullable @IntRange(from = 0) Integer... waypointIndices) { + public Builder waypointIndices(@NonNull List waypointIndices) { this.waypointIndices = waypointIndices; return this; } @@ -823,16 +962,50 @@ public Builder addWaypointIndices(@Nullable @IntRange(from = 0) Integer... waypo abstract Builder waypointIndices(@Nullable String waypointIndices); /** - * Custom names for waypoints used for the arrival instruction, - * each separated by ; . Values can be any string and total number of all characters cannot - * exceed 500. If provided, the list of waypointNames must be the same length as the list of - * coordinates, but you can skip a coordinate and show its position with the ; separator. + * Optionally, set which input coordinates should be treated as waypoints / separate legs. + * Note: coordinate indices not added here act as silent waypoints + *

+ * Most useful in combination with steps=true and requests based on traces + * with high sample rates. Can be an index corresponding to any of the input coordinates, + * but must contain the first ( 0 ) and last coordinates' indices. + * {@link #steps()} + *

+ * + * @param waypointIndex integer index to be used as waypoint + * @return this builder for chaining options together + * @since 4.4.0 + */ + public Builder addWaypointIndex(@NonNull @IntRange(from = 0) Integer waypointIndex) { + this.waypointIndices.add(waypointIndex); + return this; + } + + /** + * Custom names for waypoints used for the arrival instruction. + * Values can be any string and total number of all characters cannot exceed 500. + * If provided, the list of waypointNames must be the same length as the list of + * coordinates, but you can skip a coordinate and show its position with the null value. * * @param waypointNames Custom names for waypoints used for the arrival instruction. * @return this builder for chaining options together * @since 3.3.0 + * @deprecated use {@link #waypointNames(List)} */ - public Builder addWaypointNames(@Nullable String... waypointNames) { + @Deprecated + public Builder addWaypointNames(@NonNull String... waypointNames) { + return waypointNames(Arrays.asList(waypointNames)); + } + + /** + * Custom names for waypoints used for the arrival instruction. + * Values can be any string and total number of all characters cannot exceed 500. + * If provided, the list of waypointNames must be the same length as the list of + * coordinates, but you can skip a coordinate and show its position with the null value. + * + * @param waypointNames a list of custom names for waypoints used for the arrival instruction. + * @return this builder for chaining options together + */ + public Builder waypointNames(@NonNull List waypointNames) { this.waypointNames = waypointNames; return this; } @@ -840,8 +1013,23 @@ public Builder addWaypointNames(@Nullable String... waypointNames) { abstract Builder waypointNames(@Nullable String waypointNames); /** - * A list of coordinate points used to specify drop-off locations - * that are distinct from the locations specified in coordinates. + * Custom names for waypoints used for the arrival instruction. + * Values can be any string and total number of all characters cannot exceed 500. + * If provided, the list of waypointNames must be the same length as the list of + * coordinates, but you can skip a coordinate and show its position with the null value. + * + * @param waypointName Custom name for waypoint used for the arrival instruction. + * @return this builder for chaining options together + * @since 3.3.0 + */ + public Builder addWaypointName(@Nullable String waypointName) { + this.waypointNames.add(waypointName); + return this; + } + + /** + * Points to specify drop-off locations that are distinct from the locations specified in + * coordinates. * The number of waypoint targets must be the same as the number of coordinates, * but you can skip a coordinate with a null value. * Must be used with steps=true. @@ -849,14 +1037,46 @@ public Builder addWaypointNames(@Nullable String... waypointNames) { * @param waypointTargets list of coordinate points for drop-off locations * @return this builder for chaining options together * @since 4.3.0 + * @deprecated use {@link #waypointTargets(List)} + */ + @Deprecated + public Builder addWaypointTargets(@NonNull Point... waypointTargets) { + return waypointTargets(Arrays.asList(waypointTargets)); + } + + /** + * A list of points used to specify drop-off locations that are distinct from the locations + * specified in coordinates. + * The number of waypoint targets must be the same as the number of coordinates, + * but you can skip a coordinate with a null value. + * Must be used with steps=true. + * + * @param waypointTargets list of coordinate points for drop-off locations + * @return this builder for chaining options together */ - public Builder addWaypointTargets(@Nullable Point... waypointTargets) { + public Builder waypointTargets(@NonNull List waypointTargets) { this.waypointTargets = waypointTargets; return this; } abstract Builder waypointTargets(@Nullable String waypointTargets); + /** + * A point to specify drop-off locations that are distinct from the locations specified in + * coordinates. + * The number of waypoint targets must be the same as the number of coordinates, + * but you can skip a coordinate with a null value. + * Must be used with steps=true. + * + * @param waypointTarget a point for drop-off locations + * @return this builder for chaining options together + * @since 4.3.0 + */ + public Builder addWaypointTarget(@Nullable Point waypointTarget) { + this.waypointTargets.add(waypointTarget); + return this; + } + /** * Whether the routes should be refreshable via the directions refresh API. * @@ -925,45 +1145,45 @@ public MapboxDirections build() { + " directions API request."); } - if (waypointIndices != null) { - if (waypointIndices.length < 2) { + if (!waypointIndices.isEmpty()) { + if (waypointIndices.size() < 2) { throw new ServicesException( "Waypoints must be a list of at least two indexes separated by ';'"); } - if (waypointIndices[0] != 0 || waypointIndices[waypointIndices.length - 1] + if (waypointIndices.get(0) != 0 || waypointIndices.get(waypointIndices.size() - 1) != coordinates.size() - 1) { throw new ServicesException( "Waypoints must contain indices of the first and last coordinates" ); } - for (int i = 1; i < waypointIndices.length - 1; i++) { - if (waypointIndices[i] < 0 || waypointIndices[i] >= coordinates.size()) { + for (int i = 1; i < waypointIndices.size() - 1; i++) { + if (waypointIndices.get(i) < 0 || waypointIndices.get(i) >= coordinates.size()) { throw new ServicesException( "Waypoints index too large (no corresponding coordinate)"); } } } - if (waypointNames != null) { - final String waypointNamesStr = TextUtils.formatWaypointNames(waypointNames); + if (!waypointNames.isEmpty()) { + final String waypointNamesStr = FormatUtils.formatWaypointNames(waypointNames); waypointNames(waypointNamesStr); } - if (waypointTargets != null) { - if (waypointTargets.length != coordinates.size()) { + if (!waypointTargets.isEmpty()) { + if (waypointTargets.size() != coordinates.size()) { throw new ServicesException("Number of waypoint targets must match " + " the number of waypoints provided."); } - waypointTargets(formatWaypointTargets(waypointTargets)); + waypointTargets(FormatUtils.formatPointsList(waypointTargets)); } - if (approaches != null) { - if (approaches.length != coordinates.size()) { + if (!approaches.isEmpty()) { + if (approaches.size() != coordinates.size()) { throw new ServicesException("Number of approach elements must match " + "number of coordinates provided."); } - String formattedApproaches = TextUtils.formatApproaches(approaches); + String formattedApproaches = FormatUtils.formatApproaches(approaches); if (formattedApproaches == null) { throw new ServicesException("All approaches values must be one of curb, unrestricted"); } @@ -971,10 +1191,10 @@ public MapboxDirections build() { } coordinates(coordinates); - bearing(TextUtils.formatBearing(bearings)); - annotation(TextUtils.join(",", annotations)); - radius(TextUtils.formatRadiuses(radiuses)); - waypointIndices(TextUtils.join(";", waypointIndices)); + bearing(FormatUtils.formatBearings(bearings)); + annotation(FormatUtils.join(",", annotations)); + radius(FormatUtils.formatRadiuses(radiuses)); + waypointIndices(FormatUtils.join(";", waypointIndices, true)); MapboxDirections directions = autoBuild(); diff --git a/services-directions/src/test/java/com/mapbox/api/directions/v5/MapboxDirectionsTest.java b/services-directions/src/test/java/com/mapbox/api/directions/v5/MapboxDirectionsTest.java index 73a2c7f0d..045a13b1e 100644 --- a/services-directions/src/test/java/com/mapbox/api/directions/v5/MapboxDirectionsTest.java +++ b/services-directions/src/test/java/com/mapbox/api/directions/v5/MapboxDirectionsTest.java @@ -11,21 +11,12 @@ import com.mapbox.core.TestUtils; import com.mapbox.core.exceptions.ServicesException; import com.mapbox.geojson.Point; - -import org.hamcrest.core.StringStartsWith; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.Random; - import okhttp3.Call; import okhttp3.EventListener; import okhttp3.HttpUrl; @@ -33,6 +24,13 @@ import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; +import org.hamcrest.core.StringStartsWith; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; import retrofit2.Response; import static com.mapbox.api.directions.v5.DirectionsCriteria.APPROACH_CURB; @@ -168,6 +166,22 @@ public void build_coordinatesListCreatedInCorrectOrder() throws Exception { .contains("1.234,2.345;5.29838,4.42189;13.493,9.958")); } + @Test + public void build_coordinatesCreatedInCorrectOrder() throws Exception { + List waypoints = new ArrayList<>(); + waypoints.add( Point.fromLngLat(5.29838, 4.42189)); + waypoints.add(Point.fromLngLat(5.11111, 4.22222)); + + MapboxDirections directions = MapboxDirections.builder() + .origin(Point.fromLngLat(1.234, 2.345)) + .destination(Point.fromLngLat(13.4930, 9.958)) + .waypoints(waypoints) + .accessToken(ACCESS_TOKEN) + .build(); + assertTrue(directions.cloneCall().request().url().toString() + .contains("1.234,2.345;5.29838,4.42189;5.11111,4.22222;13.493,9.958")); + } + @Test public void build_originDestinationGetAddedToListCorrectly() throws Exception { MapboxDirections directions = MapboxDirections.builder() @@ -272,17 +286,79 @@ public void addWaypoint_doesGetFormattedInUrlCorrectly() throws Exception { @Test public void waypoints_doesGetFormattedInUrlCorrectly() { + List indices = new ArrayList<>(); + indices.add(0); + indices.add(2); + MapboxDirections directions = MapboxDirections.builder() .destination(Point.fromLngLat(13.4930, 9.958)) .addWaypoint(Point.fromLngLat(4.56, 7.89)) .origin(Point.fromLngLat(1.234, 2.345)) - .addWaypointIndices(0, 2) + .waypointIndices(indices) .accessToken(ACCESS_TOKEN) .build(); String semicolon = "%3B"; assertTrue(directions.cloneCall().request().url().toString().contains("waypoints=0" + semicolon + "2")); } + @Test + public void waypointsList_doesGetFormattedInUrlCorrectly() { + List waypointIndices = new ArrayList<>(); + waypointIndices.add(0); + waypointIndices.add(2); + + MapboxDirections directions = MapboxDirections.builder() + .destination(Point.fromLngLat(13.4930, 9.958)) + .addWaypoint(Point.fromLngLat(4.56, 7.89)) + .origin(Point.fromLngLat(1.234, 2.345)) + .waypointIndices(waypointIndices) + .accessToken(ACCESS_TOKEN) + .build(); + String semicolon = "%3B"; + assertTrue(directions.cloneCall().request().url().toString().contains("waypoints=0" + semicolon + "2")); + } + + @Test + public void waypointList_doesGetFormattedInUrlCorrectly() { + List waypointIndices = new ArrayList<>(); + waypointIndices.add(0); + waypointIndices.add(2); + + MapboxDirections directions = MapboxDirections.builder() + .destination(Point.fromLngLat(13.4930, 9.958)) + .addWaypoint(Point.fromLngLat(4.56, 7.89)) + .origin(Point.fromLngLat(1.234, 2.345)) + .addWaypoint(Point.fromLngLat(4.56, 7.89)) + .waypointIndices(waypointIndices) + .addWaypointIndex(3) + .accessToken(ACCESS_TOKEN) + .build(); + + assertEquals("0;2;3", directions.cloneCall().request().url().queryParameter("waypoints")); + } + + @Test + public void waypointListRemoveAndAdd_doesGetFormattedInUrlCorrectly() { + List waypointIndices = new ArrayList<>(); + waypointIndices.add(0); + waypointIndices.add(2); + + MapboxDirections directions = MapboxDirections.builder() + .destination(Point.fromLngLat(13.4930, 9.958)) + .addWaypoint(Point.fromLngLat(4.56, 7.89)) + .origin(Point.fromLngLat(1.234, 2.345)) + .addWaypoint(Point.fromLngLat(4.56, 7.89)) + .waypointIndices(waypointIndices) + .waypointIndices(new ArrayList()) + .addWaypointIndex(0) + .addWaypointIndex(1) + .addWaypointIndex(3) + .accessToken(ACCESS_TOKEN) + .build(); + + assertEquals("0;1;3", directions.cloneCall().request().url().queryParameter("waypoints")); + } + @Test public void alternatives_doesGetFormattedInUrlCorrectly() throws Exception { MapboxDirections directions = MapboxDirections.builder() @@ -355,7 +431,8 @@ public void annotations_doesGetFormattedInUrlCorrectly() throws Exception { MapboxDirections directions = MapboxDirections.builder() .destination(Point.fromLngLat(13.4930, 9.958)) .origin(Point.fromLngLat(1.234, 2.345)) - .annotations(DirectionsCriteria.ANNOTATION_CONGESTION, DirectionsCriteria.ANNOTATION_DURATION) + .addAnnotation(DirectionsCriteria.ANNOTATION_CONGESTION) + .addAnnotation(DirectionsCriteria.ANNOTATION_DURATION) .overview(DirectionsCriteria.OVERVIEW_FULL) .accessToken(ACCESS_TOKEN) .build(); @@ -363,6 +440,23 @@ public void annotations_doesGetFormattedInUrlCorrectly() throws Exception { directions.cloneCall().request().url().queryParameter("annotations")); } + @Test + public void annotationsList_doesGetFormattedInUrlCorrectly() throws Exception { + List annotations = new ArrayList<>(); + annotations.add(DirectionsCriteria.ANNOTATION_CONGESTION); + annotations.add(DirectionsCriteria.ANNOTATION_DURATION); + + MapboxDirections directions = MapboxDirections.builder() + .destination(Point.fromLngLat(13.4930, 9.958)) + .origin(Point.fromLngLat(1.234, 2.345)) + .annotations(annotations) + .overview(DirectionsCriteria.OVERVIEW_FULL) + .accessToken(ACCESS_TOKEN) + .build(); + assertEquals("congestion,duration", + directions.cloneCall().request().url().queryParameter("annotations")); + } + @Test public void addBearing_doesGetFormattedInUrlCorrectly() throws Exception { MapboxDirections directions = MapboxDirections.builder() @@ -376,18 +470,54 @@ public void addBearing_doesGetFormattedInUrlCorrectly() throws Exception { directions.cloneCall().request().url().queryParameter("bearings")); } + @Test + public void bearing_doesGetFormattedInUrlCorrectly() throws Exception { + List> bearings = new ArrayList<>(); + bearings.add(Arrays.asList(45d, 90d)); + bearings.add(Arrays.asList(2d, 90d)); + + MapboxDirections directions = MapboxDirections.builder() + .destination(Point.fromLngLat(13.4930, 9.958)) + .origin(Point.fromLngLat(1.234, 2.345)) + .bearings(bearings) + .accessToken(ACCESS_TOKEN) + .build(); + assertEquals("45,90;2,90", + directions.cloneCall().request().url().queryParameter("bearings")); + } + @Test public void radiuses_doesGetFormattedInUrlCorrectly() throws Exception { + List radiuses = new ArrayList<>(); + radiuses.add(23d); + radiuses.add(30d); + MapboxDirections directions = MapboxDirections.builder() .destination(Point.fromLngLat(13.4930, 9.958)) .origin(Point.fromLngLat(1.234, 2.345)) - .radiuses(23, 30) + .radiuses(radiuses) .accessToken(ACCESS_TOKEN) .build(); assertEquals("23;30", directions.cloneCall().request().url().queryParameter("radiuses")); } + @Test + public void radiusesList_doesGetFormattedInUrlCorrectly() throws Exception { + List radiuses = new ArrayList<>(); + radiuses.add(23d); + radiuses.add(30d); + + MapboxDirections directions = MapboxDirections.builder() + .destination(Point.fromLngLat(13.4930, 9.958)) + .origin(Point.fromLngLat(1.234, 2.345)) + .radiuses(radiuses) + .accessToken(ACCESS_TOKEN) + .build(); + assertEquals("23;30", + directions.cloneCall().request().url().queryParameter("radiuses")); + } + @Test public void clientAppName_doesGetAddedToRequestHeader1() throws Exception { MapboxDirections directions = MapboxDirections.builder() @@ -505,25 +635,55 @@ public void callFactoryNonNull() throws IOException { } @Test - public void testRadiusWithUnlimitedDistance() throws IOException { + public void radiusWithUnlimitedDistance() throws IOException { List coordinates = new ArrayList<>(); coordinates.add(Point.fromLngLat(13.4301, 52.5109)); coordinates.add(Point.fromLngLat(13.4265, 52.5080)); coordinates.add(Point.fromLngLat(13.4316, 52.5021)); + List radiuses = new ArrayList<>(); + radiuses.add(100d); + radiuses.add(Double.POSITIVE_INFINITY); + radiuses.add(100d); + MapboxDirections client = MapboxDirections.builder() .accessToken(ACCESS_TOKEN) .origin(coordinates.get(0)) .addWaypoint(coordinates.get(1)) .destination(coordinates.get(2)) .baseUrl("https://foobar.com") - .radiuses(100, Double.POSITIVE_INFINITY, 100) + .radiuses(radiuses) .build(); assertEquals("100;unlimited;100", client.cloneCall().request().url().queryParameter("radiuses")); } + @Test + public void radiusesListWithUnlimitedDistance() throws IOException { + List coordinates = new ArrayList<>(); + coordinates.add(Point.fromLngLat(13.4301, 52.5109)); + coordinates.add(Point.fromLngLat(13.4265, 52.5080)); + coordinates.add(Point.fromLngLat(13.4316, 52.5021)); + + List radiuses = new ArrayList<>(); + radiuses.add(100d); + radiuses.add(Double.POSITIVE_INFINITY); + radiuses.add(100d); + + MapboxDirections client = MapboxDirections.builder() + .accessToken(ACCESS_TOKEN) + .origin(coordinates.get(0)) + .addWaypoint(coordinates.get(1)) + .destination(coordinates.get(2)) + .baseUrl("https://foobar.com") + .radiuses(radiuses) + .build(); + + assertEquals("100;unlimited;100", + client.cloneCall().request().url().queryParameter("radiuses")); + } + @Test public void noValidRouteTest() throws Exception { MapboxDirections mapboxDirections = MapboxDirections.builder() @@ -560,7 +720,7 @@ public void maxSpeedAnnotation_doesGetFormattedInUrlCorrectly() { .origin(Point.fromLngLat(1.234,2.345)) .addWaypoint(Point.fromLngLat(13.493,9.958)) .destination(Point.fromLngLat(5.29838,4.42189)) - .annotations(DirectionsCriteria.ANNOTATION_MAXSPEED) + .addAnnotation(DirectionsCriteria.ANNOTATION_MAXSPEED) .build(); assertThat(directions.cloneCall().request().url().toString(), @@ -628,12 +788,18 @@ public void subBannerInstructionsFromJson() throws Exception { @Test public void sanityApproachesInstructions() throws Exception { + List approaches = new ArrayList<>(); + approaches.add(APPROACH_UNRESTRICTED); + approaches.add(null); + approaches.add(""); + approaches.add(APPROACH_CURB); + MapboxDirections mapboxDirections = MapboxDirections.builder() .origin(Point.fromLngLat(1.0, 1.0)) .addWaypoint(Point.fromLngLat(2.0, 2.0)) .addWaypoint(Point.fromLngLat(3.0, 3.0)) .destination(Point.fromLngLat(4.0, 4.0)) - .addApproaches(APPROACH_UNRESTRICTED, null, "", APPROACH_CURB) + .approaches(approaches) .baseUrl("https://foobar.com") .accessToken(ACCESS_TOKEN) .build(); @@ -651,7 +817,7 @@ public void build_exceptionThrownWhenNumApproachesDoesNotMatchCoordinates() thro MapboxDirections mapboxDirections = MapboxDirections.builder() .origin(Point.fromLngLat(2.0, 2.0)) .destination(Point.fromLngLat(4.0, 4.0)) - .addApproaches(APPROACH_UNRESTRICTED) + .addApproach(APPROACH_UNRESTRICTED) .baseUrl("https://foobar.com") .accessToken(ACCESS_TOKEN) .build(); @@ -662,22 +828,31 @@ public void build_exceptionThrownWhenInvalidApproaches() throws Exception { thrown.expect(ServicesException.class); thrown.expectMessage( startsWith("All approaches values must be one of curb, unrestricted")); + + List approaches = new ArrayList<>(); + approaches.add(APPROACH_UNRESTRICTED); + approaches.add("restricted"); + MapboxDirections mapboxDirections = MapboxDirections.builder() .origin(Point.fromLngLat(2.0, 2.0)) .destination(Point.fromLngLat(4.0, 4.0)) - .addApproaches(APPROACH_UNRESTRICTED, "restricted") + .approaches(approaches) .baseUrl("https://foobar.com") .accessToken(ACCESS_TOKEN) .build(); } @Test - public void testApproaches() throws Exception { + public void approaches() throws Exception { + List approaches = new ArrayList<>(); + approaches.add(APPROACH_UNRESTRICTED); + approaches.add(APPROACH_CURB); + MapboxDirections mapboxDirections = MapboxDirections.builder() .profile(PROFILE_DRIVING) .origin(Point.fromLngLat(13.4301,52.5109)) .destination(Point.fromLngLat(13.432508,52.501725)) - .addApproaches(APPROACH_UNRESTRICTED, APPROACH_CURB) + .approaches(approaches) .accessToken(ACCESS_TOKEN) .baseUrl(mockUrl.toString()) .build(); @@ -689,12 +864,37 @@ public void testApproaches() throws Exception { } @Test - public void testRouteOptionsApproaches() throws Exception { + public void approachesList() throws Exception { + List approaches = new ArrayList<>(); + approaches.add(APPROACH_UNRESTRICTED); + approaches.add(APPROACH_CURB); + + MapboxDirections mapboxDirections = MapboxDirections.builder() + .profile(PROFILE_DRIVING) + .origin(Point.fromLngLat(13.4301,52.5109)) + .destination(Point.fromLngLat(13.432508,52.501725)) + .approaches(approaches) + .accessToken(ACCESS_TOKEN) + .baseUrl(mockUrl.toString()) + .build(); + + mapboxDirections.setCallFactory(null); + Response response = mapboxDirections.executeCall(); + assertEquals(200, response.code()); + assertEquals("Ok", response.body().code()); + } + + @Test + public void routeOptionsApproaches() throws Exception { + List approachesList = new ArrayList<>(); + approachesList.add(APPROACH_UNRESTRICTED); + approachesList.add(APPROACH_CURB); + MapboxDirections mapboxDirections = MapboxDirections.builder() .profile(PROFILE_DRIVING) .origin(Point.fromLngLat(13.4301,52.5109)) .destination(Point.fromLngLat(13.432508,52.501725)) - .addApproaches(APPROACH_UNRESTRICTED, APPROACH_CURB) + .approaches(approachesList) .accessToken(ACCESS_TOKEN) .baseUrl(mockUrl.toString()) .build(); @@ -704,8 +904,34 @@ public void testRouteOptionsApproaches() throws Exception { RouteOptions routeOptions = response.body().routes().get(0).routeOptions(); - String approaches = routeOptions.approaches(); - assertEquals("unrestricted;curb", approaches); + List approaches = routeOptions.approachesList(); + assertEquals("unrestricted", approaches.get(0)); + assertEquals("curb", approaches.get(1)); + } + + @Test + public void routeOptionsApproachesList() throws Exception { + List approaches = new ArrayList<>(); + approaches.add(APPROACH_UNRESTRICTED); + approaches.add(APPROACH_CURB); + + MapboxDirections mapboxDirections = MapboxDirections.builder() + .profile(PROFILE_DRIVING) + .origin(Point.fromLngLat(13.4301,52.5109)) + .destination(Point.fromLngLat(13.432508,52.501725)) + .approaches(approaches) + .accessToken(ACCESS_TOKEN) + .baseUrl(mockUrl.toString()) + .build(); + + mapboxDirections.setCallFactory(null); + Response response = mapboxDirections.executeCall(); + RouteOptions routeOptions = response.body().routes().get(0).routeOptions(); + + + List result = routeOptions.approachesList(); + assertEquals("unrestricted", result.get(0)); + assertEquals("curb", result.get(1)); } @Test(expected = ServicesException.class) @@ -713,7 +939,7 @@ public void build_exceptionThrownWhenLessThanTwoWaypointsProvided() { MapboxDirections.builder() .origin(Point.fromLngLat(2.0, 2.0)) .destination(Point.fromLngLat(4.0, 4.0)) - .addWaypointIndices(0) + .addWaypointIndex(0) .baseUrl("https://foobar.com") .accessToken(ACCESS_TOKEN) .build(); @@ -725,7 +951,8 @@ public void build_exceptionThrownWhenWaypointsDoNotStartWith0() { .origin(Point.fromLngLat(2.0, 2.0)) .addWaypoint(Point.fromLngLat(3.0, 3.0)) .destination(Point.fromLngLat(4.0, 4.0)) - .addWaypointIndices(1, 2) + .addWaypointIndex(1) + .addWaypointIndex(2) .baseUrl("https://foobar.com") .accessToken(ACCESS_TOKEN) .build(); @@ -733,11 +960,15 @@ public void build_exceptionThrownWhenWaypointsDoNotStartWith0() { @Test(expected = ServicesException.class) public void build_exceptionThrownWhenWaypointDoNotEndWithLast() { + List indices = new ArrayList<>(); + indices.add(0); + indices.add(1); + MapboxDirections.builder() .origin(Point.fromLngLat(2.0, 2.0)) .addWaypoint(Point.fromLngLat(3.0, 3.0)) .destination(Point.fromLngLat(4.0, 4.0)) - .addWaypointIndices(0, 1) + .waypointIndices(indices) .baseUrl("https://foobar.com") .accessToken(ACCESS_TOKEN) .build(); @@ -745,11 +976,16 @@ public void build_exceptionThrownWhenWaypointDoNotEndWithLast() { @Test(expected = ServicesException.class) public void build_exceptionThrownWhenMiddleWaypointsAreWrong() { + List indices = new ArrayList<>(); + indices.add(0); + indices.add(3); + indices.add(2); + MapboxDirections.builder() .origin(Point.fromLngLat(2.0, 2.0)) .addWaypoint(Point.fromLngLat(3.0, 3.0)) .destination(Point.fromLngLat(4.0, 4.0)) - .addWaypointIndices(0, 3, 2) + .waypointIndices(indices) .baseUrl("https://foobar.com") .accessToken(ACCESS_TOKEN) .build(); @@ -757,11 +993,16 @@ public void build_exceptionThrownWhenMiddleWaypointsAreWrong() { @Test public void sanityWaypointNamesInstructions() throws Exception { + List names = new ArrayList<>(); + names.add("Home"); + names.add("Store"); + names.add("Work"); + MapboxDirections mapboxDirections = MapboxDirections.builder() .origin(Point.fromLngLat(1.0, 1.0)) .addWaypoint(Point.fromLngLat(2.0, 2.0)) .destination(Point.fromLngLat(4.0, 4.0)) - .addWaypointNames("Home", "Store", "Work") + .waypointNames(names) .baseUrl("https://foobar.com") .accessToken(ACCESS_TOKEN) .build(); @@ -771,7 +1012,30 @@ public void sanityWaypointNamesInstructions() throws Exception { } @Test - public void testWithWaypointNames() throws Exception { + public void sanityWaypointNamesListInstructions() throws Exception { + List names = new ArrayList<>(); + names.add("Home"); + names.add("Store"); + names.add("Work"); + + MapboxDirections mapboxDirections = MapboxDirections.builder() + .origin(Point.fromLngLat(1.0, 1.0)) + .addWaypoint(Point.fromLngLat(2.0, 2.0)) + .destination(Point.fromLngLat(4.0, 4.0)) + .waypointNames(names) + .baseUrl("https://foobar.com") + .accessToken(ACCESS_TOKEN) + .build(); + assertNotNull(mapboxDirections); + assertEquals("Home;Store;Work", + mapboxDirections.cloneCall().request().url().queryParameter("waypoint_names")); + } + + @Test + public void withWaypointNames() throws Exception { + List names = new ArrayList<>(); + names.add("Home"); + names.add("Work"); MapboxDirections mapboxDirections = MapboxDirections.builder() .profile(PROFILE_CYCLING) @@ -780,7 +1044,7 @@ public void testWithWaypointNames() throws Exception { .steps(true) .voiceInstructions(true) .voiceUnits(DirectionsCriteria.IMPERIAL) - .addWaypointNames("Home", "Work") + .waypointNames(names) .accessToken(ACCESS_TOKEN) .baseUrl(mockUrl.toString()) .build(); @@ -792,14 +1056,43 @@ public void testWithWaypointNames() throws Exception { } @Test - public void testWithWaypointTargets() throws Exception { + public void withWaypointNamesList() throws Exception { + List names = new ArrayList<>(); + names.add("Home"); + names.add("Work"); + + MapboxDirections mapboxDirections = MapboxDirections.builder() + .profile(PROFILE_CYCLING) + .origin(Point.fromLngLat(-122.42,37.78)) + .destination(Point.fromLngLat(-77.03,38.91)) + .steps(true) + .voiceInstructions(true) + .voiceUnits(DirectionsCriteria.IMPERIAL) + .waypointNames(names) + .accessToken(ACCESS_TOKEN) + .baseUrl(mockUrl.toString()) + .build(); + + mapboxDirections.setCallFactory(null); + Response response = mapboxDirections.executeCall(); + assertEquals(200, response.code()); + assertEquals("Ok", response.body().code()); + assertEquals("Home", response.body().routes().get(0).routeOptions().waypointNamesList().get(0)); + assertEquals("Work", response.body().routes().get(0).routeOptions().waypointNamesList().get(1)); + } + + @Test + public void withWaypointTargets() throws Exception { + List targets = new ArrayList<>(); + targets.add(null); + targets.add( Point.fromLngLat(-6.799936294555664,61.99987216574813)); MapboxDirections mapboxDirections = MapboxDirections.builder() .profile(PROFILE_DRIVING_TRAFFIC) .origin(Point.fromLngLat(-6.80904429026134,62.00015328799685)) .destination(Point.fromLngLat(-6.800065040588378,62.00012400993553)) .steps(true) - .addWaypointTargets(null, Point.fromLngLat(-6.799936294555664,61.99987216574813)) + .waypointTargets(targets) .accessToken(ACCESS_TOKEN) .baseUrl(mockUrl.toString()) .build(); @@ -816,13 +1109,18 @@ public void testWithWaypointTargets() throws Exception { } @Test - public void testWithInterceptor() throws Exception { + public void withInterceptor() throws Exception { Interceptor interceptor = new Interceptor() { @Override public okhttp3.Response intercept(Chain chain) throws IOException { return null; } }; + + List names = new ArrayList<>(); + names.add("Home"); + names.add("Work"); + MapboxDirections mapboxDirections = MapboxDirections.builder() .profile(PROFILE_CYCLING) .origin(Point.fromLngLat(-122.42,37.78)) @@ -830,7 +1128,7 @@ public okhttp3.Response intercept(Chain chain) throws IOException { .steps(true) .voiceInstructions(true) .voiceUnits(DirectionsCriteria.IMPERIAL) - .addWaypointNames("Home", "Work") + .waypointNames(names) .accessToken(ACCESS_TOKEN) .baseUrl(mockUrl.toString()) .interceptor(interceptor) @@ -840,13 +1138,18 @@ public okhttp3.Response intercept(Chain chain) throws IOException { } @Test - public void testWithNetworkInterceptor() throws Exception { + public void withNetworkInterceptor() throws Exception { Interceptor interceptor = new Interceptor() { @Override public okhttp3.Response intercept(Chain chain) throws IOException { return null; } }; + + List names = new ArrayList<>(); + names.add("Home"); + names.add("Work"); + MapboxDirections mapboxDirections = MapboxDirections.builder() .profile(PROFILE_CYCLING) .origin(Point.fromLngLat(-122.42,37.78)) @@ -854,7 +1157,7 @@ public okhttp3.Response intercept(Chain chain) throws IOException { .steps(true) .voiceInstructions(true) .voiceUnits(DirectionsCriteria.IMPERIAL) - .addWaypointNames("Home", "Work") + .waypointNames(names) .accessToken(ACCESS_TOKEN) .baseUrl(mockUrl.toString()) .networkInterceptor(interceptor) @@ -864,13 +1167,18 @@ public okhttp3.Response intercept(Chain chain) throws IOException { } @Test - public void testWithEventListener() throws Exception { + public void withEventListener() throws Exception { EventListener eventListener = new EventListener() { @Override public void callStart(Call call) { super.callStart(call); } }; + + List names = new ArrayList<>(); + names.add("Home"); + names.add("Work"); + MapboxDirections mapboxDirections = MapboxDirections.builder() .profile(PROFILE_CYCLING) .origin(Point.fromLngLat(-122.42,37.78)) @@ -878,7 +1186,7 @@ public void callStart(Call call) { .steps(true) .voiceInstructions(true) .voiceUnits(DirectionsCriteria.IMPERIAL) - .addWaypointNames("Home", "Work") + .waypointNames(names) .accessToken(ACCESS_TOKEN) .baseUrl(mockUrl.toString()) .eventListener(eventListener) @@ -889,7 +1197,11 @@ public void callStart(Call call) { } @Test - public void testPost() throws IOException { + public void post() throws IOException { + List names = new ArrayList<>(); + names.add("Home"); + names.add("Work"); + MapboxDirections mapboxDirections = MapboxDirections.builder() .profile(PROFILE_CYCLING) .origin(Point.fromLngLat(-122.42,37.78)) @@ -897,7 +1209,7 @@ public void testPost() throws IOException { .steps(true) .voiceInstructions(true) .voiceUnits(DirectionsCriteria.IMPERIAL) - .addWaypointNames("Home", "Work") + .waypointNames(names) .accessToken(ACCESS_TOKEN) .baseUrl(mockUrl.toString()) .post() @@ -912,7 +1224,7 @@ public void testPost() throws IOException { } @Test - public void testCallForUrlLength_longUrl() { + public void callForUrlLength_longUrl() { MapboxDirections.Builder builder = MapboxDirections.builder() .profile(PROFILE_CYCLING) .steps(true) @@ -930,7 +1242,7 @@ public void testCallForUrlLength_longUrl() { } @Test - public void testCallForUrlLength_shortUrl() { + public void callForUrlLength_shortUrl() { MapboxDirections.Builder builder = MapboxDirections.builder() .profile(PROFILE_CYCLING) .steps(true) @@ -948,7 +1260,7 @@ public void testCallForUrlLength_shortUrl() { } @Test - public void testPostIsUsed() { + public void postIsUsed() { MapboxDirections.Builder builder = MapboxDirections.builder() .profile(PROFILE_CYCLING) .steps(true) @@ -966,7 +1278,7 @@ public void testPostIsUsed() { } @Test - public void testGetIsUsed() { + public void getIsUsed() { MapboxDirections.Builder builder = MapboxDirections.builder() .profile(PROFILE_CYCLING) .steps(true) diff --git a/services-matching/src/main/java/com/mapbox/api/matching/v5/MapboxMapMatching.java b/services-matching/src/main/java/com/mapbox/api/matching/v5/MapboxMapMatching.java index a8c653cb1..7d03a9b44 100644 --- a/services-matching/src/main/java/com/mapbox/api/matching/v5/MapboxMapMatching.java +++ b/services-matching/src/main/java/com/mapbox/api/matching/v5/MapboxMapMatching.java @@ -13,6 +13,7 @@ import com.mapbox.api.directions.v5.DirectionsCriteria.GeometriesCriteria; import com.mapbox.api.directions.v5.DirectionsCriteria.OverviewCriteria; import com.mapbox.api.directions.v5.DirectionsCriteria.ProfileCriteria; +import com.mapbox.api.directions.v5.utils.FormatUtils; import com.mapbox.api.matching.v5.models.MapMatchingAdapterFactory; import com.mapbox.api.matching.v5.models.MapMatchingResponse; import com.mapbox.core.MapboxService; @@ -25,6 +26,7 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Locale; @@ -700,7 +702,8 @@ public MapboxMapMatching build() { } if (waypointNames != null) { - final String waypointNamesStr = TextUtils.formatWaypointNames(waypointNames); + final String waypointNamesStr + = FormatUtils.formatWaypointNames(Arrays.asList(waypointNames)); waypointNames(waypointNamesStr); } @@ -709,7 +712,7 @@ public MapboxMapMatching build() { throw new ServicesException("Number of approach elements must match " + "number of coordinates provided."); } - String formattedApproaches = TextUtils.formatApproaches(approaches); + String formattedApproaches = FormatUtils.formatApproaches(Arrays.asList(approaches)); if (formattedApproaches == null) { throw new ServicesException("All approaches values must be one of curb, unrestricted"); } @@ -735,8 +738,8 @@ private static String formatCoordinates(List coordinates) { List coordinatesFormatted = new ArrayList<>(); for (Point point : coordinates) { coordinatesFormatted.add(String.format(Locale.US, "%s,%s", - TextUtils.formatCoordinate(point.longitude()), - TextUtils.formatCoordinate(point.latitude()))); + FormatUtils.formatCoordinate(point.longitude()), + FormatUtils.formatCoordinate(point.latitude()))); } return TextUtils.join(";", coordinatesFormatted.toArray()); diff --git a/services-matching/src/main/java/com/mapbox/api/matching/v5/MatchingResponseFactory.java b/services-matching/src/main/java/com/mapbox/api/matching/v5/MatchingResponseFactory.java index 6f1954db6..09228b937 100644 --- a/services-matching/src/main/java/com/mapbox/api/matching/v5/MatchingResponseFactory.java +++ b/services-matching/src/main/java/com/mapbox/api/matching/v5/MatchingResponseFactory.java @@ -1,6 +1,7 @@ package com.mapbox.api.matching.v5; import com.mapbox.api.directions.v5.models.RouteOptions; +import com.mapbox.api.directions.v5.utils.ParseUtils; import com.mapbox.api.matching.v5.models.MapMatchingMatching; import com.mapbox.api.matching.v5.models.MapMatchingResponse; import com.mapbox.geojson.Point; @@ -58,9 +59,9 @@ private List generateRouteOptions(Response generateRouteOptions(Response response = mapMatching.executeCall(); RouteOptions routeOptions = response.body().matchings().get(0).routeOptions(); - assertEquals("unrestricted;curb", routeOptions.approaches()); + assertEquals("unrestricted", routeOptions.approachesList().get(0)); + assertEquals("curb", routeOptions.approachesList().get(1)); + } + + @Test + public void routeOptionsApproachesString() throws Exception { + MapboxMapMatching mapMatching = MapboxMapMatching.builder() + .profile(PROFILE_DRIVING) + .coordinate(Point.fromLngLat(-117.1728265285492,32.71204416018209)) + .coordinate(Point.fromLngLat(-117.17334151268004,32.71254065549407)) + .addApproaches(APPROACH_UNRESTRICTED, APPROACH_CURB) + .accessToken(ACCESS_TOKEN) + .baseUrl(mockUrl.toString()) + .build(); + + Response response = mapMatching.executeCall(); + RouteOptions routeOptions = response.body().matchings().get(0).routeOptions(); + + assertEquals("unrestricted;curb", FormatUtils.formatApproaches(routeOptions.approachesList())); } @Test diff --git a/services-matrix/src/main/java/com/mapbox/api/matrix/v1/MapboxMatrix.java b/services-matrix/src/main/java/com/mapbox/api/matrix/v1/MapboxMatrix.java index 305b15fff..c05e202ce 100644 --- a/services-matrix/src/main/java/com/mapbox/api/matrix/v1/MapboxMatrix.java +++ b/services-matrix/src/main/java/com/mapbox/api/matrix/v1/MapboxMatrix.java @@ -7,6 +7,7 @@ import com.google.gson.GsonBuilder; import com.mapbox.api.directions.v5.DirectionsAdapterFactory; import com.mapbox.api.directions.v5.DirectionsCriteria; +import com.mapbox.api.directions.v5.utils.FormatUtils; import com.mapbox.api.matrix.v1.models.MatrixResponse; import com.mapbox.core.MapboxService; import com.mapbox.core.constants.Constants; @@ -330,8 +331,8 @@ private static String formatCoordinates(List coordinates) { List coordinatesFormatted = new ArrayList<>(); for (Point point : coordinates) { coordinatesFormatted.add(String.format(Locale.US, "%s,%s", - TextUtils.formatCoordinate(point.longitude()), - TextUtils.formatCoordinate(point.latitude()))); + FormatUtils.formatCoordinate(point.longitude()), + FormatUtils.formatCoordinate(point.latitude()))); } return TextUtils.join(";", coordinatesFormatted.toArray()); } diff --git a/services-optimization/src/main/java/com/mapbox/api/optimization/v1/MapboxOptimization.java b/services-optimization/src/main/java/com/mapbox/api/optimization/v1/MapboxOptimization.java index bd9dc7817..bd4ba824b 100644 --- a/services-optimization/src/main/java/com/mapbox/api/optimization/v1/MapboxOptimization.java +++ b/services-optimization/src/main/java/com/mapbox/api/optimization/v1/MapboxOptimization.java @@ -3,7 +3,6 @@ import androidx.annotation.FloatRange; import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import com.google.auto.value.AutoValue; import com.google.gson.GsonBuilder; import com.mapbox.api.directions.v5.DirectionsAdapterFactory; @@ -13,6 +12,7 @@ import com.mapbox.api.directions.v5.DirectionsCriteria.GeometriesCriteria; import com.mapbox.api.directions.v5.DirectionsCriteria.OverviewCriteria; import com.mapbox.api.directions.v5.DirectionsCriteria.ProfileCriteria; +import com.mapbox.api.directions.v5.utils.FormatUtils; import com.mapbox.api.optimization.v1.models.OptimizationAdapterFactory; import com.mapbox.api.optimization.v1.models.OptimizationResponse; import com.mapbox.core.MapboxService; @@ -22,11 +22,10 @@ import com.mapbox.core.utils.MapboxUtils; import com.mapbox.core.utils.TextUtils; import com.mapbox.geojson.Point; - import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Locale; - import retrofit2.Call; /** @@ -158,7 +157,7 @@ public static Builder builder() { public abstract static class Builder { private List distributions = new ArrayList<>(); - private List bearings = new ArrayList<>(); + private List> bearings = new ArrayList<>(); private List coordinates = new ArrayList<>(); private String[] annotations; private double[] radiuses; @@ -339,7 +338,7 @@ public Builder radiuses(@FloatRange(from = 0) double... radiuses) { */ public Builder bearing(@Nullable @FloatRange(from = 0, to = 360) Double angle, @Nullable @FloatRange(from = 0, to = 360) Double tolerance) { - bearings.add(new Double[] {angle, tolerance}); + bearings.add(Arrays.asList(angle, tolerance)); return this; } @@ -487,10 +486,10 @@ public MapboxOptimization build() { } coordinates(formatCoordinates(coordinates)); - bearings(TextUtils.formatBearing(bearings)); + bearings(FormatUtils.formatBearings(bearings)); annotations(TextUtils.join(",", annotations)); radiuses(TextUtils.formatRadiuses(radiuses)); - distributions(TextUtils.formatDistributions(distributions)); + distributions(FormatUtils.formatDistributions(distributions)); // Generate build so that we can check that values are valid. MapboxOptimization optimization = autoBuild(); @@ -505,8 +504,8 @@ private static String formatCoordinates(List coordinates) { List coordinatesFormatted = new ArrayList<>(); for (Point point : coordinates) { coordinatesFormatted.add(String.format(Locale.US, "%s,%s", - TextUtils.formatCoordinate(point.longitude()), - TextUtils.formatCoordinate(point.latitude()))); + FormatUtils.formatCoordinate(point.longitude()), + FormatUtils.formatCoordinate(point.latitude()))); } return TextUtils.join(";", coordinatesFormatted.toArray());