com.mapbox.api.optimization.v1.MapboxOptimization Maven / Gradle / Ivy
package com.mapbox.api.optimization.v1;
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;
import com.mapbox.api.directions.v5.DirectionsCriteria;
import com.mapbox.api.directions.v5.DirectionsCriteria.AnnotationCriteria;
import com.mapbox.api.directions.v5.DirectionsCriteria.DestinationCriteria;
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;
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.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import retrofit2.Call;
/**
* The Mapbox Optimization API returns a duration-optimized trip between the input coordinates.
* This is also known as solving the Traveling Salesperson Problem. A typical use case for this API
* is planning the route for deliveries in a city. Optimized trips can be retrieved for car driving,
* bicycling and walking or hiking.
*
* Under normal plans, a maximum of 12 coordinates can be passed in at once at a maximum 60 requests
* per minute. For higher volumes, reach out through our contact page.
*
* Note that for under 10 coordinates, the returned results will be optimal. For 10 and more
* coordinates, the results will be optimized approximations.
*
* @see Traveling Salesperson
* Problem
* @see API documentation
* @since 2.1.0
*/
@AutoValue
public abstract class MapboxOptimization
extends MapboxService {
protected MapboxOptimization() {
super(OptimizationService.class);
}
@Override
protected GsonBuilder getGsonBuilder() {
return new GsonBuilder()
.registerTypeAdapterFactory(OptimizationAdapterFactory.create())
.registerTypeAdapterFactory(DirectionsAdapterFactory.create());
}
@Override
protected Call initializeCall() {
return getService().getCall(
ApiCallHelper.getHeaderUserAgent(clientAppName()),
user(),
profile(),
coordinates(),
accessToken(),
roundTrip(),
radiuses(),
bearings(),
steps(),
overview(),
geometries(),
annotations(),
destination(),
source(),
language(),
distributions());
}
@NonNull
abstract String user();
@NonNull
abstract String profile();
@Nullable
abstract Boolean roundTrip();
@Nullable
abstract String distributions();
@Nullable
abstract String source();
@Nullable
abstract String destination();
@Nullable
abstract String geometries();
@Nullable
abstract String overview();
@Nullable
abstract Boolean steps();
@Nullable
abstract String clientAppName();
@NonNull
abstract String accessToken();
@NonNull
@Override
protected abstract String baseUrl();
@Nullable
abstract String language();
@Nullable
abstract String radiuses();
@Nullable
abstract String bearings();
@NonNull
abstract String coordinates();
@Nullable
abstract String annotations();
/**
* Build a new {@link MapboxOptimization} object with the initial values set for
* {@link #baseUrl()}, {@link #profile()}, {@link #user()}, and {@link #geometries()}.
*
* @return a {@link Builder} object for creating this object
* @since 3.0.0
*/
public static Builder builder() {
return new AutoValue_MapboxOptimization.Builder()
.baseUrl(Constants.BASE_API_URL)
.profile(DirectionsCriteria.PROFILE_DRIVING)
.user(DirectionsCriteria.PROFILE_DEFAULT_USER)
.geometries(DirectionsCriteria.GEOMETRY_POLYLINE6);
}
/**
* Optimization v1 builder.
*
* @since 2.1.0
*/
@AutoValue.Builder
public abstract static class Builder {
private List distributions = new ArrayList<>();
private List> bearings = new ArrayList<>();
private List coordinates = new ArrayList<>();
private String[] annotations;
private double[] radiuses;
/**
* The username for the account that the directions engine runs on. In most cases, this should
* always remain the default value of {@link DirectionsCriteria#PROFILE_DEFAULT_USER}.
*
* @param user a non-null string which will replace the default user used in the directions
* request
* @return this builder for chaining options together
* @since 2.1.0
*/
public abstract Builder user(@NonNull String user);
/**
* Add a list of {@link Point}'s which define the route which will become optimized. The minimum
* points is 2 and the maximum points allowed in totals 12. You can use this method in
* conjunction with {@link #coordinate(Point)}.
*
* @param coordinates a List full of {@link Point}s which define the route
* @return this builder for chaining options together
* @since 2.1.0
*/
public Builder coordinates(@NonNull List coordinates) {
this.coordinates.addAll(coordinates);
return this;
}
// Required for matching with MapboxOptimization coordinates() method.
abstract Builder coordinates(@NonNull String coordinates);
/**
* This will add a single {@link Point} to the coordinate list which is used to determine the
* most optimal route. This can be called up to 12 times until you hit the maximum allowed
* points. You can use this method in conjunction with {@link #coordinates(List)}.
*
* @param coordinate a {@link Point} which you'd like the optimized route to pass through
* @return this builder for chaining options together
* @since 3.0.0
*/
public Builder coordinate(@NonNull Point coordinate) {
this.coordinates.add(coordinate);
return this;
}
/**
* This selects which mode of transportation the user will be using while navigating from the
* origin to the final destination. The options include driving, driving considering traffic,
* walking, and cycling. Using each of these profiles will result in different routing biases.
*
* @param profile required to be one of the String values found in the {@link ProfileCriteria}
* @return this builder for chaining options together
* @since 2.1.0
*/
public abstract Builder profile(@NonNull @ProfileCriteria String profile);
/**
* Returned route is a roundtrip (route returns to first location). Allowed values are:
* {@code true} (default), {@code false} and null (to reset to the default value). If the
* roundtrip is set to false, then {@link #source()} and {@link #destination()} parameters are
* required but not all combinations are possible.
*
* It is possible to explicitly set the start or end coordinate of the trip. When source? is set
* to first, the first coordinate is used as the start coordinate of the trip in the output.
* When destination is set to last, the last coordinate will be used as destination of the trip
* in the returned output. If you specify {@link DirectionsCriteria#DESTINATION_ANY}/
* {@link DirectionsCriteria#SOURCE_ANY}, any of the coordinates can be used as the first or
* last coordinate in the output.
*
* @param roundTrip true if you'd like the route to return to the origin, else false
* @return this builder for chaining options together
* @see Possible
* roundtrip combinations
* @since 2.1.0
*/
public abstract Builder roundTrip(@Nullable Boolean roundTrip);
/**
* Returned route starts at {@link DirectionsCriteria#SOURCE_ANY} or
* {@link DirectionsCriteria#SOURCE_FIRST} coordinate. Null can also be passed in to reset
* this value back to the API default if needed.
*
* @param source one of the values in {@link DirectionsCriteria.SourceCriteria}
* @return this builder for chaining options together
* @since 2.1.0
*/
public abstract Builder source(@Nullable @DirectionsCriteria.SourceCriteria String source);
/**
* Returned route ends at {@link DirectionsCriteria#DESTINATION_ANY} or
* {@link DirectionsCriteria#DESTINATION_LAST} coordinate.
*
* @param destination either {@code "any" or "last"}
* @return this builder for chaining options together
* @since 2.1.0
*/
public abstract Builder destination(@Nullable @DestinationCriteria String destination);
/**
* 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
* {@link DirectionsCriteria#GEOMETRY_POLYLINE6}.
*
* Note that while the API supports GeoJson as an option for geometry, this SDK intentionally
* removes this as an option since an encoded string for the geometry significantly reduces
* bandwidth on mobile devices and speeds up response time.
*
*
* @param geometries null if you'd like the default geometry, else one of the options found in
* {@link GeometriesCriteria}.
* @return this builder for chaining options together
* @since 2.1.0
*/
public abstract Builder geometries(@Nullable @GeometriesCriteria String geometries);
/**
* Type of returned overview geometry. Can be {@link DirectionsCriteria#OVERVIEW_FULL} (the most
* detailed geometry available), {@link DirectionsCriteria#OVERVIEW_SIMPLIFIED} (a simplified
* version of the full geometry), or {@link DirectionsCriteria#OVERVIEW_FALSE} (no overview
* geometry). The default is simplified. Passing in null will use the APIs default setting for
* the overview field.
*
* @param overview null or one of the options found in {@link OverviewCriteria}
* @return this builder for chaining options together
* @since 2.1.0
*/
public abstract Builder overview(@Nullable @OverviewCriteria String overview);
/**
* 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 double array containing the radiuses defined in unit meters.
* @return this builder for chaining options together
* @since 2.1.0
*/
public Builder radiuses(@FloatRange(from = 0) double... radiuses) {
this.radiuses = radiuses;
return this;
}
// Required for matching with MapboxOptimization radiuses() method.
abstract Builder radiuses(@Nullable String radiuses);
/**
* 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.
*
* 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
* a value of 45 degrees or 90 degrees for the range, as bearing measurements tend to be
* inaccurate. This is useful for making sure we reroute vehicles on new routes that 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. If provided, the
* list of bearings must be the same length as the list of waypoints, but you can skip a
* coordinate and show its position by passing in null value for both the angle and tolerance
* values.
*
* Each bearing value gets associated with the same order which coordinates are arranged in this
* builder. For example, the first bearing added in this builder will be associated with the
* origin {@code Point}, the nth bearing being associated with the nth waypoint added (if added)
* and the last bearing being added will be associated with the destination.
*
*
* @param angle double value used for setting the corresponding coordinate's angle of travel
* when determining the route
* @param tolerance the deviation the bearing angle can vary while determining the route,
* recommended to be either 45 or 90 degree tolerance
* @return this builder for chaining options together
* @since 2.1.0
*/
public Builder bearing(@Nullable @FloatRange(from = 0, to = 360) Double angle,
@Nullable @FloatRange(from = 0, to = 360) Double tolerance) {
bearings.add(Arrays.asList(angle, tolerance));
return this;
}
// Required for matching with MapboxOptimization bearings() method.
abstract Builder bearings(@Nullable String bearings);
/**
* Setting this will determine whether to return steps and turn-by-turn instructions. Can be
* set to either true or false to enable or disable respectively. null can also optionally be
* passed in to set the default behavior to match what the API does by default.
*
* @param steps true if you'd like step information
* @return this builder for chaining options together
* @since 2.1.0
*/
public abstract Builder steps(@Nullable Boolean steps);
/**
* 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 ,}.
*
* @param annotations string referencing one of the annotation direction criteria's. The strings
* restricted to one or multiple values inside the {@link AnnotationCriteria}
* or null which will result in no annotations being used
* @return this builder for chaining options together
* @see RouteLeg object
* documentation
* @since 2.1.0
*/
public Builder annotations(@Nullable @AnnotationCriteria String... annotations) {
this.annotations = annotations;
return this;
}
// Required for matching with MapboxOptimization annotations() method.
abstract Builder annotations(@Nullable String annotations);
/**
* Set the instruction language for the directions request, the default is english. Only a
* select number of languages are currently supported, reference the table provided in the see
* link below.
*
* @param language a Locale value representing the language you'd like the instructions to be
* written in when returned
* @return this builder for chaining options together
* @see Supported
* Languages
* @since 3.0.0
*/
public Builder language(@Nullable Locale language) {
if (language != null) {
language(language.getLanguage());
}
return this;
}
/**
* Set the instruction language for the directions request, the default is english. Only a
* select number of languages are currently supported, reference the table provided in the see
* link below. It is recommended to use the {@link #language(Locale)} method to prevent errors
* when making the request.
*
* @param language a String value representing the language you'd like the instructions to be
* written in when returned
* @return this builder for chaining options together
* @see Supported
* Languages
* @since 2.2.0
*/
public abstract Builder language(@Nullable String language);
/**
* Base package name or other simple string identifier. Used inside the calls user agent header.
*
* @param clientAppName base package name or other simple string identifier
* @return this builder for chaining options together
* @since 2.1.0
*/
public abstract Builder clientAppName(@NonNull String clientAppName);
/**
* Required to call when this is being built. If no access token provided,
* {@link ServicesException} will be thrown.
*
* @param accessToken Mapbox access token, You must have a Mapbox account inorder to use
* the Optimization API
* @return this builder for chaining options together
* @since 2.1.0
*/
public abstract Builder accessToken(@NonNull String accessToken);
/**
* Optionally change the APIs base URL to something other then the default Mapbox one.
*
* @param baseUrl base url used as end point
* @return this builder for chaining options together
* @since 2.1.0
*/
public abstract Builder baseUrl(@NonNull String baseUrl);
/**
* Specify pick-up and drop-off locations for a trip by providing a {@code pickup} and
* {@code dropOff} value correspond with the coordinates list. The first number indicates what
* place the coordinate of the pick-up location is in the coordinates list, and the second
* number indicates what place the coordinate of the drop-off location is in the coordinates
* list. Pick-up and drop-off locations in one pair cannot be the same. The returned solution
* will visit pick-up locations before visiting drop-off locations. The depot (first location)
* can only be a pick-up location but not a drop-off location.
*
* @param dropOff the coordinate index in the coordinates list which should be the drop off
* location
* @param pickup the coordinate index in the coordinates list which should be the pick-up
* location
* @return this builder for chaining options together
* @since 2.2.0
*/
public Builder distribution(@Nullable Integer pickup, @Nullable Integer dropOff) {
distributions.add(new Integer[] {pickup, dropOff});
return this;
}
// Required for matching with MapboxOptimization distributions() method.
abstract Builder distributions(@Nullable String distributions);
abstract MapboxOptimization autoBuild();
/**
* This uses the provided parameters set using the {@link Builder} and first checks that all
* values are valid, formats the values as strings for easier consumption by the API, and lastly
* creates a new {@link MapboxOptimization} object with the values provided.
*
* @return a new instance of Mapbox Optimization
* @since 2.1.0
*/
public MapboxOptimization build() {
if (coordinates == null || coordinates.size() < 2) {
throw new ServicesException("At least two coordinates must be provided with your API"
+ "request.");
} else if (coordinates.size() > 12) {
throw new ServicesException("Maximum of 12 coordinates are allowed for this API.");
}
coordinates(formatCoordinates(coordinates));
bearings(FormatUtils.formatBearings(bearings));
annotations(TextUtils.join(",", annotations));
radiuses(TextUtils.formatRadiuses(radiuses));
distributions(FormatUtils.formatDistributions(distributions));
// Generate build so that we can check that values are valid.
MapboxOptimization optimization = autoBuild();
if (!MapboxUtils.isAccessTokenValid(optimization.accessToken())) {
throw new ServicesException("Using Mapbox Services requires setting a valid access token.");
}
return optimization;
}
private static String formatCoordinates(List coordinates) {
List coordinatesFormatted = new ArrayList<>();
for (Point point : coordinates) {
coordinatesFormatted.add(String.format(Locale.US, "%s,%s",
FormatUtils.formatCoordinate(point.longitude()),
FormatUtils.formatCoordinate(point.latitude())));
}
return TextUtils.join(";", coordinatesFormatted.toArray());
}
}
}