// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";

package google.maps.routeoptimization.v1;

import "google/api/annotations.proto";
import "google/api/client.proto";
import "google/api/field_behavior.proto";
import "google/longrunning/operations.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/timestamp.proto";
import "google/type/latlng.proto";

option csharp_namespace = "Google.Maps.RouteOptimization.V1";
option go_package = "cloud.google.com/go/maps/routeoptimization/apiv1/routeoptimizationpb;routeoptimizationpb";
option java_multiple_files = true;
option java_outer_classname = "RouteOptimizationServiceProto";
option java_package = "com.google.maps.routeoptimization.v1";
option php_namespace = "Google\\Maps\\RouteOptimization\\V1";
option ruby_package = "Google::Maps::RouteOptimization::V1";

// A service for optimizing vehicle tours.
//
// Validity of certain types of fields:
//
//   * `google.protobuf.Timestamp`
//     * Times are in Unix time: seconds since 1970-01-01T00:00:00+00:00.
//     * seconds must be in [0, 253402300799],
//       i.e. in [1970-01-01T00:00:00+00:00, 9999-12-31T23:59:59+00:00].
//     * nanos must be unset or set to 0.
//   * `google.protobuf.Duration`
//     * seconds must be in [0, 253402300799],
//       i.e. in [1970-01-01T00:00:00+00:00, 9999-12-31T23:59:59+00:00].
//     * nanos must be unset or set to 0.
//   * `google.type.LatLng`
//     * latitude must be in [-90.0, 90.0].
//     * longitude must be in [-180.0, 180.0].
//     * at least one of latitude and longitude must be non-zero.
//
service RouteOptimization {
  option (google.api.default_host) = "routeoptimization.googleapis.com";
  option (google.api.oauth_scopes) =
      "https://www.googleapis.com/auth/cloud-platform";

  // Sends an `OptimizeToursRequest` containing a `ShipmentModel` and returns an
  // `OptimizeToursResponse` containing `ShipmentRoute`s, which are a set of
  // routes to be performed by vehicles minimizing the overall cost.
  //
  // A `ShipmentModel` model consists mainly of `Shipment`s that need to be
  // carried out and `Vehicle`s that can be used to transport the `Shipment`s.
  // The `ShipmentRoute`s assign `Shipment`s to `Vehicle`s. More specifically,
  // they assign a series of `Visit`s to each vehicle, where a `Visit`
  // corresponds to a `VisitRequest`, which is a pickup or delivery for a
  // `Shipment`.
  //
  // The goal is to provide an assignment of `ShipmentRoute`s to `Vehicle`s that
  // minimizes the total cost where cost has many components defined in the
  // `ShipmentModel`.
  rpc OptimizeTours(OptimizeToursRequest) returns (OptimizeToursResponse) {
    option (google.api.http) = {
      post: "/v1/{parent=projects/*/locations/*}:optimizeTours"
      body: "*"
      additional_bindings {
        post: "/v1/{parent=projects/*}:optimizeTours"
        body: "*"
      }
    };
  }

  // Optimizes vehicle tours for one or more `OptimizeToursRequest`
  // messages as a batch.
  //
  // This method is a Long Running Operation (LRO). The inputs for optimization
  // (`OptimizeToursRequest` messages) and outputs (`OptimizeToursResponse`
  // messages) are read from and written to Cloud Storage in user-specified
  // format. Like the `OptimizeTours` method, each `OptimizeToursRequest`
  // contains a `ShipmentModel` and returns an `OptimizeToursResponse`
  // containing `ShipmentRoute` fields, which are a set of routes to be
  // performed by vehicles minimizing the overall cost.
  //
  // The user can poll `operations.get` to check the status of the LRO:
  //
  // If the LRO `done` field is false, then at least one request is still
  // being processed. Other requests may have completed successfully and their
  // results are available in Cloud Storage.
  //
  // If the LRO's `done` field is true, then all requests have been processed.
  // Any successfully processed requests will have their results available in
  // Cloud Storage. Any requests that failed will not have their results
  // available in Cloud Storage. If the LRO's `error` field is set, then it
  // contains the error from one of the failed requests.
  rpc BatchOptimizeTours(BatchOptimizeToursRequest)
      returns (google.longrunning.Operation) {
    option (google.api.http) = {
      post: "/v1/{parent=projects/*/locations/*}:batchOptimizeTours"
      body: "*"
      additional_bindings {
        post: "/v1/{parent=projects/*}:batchOptimizeTours"
        body: "*"
      }
    };
    option (google.longrunning.operation_info) = {
      response_type: "BatchOptimizeToursResponse"
      metadata_type: "BatchOptimizeToursMetadata"
    };
  }
}

// Request to batch optimize tours as an asynchronous operation.
// Each input file should contain one `OptimizeToursRequest`, and each output
// file will contain one `OptimizeToursResponse`. The request contains
// information to read/write and parse the files. All the input and output files
// should be under the same project.
message BatchOptimizeToursRequest {
  // Information for solving one optimization model asynchronously.
  message AsyncModelConfig {
    // Optional. User defined model name, can be used as alias by users to keep
    // track of models.
    string display_name = 1 [(google.api.field_behavior) = OPTIONAL];

    // Required. Information about the input model.
    InputConfig input_config = 2 [(google.api.field_behavior) = REQUIRED];

    // Required. The desired output location information.
    OutputConfig output_config = 3 [(google.api.field_behavior) = REQUIRED];
  }

  // Required. Target project and location to make a call.
  //
  // Format:
  // * `projects/{project-id}`
  // * `projects/{project-id}/locations/{location-id}`
  //
  // If no location is specified, a region will be chosen automatically.
  string parent = 1 [(google.api.field_behavior) = REQUIRED];

  // Required. Input/Output information each purchase model, such as file paths
  // and data formats.
  repeated AsyncModelConfig model_configs = 2
      [(google.api.field_behavior) = REQUIRED];
}

// Response to a `BatchOptimizeToursRequest`. This is returned in
// the Long Running Operation after the operation is complete.
message BatchOptimizeToursResponse {}

// Operation metadata for `BatchOptimizeToursRequest` calls.
message BatchOptimizeToursMetadata {}

// Request to be given to a tour optimization solver which defines the
// shipment model to solve as well as optimization parameters.
message OptimizeToursRequest {
  // Defines how the solver should handle the request. In all modes but
  // `VALIDATE_ONLY`, if the request is invalid, you will receive an
  // `INVALID_REQUEST` error. See
  // [max_validation_errors][google.maps.routeoptimization.v1.OptimizeToursRequest.max_validation_errors]
  // to cap the number of errors returned.
  enum SolvingMode {
    // Solve the model. Warnings may be issued in
    // [OptimizeToursResponse.validation_errors][google.cloud.optimization.v1.OptimizeToursResponse.validation_errors].
    DEFAULT_SOLVE = 0;

    // Only validates the model without solving it: populates as many
    // [OptimizeToursResponse.validation_errors][google.maps.routeoptimization.v1.OptimizeToursResponse.validation_errors]
    // as possible.
    VALIDATE_ONLY = 1;

    // Only populates
    // [OptimizeToursResponse.validation_errors][google.maps.routeoptimization.v1.OptimizeToursResponse.validation_errors]
    // or
    // [OptimizeToursResponse.skipped_shipments][google.maps.routeoptimization.v1.OptimizeToursResponse.skipped_shipments],
    // and doesn't actually solve the rest of the request (`status` and `routes`
    // are unset in the response).
    // If infeasibilities in `injected_solution_constraint` routes are detected
    // they are populated in the
    // [OptimizeToursResponse.validation_errors][google.maps.routeoptimization.v1.OptimizeToursResponse.validation_errors]
    // field and
    // [OptimizeToursResponse.skipped_shipments][google.maps.routeoptimization.v1.OptimizeToursResponse.skipped_shipments]
    // is left empty.
    //
    // *IMPORTANT*: not all infeasible shipments are returned here, but only the
    // ones that are detected as infeasible during preprocessing.
    DETECT_SOME_INFEASIBLE_SHIPMENTS = 2;
  }

  // Mode defining the behavior of the search, trading off latency versus
  // solution quality. In all modes, the global request deadline is enforced.
  enum SearchMode {
    // Unspecified search mode, equivalent to `RETURN_FAST`.
    SEARCH_MODE_UNSPECIFIED = 0;

    // Stop the search after finding the first good solution.
    RETURN_FAST = 1;

    // Spend all the available time to search for better solutions.
    CONSUME_ALL_AVAILABLE_TIME = 2;
  }

  // Required. Target project or location to make a call.
  //
  // Format:
  // * `projects/{project-id}`
  // * `projects/{project-id}/locations/{location-id}`
  //
  // If no location is specified, a region will be chosen automatically.
  string parent = 1 [(google.api.field_behavior) = REQUIRED];

  // If this timeout is set, the server returns a response before the timeout
  // period has elapsed or the server deadline for synchronous requests is
  // reached, whichever is sooner.
  //
  // For asynchronous requests, the server will generate a solution (if
  // possible) before the timeout has elapsed.
  google.protobuf.Duration timeout = 2;

  // Shipment model to solve.
  ShipmentModel model = 3;

  // By default, the solving mode is `DEFAULT_SOLVE` (0).
  SolvingMode solving_mode = 4;

  // Search mode used to solve the request.
  SearchMode search_mode = 6;

  // Guide the optimization algorithm in finding a first solution that is
  // similar to a previous solution.
  //
  // The model is constrained when the first solution is built.
  // Any shipments not performed on a route are implicitly skipped in the first
  // solution, but they may be performed in successive solutions.
  //
  // The solution must satisfy some basic validity assumptions:
  //
  //   * for all routes, `vehicle_index` must be in range and not be duplicated.
  //   * for all visits, `shipment_index` and `visit_request_index` must be
  //     in range.
  //   * a shipment may only be referenced on one route.
  //   * the pickup of a pickup-delivery shipment must be performed before
  //     the delivery.
  //   * no more than one pickup alternative or delivery alternative of
  //     a shipment may be performed.
  //   * for all routes, times are increasing (i.e., `vehicle_start_time
  //     <= visits[0].start_time <= visits[1].start_time ...
  //     <= vehicle_end_time`).
  //   * a shipment may only be performed on a vehicle that is allowed. A
  //     vehicle is allowed if
  //     [Shipment.allowed_vehicle_indices][google.maps.routeoptimization.v1.Shipment.allowed_vehicle_indices]
  //     is empty or its `vehicle_index` is included in
  //     [Shipment.allowed_vehicle_indices][google.maps.routeoptimization.v1.Shipment.allowed_vehicle_indices].
  //
  // If the injected solution is not feasible, a validation error is not
  // necessarily returned and an error indicating infeasibility may be returned
  // instead.
  repeated ShipmentRoute injected_first_solution_routes = 7;

  // Constrain the optimization algorithm to find a final solution that is
  // similar to a previous solution. For example, this may be used to freeze
  // portions of routes which have already been completed or which are to be
  // completed but must not be modified.
  //
  // If the injected solution is not feasible, a validation error is not
  // necessarily returned and an error indicating infeasibility may be returned
  // instead.
  InjectedSolutionConstraint injected_solution_constraint = 8;

  // If non-empty, the given routes will be refreshed, without modifying their
  // underlying sequence of visits or travel times: only other details will be
  // updated. This does not solve the model.
  //
  // As of 2020/11, this only populates the polylines of non-empty routes and
  // requires that `populate_polylines` is true.
  //
  // The `route_polyline` fields of the passed-in routes may be inconsistent
  // with route `transitions`.
  //
  // This field must not be used together with `injected_first_solution_routes`
  // or `injected_solution_constraint`.
  //
  // `Shipment.ignore` and `Vehicle.ignore` have no effect on the behavior.
  // Polylines are still populated between all visits in all non-empty routes
  // regardless of whether the related shipments or vehicles are ignored.
  repeated ShipmentRoute refresh_details_routes = 9;

  // If true:
  //
  //   * uses
  //   [ShipmentRoute.vehicle_label][google.maps.routeoptimization.v1.ShipmentRoute.vehicle_label]
  //   instead of `vehicle_index` to
  //     match routes in an injected solution with vehicles in the request;
  //     reuses the mapping of original
  //     [ShipmentRoute.vehicle_index][google.maps.routeoptimization.v1.ShipmentRoute.vehicle_index]
  //     to new
  //     [ShipmentRoute.vehicle_index][google.maps.routeoptimization.v1.ShipmentRoute.vehicle_index]
  //     to update
  //     [ConstraintRelaxation.vehicle_indices][google.maps.routeoptimization.v1.InjectedSolutionConstraint.ConstraintRelaxation.vehicle_indices]
  //     if non-empty, but the mapping must be unambiguous (i.e., multiple
  //     `ShipmentRoute`s must not share the same original `vehicle_index`).
  //   * uses
  //   [ShipmentRoute.Visit.shipment_label][google.maps.routeoptimization.v1.ShipmentRoute.Visit.shipment_label]
  //   instead of `shipment_index`
  //     to match visits in an injected solution with shipments in the request;
  //   * uses
  //   [SkippedShipment.label][google.maps.routeoptimization.v1.SkippedShipment.label]
  //   instead of
  //   [SkippedShipment.index][google.maps.routeoptimization.v1.SkippedShipment.index]
  //   to
  //     match skipped shipments in the injected solution with request
  //     shipments.
  //
  // This interpretation applies to the `injected_first_solution_routes`,
  // `injected_solution_constraint`, and `refresh_details_routes` fields.
  // It can be used when shipment or vehicle indices in the request have
  // changed since the solution was created, perhaps because shipments or
  // vehicles have been removed from or added to the request.
  //
  // If true, labels in the following categories must appear at most once in
  // their category:
  //
  //   * [Vehicle.label][google.maps.routeoptimization.v1.Vehicle.label] in the
  //   request;
  //   * [Shipment.label][google.maps.routeoptimization.v1.Shipment.label] in
  //   the request;
  //   * [ShipmentRoute.vehicle_label][google.maps.routeoptimization.v1.ShipmentRoute.vehicle_label] in the injected solution;
  //   * [SkippedShipment.label][google.maps.routeoptimization.v1.SkippedShipment.label] and [ShipmentRoute.Visit.shipment_label][google.maps.routeoptimization.v1.ShipmentRoute.Visit.shipment_label] in
  //     the injected solution (except pickup/delivery visit pairs, whose
  //     `shipment_label` must appear twice).
  //
  // If a `vehicle_label` in the injected solution does not correspond to a
  // request vehicle, the corresponding route is removed from the solution
  // along with its visits. If a `shipment_label` in the injected solution does
  // not correspond to a request shipment, the corresponding visit is removed
  // from the solution. If a
  // [SkippedShipment.label][google.maps.routeoptimization.v1.SkippedShipment.label]
  // in the injected solution does not correspond to a request shipment, the
  // `SkippedShipment` is removed from the solution.
  //
  // Removing route visits or entire routes from an injected solution may
  // have an effect on the implied constraints, which may lead to change in
  // solution, validation errors, or infeasibility.
  //
  // NOTE: The caller must ensure that each
  // [Vehicle.label][google.maps.routeoptimization.v1.Vehicle.label] (resp.
  // [Shipment.label][google.maps.routeoptimization.v1.Shipment.label]) uniquely
  // identifies a vehicle (resp. shipment) entity used across the two relevant
  // requests: the past request that produced the `OptimizeToursResponse` used
  // in the injected solution and the current request that includes the injected
  // solution. The uniqueness checks described above are not enough to guarantee
  // this requirement.
  bool interpret_injected_solutions_using_labels = 10;

  // Consider traffic estimation in calculating `ShipmentRoute` fields
  // [Transition.travel_duration][google.maps.routeoptimization.v1.ShipmentRoute.Transition.travel_duration],
  // [Visit.start_time][google.maps.routeoptimization.v1.ShipmentRoute.Visit.start_time],
  // and `vehicle_end_time`; in setting the
  // [ShipmentRoute.has_traffic_infeasibilities][google.maps.routeoptimization.v1.ShipmentRoute.has_traffic_infeasibilities]
  // field, and in calculating the
  // [OptimizeToursResponse.total_cost][google.maps.routeoptimization.v1.OptimizeToursResponse.total_cost]
  // field.
  bool consider_road_traffic = 11;

  // If true, polylines will be populated in response `ShipmentRoute`s.
  bool populate_polylines = 12;

  // If true, polylines and route tokens will be populated in response
  // [ShipmentRoute.transitions][google.maps.routeoptimization.v1.ShipmentRoute.transitions].
  bool populate_transition_polylines = 13;

  // If this is set, then the request can have a deadline
  // (see https://grpc.io/blog/deadlines) of up to 60 minutes.
  // Otherwise, the maximum deadline is only 30 minutes.
  // Note that long-lived requests have a significantly larger (but still small)
  // risk of interruption.
  bool allow_large_deadline_despite_interruption_risk = 14;

  // If true, travel distances will be computed using geodesic distances instead
  // of Google Maps distances, and travel times will be computed using geodesic
  // distances with a speed defined by `geodesic_meters_per_second`.
  bool use_geodesic_distances = 15;

  // When `use_geodesic_distances` is true, this field must be set and defines
  // the speed applied to compute travel times. Its value must be at least 1.0
  // meters/seconds.
  optional double geodesic_meters_per_second = 16;

  // Truncates the number of validation errors returned. These errors are
  // typically attached to an INVALID_ARGUMENT error payload as a BadRequest
  // error detail (https://cloud.google.com/apis/design/errors#error_details),
  // unless solving_mode=VALIDATE_ONLY: see the
  // [OptimizeToursResponse.validation_errors][google.maps.routeoptimization.v1.OptimizeToursResponse.validation_errors]
  // field.
  // This defaults to 100 and is capped at 10,000.
  optional int32 max_validation_errors = 5;

  // Label that may be used to identify this request, reported back in the
  // [OptimizeToursResponse.request_label][google.maps.routeoptimization.v1.OptimizeToursResponse.request_label].
  string label = 17;
}

// Response after solving a tour optimization problem containing the routes
// followed by each vehicle, the shipments which have been skipped and the
// overall cost of the solution.
message OptimizeToursResponse {
  // Overall metrics, aggregated over all routes.
  message Metrics {
    // Aggregated over the routes. Each metric is the sum (or max, for loads)
    // over all
    // [ShipmentRoute.metrics][google.maps.routeoptimization.v1.ShipmentRoute.metrics]
    // fields of the same name.
    AggregatedMetrics aggregated_route_metrics = 1;

    // Number of mandatory shipments skipped.
    int32 skipped_mandatory_shipment_count = 2;

    // Number of vehicles used. Note: if a vehicle route is empty and
    // [Vehicle.used_if_route_is_empty][google.maps.routeoptimization.v1.Vehicle.used_if_route_is_empty]
    // is true, the vehicle is considered used.
    int32 used_vehicle_count = 3;

    // The earliest start time for a used vehicle, computed as the minimum over
    // all used vehicles of
    // [ShipmentRoute.vehicle_start_time][google.maps.routeoptimization.v1.ShipmentRoute.vehicle_start_time].
    google.protobuf.Timestamp earliest_vehicle_start_time = 4;

    // The latest end time for a used vehicle, computed as the maximum over all
    // used vehicles of
    // [ShipmentRoute.vehicle_end_time][google.maps.routeoptimization.v1.ShipmentRoute.vehicle_end_time].
    google.protobuf.Timestamp latest_vehicle_end_time = 5;

    // Cost of the solution, broken down by cost-related request fields.
    // The keys are proto paths, relative to the input OptimizeToursRequest,
    // e.g. "model.shipments.pickups.cost", and the values are the total cost
    // generated by the corresponding cost field, aggregated over the whole
    // solution. In other words, costs["model.shipments.pickups.cost"] is the
    // sum of all pickup costs over the solution. All costs defined in the model
    // are reported in detail here with the exception of costs related to
    // TransitionAttributes that are only reported in an aggregated way as of
    // 2022/01.
    map<string, double> costs = 10;

    // Total cost of the solution. The sum of all values in the costs map.
    double total_cost = 6;
  }

  // Routes computed for each vehicle; the i-th route corresponds to the i-th
  // vehicle in the model.
  repeated ShipmentRoute routes = 1;

  // Copy of the
  // [OptimizeToursRequest.label][google.maps.routeoptimization.v1.OptimizeToursRequest.label],
  // if a label was specified in the request.
  string request_label = 3;

  // The list of all shipments skipped.
  repeated SkippedShipment skipped_shipments = 4;

  // List of all the validation errors that we were able to detect
  // independently. See the "MULTIPLE ERRORS" explanation for the
  // [OptimizeToursValidationError][google.maps.routeoptimization.v1.OptimizeToursValidationError]
  // message. Instead of errors, this will include warnings in the case
  // `solving_mode` is `DEFAULT_SOLVE`.
  repeated OptimizeToursValidationError validation_errors = 5;

  // Duration, distance and usage metrics for this solution.
  Metrics metrics = 6;
}

// A shipment model contains a set of shipments which must be performed by a
// set of vehicles, while minimizing the overall cost, which is the sum of:
//
// * the cost of routing the vehicles (sum of cost per total time, cost per
//   travel time, and fixed cost over all vehicles).
// * the unperformed shipment penalties.
// * the cost of the global duration of the shipments
message ShipmentModel {
  // Specifies a duration and distance matrix from visit and vehicle start
  // locations to visit and vehicle end locations.
  message DurationDistanceMatrix {
    // Specifies a row of the duration and distance matrix.
    message Row {
      // Duration values for a given row. It must have as many elements as
      // [ShipmentModel.duration_distance_matrix_dst_tags][google.maps.routeoptimization.v1.ShipmentModel.duration_distance_matrix_dst_tags].
      repeated google.protobuf.Duration durations = 1;

      // Distance values for a given row. If no costs or constraints refer to
      // distances in the model, this can be left empty; otherwise it must have
      // as many elements as `durations`.
      repeated double meters = 2;
    }

    // Specifies the rows of the duration and distance matrix. It must have as
    // many elements as
    // [ShipmentModel.duration_distance_matrix_src_tags][google.maps.routeoptimization.v1.ShipmentModel.duration_distance_matrix_src_tags].
    repeated Row rows = 1;

    // Tag defining to which vehicles this duration and distance matrix applies.
    // If empty, this applies to all vehicles, and there can only be a single
    // matrix.
    //
    // Each vehicle start must match exactly one matrix, i.e. exactly one of
    // their `start_tags` field must match the `vehicle_start_tag` of a matrix
    // (and of that matrix only).
    //
    // All matrices must have a different `vehicle_start_tag`.
    string vehicle_start_tag = 2;
  }

  // A precedence rule between two events (each event is the pickup or the
  // delivery of a shipment): the "second" event has to start at least
  // `offset_duration` after "first" has started.
  //
  // Several precedences can refer to the same (or related) events, e.g.,
  // "pickup of B happens after delivery of A" and "pickup of C happens after
  // pickup of B".
  //
  // Furthermore, precedences only apply when both shipments are performed and
  // are otherwise ignored.
  message PrecedenceRule {
    // Shipment index of the "first" event. This field must be specified.
    optional int32 first_index = 1;

    // Indicates if the "first" event is a delivery.
    bool first_is_delivery = 3;

    // Shipment index of the "second" event. This field must be specified.
    optional int32 second_index = 2;

    // Indicates if the "second" event is a delivery.
    bool second_is_delivery = 4;

    // The offset between the "first" and "second" event. It can be negative.
    google.protobuf.Duration offset_duration = 5;
  }

  // Set of shipments which must be performed in the model.
  repeated Shipment shipments = 1;

  // Set of vehicles which can be used to perform visits.
  repeated Vehicle vehicles = 2;

  // Constrains the maximum number of active vehicles. A vehicle is active if
  // its route performs at least one shipment. This can be used to limit the
  // number of routes in the case where there are fewer drivers than
  // vehicles and that the fleet of vehicles is heterogeneous. The optimization
  // will then select the best subset of vehicles to use.
  // Must be strictly positive.
  optional int32 max_active_vehicles = 4;

  // Global start and end time of the model: no times outside of this range
  // can be considered valid.
  //
  // The model's time span must be less than a year, i.e. the `global_end_time`
  // and the `global_start_time` must be within 31536000 seconds of each other.
  //
  // When using `cost_per_*hour` fields, you might want to set this window to a
  // smaller interval to increase performance (eg. if you model a single day,
  // you should set the global time limits to that day).
  // If unset, 00:00:00 UTC, January 1, 1970 (i.e. seconds: 0, nanos: 0) is used
  // as default.
  google.protobuf.Timestamp global_start_time = 5;

  // If unset, 00:00:00 UTC, January 1, 1971 (i.e. seconds: 31536000, nanos: 0)
  // is used as default.
  google.protobuf.Timestamp global_end_time = 6;

  // The "global duration" of the overall plan is the difference between the
  // earliest effective start time and the latest effective end time of
  // all vehicles. Users can assign a cost per hour to that quantity to try
  // and optimize for earliest job completion, for example. This cost must be in
  // the same unit as
  // [Shipment.penalty_cost][google.maps.routeoptimization.v1.Shipment.penalty_cost].
  double global_duration_cost_per_hour = 7;

  // Specifies duration and distance matrices used in the model. If this field
  // is empty, Google Maps or geodesic distances will be used instead, depending
  // on the value of the `use_geodesic_distances` field. If it is not empty,
  // `use_geodesic_distances` cannot be true and neither
  // `duration_distance_matrix_src_tags` nor `duration_distance_matrix_dst_tags`
  // can be empty.
  //
  // Usage examples:
  //
  // * There are two locations: locA and locB.
  // * 1 vehicle starting its route at locA and ending it at locA.
  // * 1 pickup visit request at locB.
  //
  // ```
  // model {
  //   vehicles { start_tags: "locA"  end_tags: "locA" }
  //   shipments { pickups { tags: "locB" } }
  //   duration_distance_matrix_src_tags: "locA"
  //   duration_distance_matrix_src_tags: "locB"
  //   duration_distance_matrix_dst_tags: "locA"
  //   duration_distance_matrix_dst_tags: "locB"
  //   duration_distance_matrices {
  //     rows {  # from: locA
  //       durations { seconds: 0 }   meters: 0    # to: locA
  //       durations { seconds: 100 } meters: 1000 # to: locB
  //     }
  //     rows {  # from: locB
  //       durations { seconds: 102 } meters: 990 # to: locA
  //       durations { seconds: 0 }   meters: 0   # to: locB
  //     }
  //   }
  // }
  // ```
  //
  //
  // * There are three locations: locA, locB and locC.
  // * 1 vehicle starting its route at locA and ending it at locB, using
  //   matrix "fast".
  // * 1 vehicle starting its route at locB and ending it at locB, using
  //   matrix "slow".
  // * 1 vehicle starting its route at locB and ending it at locB, using
  //   matrix "fast".
  // * 1 pickup visit request at locC.
  //
  // ```
  // model {
  //   vehicles { start_tags: "locA" end_tags: "locB" start_tags: "fast" }
  //   vehicles { start_tags: "locB" end_tags: "locB" start_tags: "slow" }
  //   vehicles { start_tags: "locB" end_tags: "locB" start_tags: "fast" }
  //   shipments { pickups { tags: "locC" } }
  //   duration_distance_matrix_src_tags: "locA"
  //   duration_distance_matrix_src_tags: "locB"
  //   duration_distance_matrix_src_tags: "locC"
  //   duration_distance_matrix_dst_tags: "locB"
  //   duration_distance_matrix_dst_tags: "locC"
  //   duration_distance_matrices {
  //     vehicle_start_tag: "fast"
  //     rows {  # from: locA
  //       durations { seconds: 1000 } meters: 2000 # to: locB
  //       durations { seconds: 600 }  meters: 1000 # to: locC
  //     }
  //     rows {  # from: locB
  //       durations { seconds: 0 }   meters: 0    # to: locB
  //       durations { seconds: 700 } meters: 1200 # to: locC
  //     }
  //     rows {  # from: locC
  //       durations { seconds: 702 } meters: 1190 # to: locB
  //       durations { seconds: 0 }   meters: 0    # to: locC
  //     }
  //   }
  //   duration_distance_matrices {
  //     vehicle_start_tag: "slow"
  //     rows {  # from: locA
  //       durations { seconds: 1800 } meters: 2001 # to: locB
  //       durations { seconds: 900 }  meters: 1002 # to: locC
  //     }
  //     rows {  # from: locB
  //       durations { seconds: 0 }    meters: 0    # to: locB
  //       durations { seconds: 1000 } meters: 1202 # to: locC
  //     }
  //     rows {  # from: locC
  //       durations { seconds: 1001 } meters: 1195 # to: locB
  //       durations { seconds: 0 }    meters: 0    # to: locC
  //     }
  //   }
  // }
  // ```
  repeated DurationDistanceMatrix duration_distance_matrices = 8;

  // Tags defining the sources of the duration and distance matrices;
  // `duration_distance_matrices(i).rows(j)` defines durations and distances
  // from visits with tag `duration_distance_matrix_src_tags(j)` to other visits
  // in matrix i.
  //
  // Tags correspond to
  // [VisitRequest.tags][google.maps.routeoptimization.v1.Shipment.VisitRequest.tags]
  // or
  // [Vehicle.start_tags][google.maps.routeoptimization.v1.Vehicle.start_tags].
  // A given `VisitRequest` or `Vehicle` must match exactly one tag in this
  // field. Note that a `Vehicle`'s source, destination and matrix tags may be
  // the same; similarly a `VisitRequest`'s source and destination tags may be
  // the same. All tags must be different and cannot be empty strings. If this
  // field is not empty, then `duration_distance_matrices` must not be empty.
  repeated string duration_distance_matrix_src_tags = 9;

  // Tags defining the destinations of the duration and distance matrices;
  // `duration_distance_matrices(i).rows(j).durations(k)` (resp.
  // `duration_distance_matrices(i).rows(j).meters(k))` defines the duration
  // (resp. the distance) of the travel from visits with tag
  // `duration_distance_matrix_src_tags(j)` to visits with tag
  // `duration_distance_matrix_dst_tags(k)` in matrix i.
  //
  // Tags correspond to
  // [VisitRequest.tags][google.maps.routeoptimization.v1.Shipment.VisitRequest.tags]
  // or
  // [Vehicle.start_tags][google.maps.routeoptimization.v1.Vehicle.start_tags].
  // A given `VisitRequest` or `Vehicle` must match exactly one tag in this
  // field. Note that a `Vehicle`'s source, destination and matrix tags may be
  // the same; similarly a `VisitRequest`'s source and destination tags may be
  // the same. All tags must be different and cannot be empty strings. If this
  // field is not empty, then `duration_distance_matrices` must not be empty.
  repeated string duration_distance_matrix_dst_tags = 10;

  // Transition attributes added to the model.
  repeated TransitionAttributes transition_attributes = 11;

  // Sets of incompatible shipment_types (see `ShipmentTypeIncompatibility`).
  repeated ShipmentTypeIncompatibility shipment_type_incompatibilities = 12;

  // Sets of `shipment_type` requirements (see `ShipmentTypeRequirement`).
  repeated ShipmentTypeRequirement shipment_type_requirements = 13;

  // Set of precedence rules which must be enforced in the model.
  repeated PrecedenceRule precedence_rules = 14;
}

// The shipment of a single item, from one of its pickups to one of its
// deliveries. For the shipment to be considered as performed, a unique vehicle
// must visit one of its pickup locations (and decrease its spare capacities
// accordingly), then visit one of its delivery locations later on (and
// therefore re-increase its spare capacities accordingly).
message Shipment {
  // Request for a visit which can be done by a vehicle: it has a geo-location
  // (or two, see below), opening and closing times represented by time windows,
  // and a service duration time (time spent by the vehicle once it has arrived
  // to pickup or drop off goods).
  message VisitRequest {
    // The geo-location where the vehicle arrives when performing this
    // `VisitRequest`. If the shipment model has duration distance matrices,
    // `arrival_location` must not be specified.
    google.type.LatLng arrival_location = 1;

    // The waypoint where the vehicle arrives when performing this
    // `VisitRequest`. If the shipment model has duration distance matrices,
    // `arrival_waypoint` must not be specified.
    Waypoint arrival_waypoint = 2;

    // The geo-location where the vehicle departs after completing this
    // `VisitRequest`. Can be omitted if it is the same as `arrival_location`.
    // If the shipment model has duration distance matrices,
    // `departure_location` must not be specified.
    google.type.LatLng departure_location = 3;

    // The waypoint where the vehicle departs after completing this
    // `VisitRequest`. Can be omitted if it is the same as `arrival_waypoint`.
    // If the shipment model has duration distance matrices,
    // `departure_waypoint` must not be specified.
    Waypoint departure_waypoint = 4;

    // Specifies tags attached to the visit request.
    // Empty or duplicate strings are not allowed.
    repeated string tags = 5;

    // Time windows which constrain the arrival time at a visit.
    // Note that a vehicle may depart outside of the arrival time window, i.e.
    // arrival time + duration do not need to be inside a time window. This can
    // result in waiting time if the vehicle arrives before
    // [TimeWindow.start_time][google.maps.routeoptimization.v1.TimeWindow.start_time].
    //
    // The absence of `TimeWindow` means that the vehicle can perform this visit
    // at any time.
    //
    // Time windows must be disjoint, i.e. no time window must overlap with or
    // be adjacent to another, and they must be in increasing order.
    //
    // `cost_per_hour_after_soft_end_time` and `soft_end_time` can only
    // be set if there is a single time window.
    repeated TimeWindow time_windows = 6;

    // Duration of the visit, i.e. time spent by the vehicle between arrival
    // and departure (to be added to the possible waiting time; see
    // `time_windows`).
    google.protobuf.Duration duration = 7;

    // Cost to service this visit request on a vehicle route. This can be used
    // to pay different costs for each alternative pickup or delivery of a
    // shipment. This cost must be in the same unit as `Shipment.penalty_cost`
    // and must not be negative.
    double cost = 8;

    // Load demands of this visit request. This is just like
    // [Shipment.load_demands][google.maps.routeoptimization.v1.Shipment.load_demands]
    // field, except that it only applies to this
    // [VisitRequest][google.maps.routeoptimization.v1.Shipment.VisitRequest]
    // instead of the whole
    // [Shipment][google.maps.routeoptimization.v1.Shipment]. The demands listed
    // here are added to the demands listed in
    // [Shipment.load_demands][google.maps.routeoptimization.v1.Shipment.load_demands].
    map<string, Load> load_demands = 12;

    // Specifies the types of the visit. This may be used to allocate additional
    // time required for a vehicle to complete this visit (see
    // [Vehicle.extra_visit_duration_for_visit_type][google.maps.routeoptimization.v1.Vehicle.extra_visit_duration_for_visit_type]).
    //
    // A type can only appear once.
    repeated string visit_types = 10;

    // Specifies a label for this `VisitRequest`. This label is reported in the
    // response as `visit_label` in the corresponding
    // [ShipmentRoute.Visit][google.maps.routeoptimization.v1.ShipmentRoute.Visit].
    string label = 11;
  }

  // When performing a visit, a predefined amount may be added to the vehicle
  // load if it's a pickup, or subtracted if it's a delivery. This message
  // defines such amount. See
  // [load_demands][google.maps.routeoptimization.v1.Shipment.load_demands].
  message Load {
    // The amount by which the load of the vehicle performing the corresponding
    // visit will vary. Since it is an integer, users are advised to choose an
    // appropriate unit to avoid loss of precision. Must be ≥ 0.
    int64 amount = 2;
  }

  // The user-defined display name of the shipment.
  // It can be up to 63 characters long and may use UTF-8 characters.
  string display_name = 16;

  // Set of pickup alternatives associated to the shipment. If not specified,
  // the vehicle only needs to visit a location corresponding to the deliveries.
  repeated VisitRequest pickups = 1;

  // Set of delivery alternatives associated to the shipment. If not specified,
  // the vehicle only needs to visit a location corresponding to the pickups.
  repeated VisitRequest deliveries = 2;

  // Load demands of the shipment (for example weight, volume, number of
  // pallets etc). The keys in the map should be identifiers describing the type
  // of the corresponding load, ideally also including the units.
  // For example: "weight_kg", "volume_gallons", "pallet_count", etc.
  // If a given key does not appear in the map, the corresponding load is
  // considered as null.
  map<string, Load> load_demands = 14;

  // If the shipment is not completed, this penalty is added to the overall
  // cost of the routes. A shipment is considered completed if one of its pickup
  // and delivery alternatives is visited. The cost may be expressed in the
  // same unit used for all other cost-related fields in the model and must be
  // positive.
  //
  // *IMPORTANT*: If this penalty is not specified, it is considered infinite,
  // i.e. the shipment must be completed.
  optional double penalty_cost = 4;

  // The set of vehicles that may perform this shipment. If empty, all vehicles
  // may perform it. Vehicles are given by their index in the `ShipmentModel`'s
  // `vehicles` list.
  repeated int32 allowed_vehicle_indices = 5;

  // Specifies the cost that is incurred when this shipment is delivered by each
  // vehicle. If specified, it must have EITHER:
  //
  //   * the same number of elements as `costs_per_vehicle_indices`.
  //     `costs_per_vehicle[i]` corresponds to vehicle
  //     `costs_per_vehicle_indices[i]` of the model.
  //   * the same number of elements as there are vehicles in the model. The
  //     i-th element corresponds to vehicle #i of the model.
  //
  // These costs must be in the same unit as `penalty_cost` and must not be
  // negative. Leave this field empty, if there are no such costs.
  repeated double costs_per_vehicle = 6;

  // Indices of the vehicles to which `costs_per_vehicle` applies. If non-empty,
  // it must have the same number of elements as `costs_per_vehicle`. A vehicle
  // index may not be specified more than once. If a vehicle is excluded from
  // `costs_per_vehicle_indices`, its cost is zero.
  repeated int32 costs_per_vehicle_indices = 7;

  // Specifies the maximum relative detour time compared to the shortest path
  // from pickup to delivery. If specified, it must be nonnegative, and the
  // shipment must contain at least a pickup and a delivery.
  //
  // For example, let t be the shortest time taken to go from the selected
  // pickup alternative directly to the selected delivery alternative. Then
  // setting `pickup_to_delivery_relative_detour_limit` enforces:
  //
  // ```
  // start_time(delivery) - start_time(pickup) <=
  // std::ceil(t * (1.0 + pickup_to_delivery_relative_detour_limit))
  // ```
  //
  // If both relative and absolute limits are specified on the same shipment,
  // the more constraining limit is used for each possible pickup/delivery pair.
  // As of 2017/10, detours are only supported when travel durations do not
  // depend on vehicles.
  optional double pickup_to_delivery_relative_detour_limit = 8;

  // Specifies the maximum absolute detour time compared to the shortest path
  // from pickup to delivery. If specified, it must be nonnegative, and the
  // shipment must contain at least a pickup and a delivery.
  //
  // For example, let t be the shortest time taken to go from the selected
  // pickup alternative directly to the selected delivery alternative. Then
  // setting `pickup_to_delivery_absolute_detour_limit` enforces:
  //
  // ```
  // start_time(delivery) - start_time(pickup) <=
  // t + pickup_to_delivery_absolute_detour_limit
  // ```
  //
  // If both relative and absolute limits are specified on the same shipment,
  // the more constraining limit is used for each possible pickup/delivery pair.
  // As of 2017/10, detours are only supported when travel durations do not
  // depend on vehicles.
  google.protobuf.Duration pickup_to_delivery_absolute_detour_limit = 9;

  // Specifies the maximum duration from start of pickup to start of delivery of
  // a shipment. If specified, it must be nonnegative, and the shipment must
  // contain at least a pickup and a delivery. This does not depend on which
  // alternatives are selected for pickup and delivery, nor on vehicle speed.
  // This can be specified alongside maximum detour constraints: the solution
  // will respect both specifications.
  google.protobuf.Duration pickup_to_delivery_time_limit = 10;

  // Non-empty string specifying a "type" for this shipment.
  // This feature can be used to define incompatibilities or requirements
  // between `shipment_types` (see `shipment_type_incompatibilities` and
  // `shipment_type_requirements` in `ShipmentModel`).
  //
  // Differs from `visit_types` which is specified for a single visit: All
  // pickup/deliveries belonging to the same shipment share the same
  // `shipment_type`.
  string shipment_type = 11;

  // Specifies a label for this shipment. This label is reported in the response
  // in the `shipment_label` of the corresponding
  // [ShipmentRoute.Visit][google.maps.routeoptimization.v1.ShipmentRoute.Visit].
  string label = 12;

  // If true, skip this shipment, but don't apply a `penalty_cost`.
  //
  // Ignoring a shipment results in a validation error when there are any
  // `shipment_type_requirements` in the model.
  //
  // Ignoring a shipment that is performed in `injected_first_solution_routes`
  // or `injected_solution_constraint` is permitted; the solver removes the
  // related pickup/delivery visits from the performing route.
  // `precedence_rules` that reference ignored shipments will also be ignored.
  bool ignore = 13;
}

// Specifies incompatibilties between shipments depending on their
// shipment_type. The appearance of incompatible shipments on the same route is
// restricted based on the incompatibility mode.
message ShipmentTypeIncompatibility {
  // Modes defining how the appearance of incompatible shipments are restricted
  // on the same route.
  enum IncompatibilityMode {
    // Unspecified incompatibility mode. This value should never be used.
    INCOMPATIBILITY_MODE_UNSPECIFIED = 0;

    // In this mode, two shipments with incompatible types can never share the
    // same vehicle.
    NOT_PERFORMED_BY_SAME_VEHICLE = 1;

    // For two shipments with incompatible types with the
    // `NOT_IN_SAME_VEHICLE_SIMULTANEOUSLY` incompatibility mode:
    //
    // * If both are pickups only (no deliveries) or deliveries only (no
    //   pickups), they cannot share the same vehicle at all.
    // * If one of the shipments has a delivery and the other a pickup, the two
    //   shipments can share the same vehicle iff the former shipment is
    //   delivered before the latter is picked up.
    NOT_IN_SAME_VEHICLE_SIMULTANEOUSLY = 2;
  }

  // List of incompatible types. Two shipments having different `shipment_types`
  // among those listed are "incompatible".
  repeated string types = 1;

  // Mode applied to the incompatibility.
  IncompatibilityMode incompatibility_mode = 2;
}

// Specifies requirements between shipments based on their shipment_type.
// The specifics of the requirement are defined by the requirement mode.
message ShipmentTypeRequirement {
  // Modes defining the appearance of dependent shipments on a route.
  enum RequirementMode {
    // Unspecified requirement mode. This value should never be used.
    REQUIREMENT_MODE_UNSPECIFIED = 0;

    // In this mode, all "dependent" shipments must share the same vehicle as at
    // least one of their "required" shipments.
    PERFORMED_BY_SAME_VEHICLE = 1;

    // With the `IN_SAME_VEHICLE_AT_PICKUP_TIME` mode, all "dependent"
    // shipments need to have at least one "required" shipment on their vehicle
    // at the time of their pickup.
    //
    // A "dependent" shipment pickup must therefore have either:
    //
    // * A delivery-only "required" shipment delivered on the route after, or
    // * A "required" shipment picked up on the route before it, and if the
    //   "required" shipment has a delivery, this delivery must be performed
    //   after the "dependent" shipment's pickup.
    IN_SAME_VEHICLE_AT_PICKUP_TIME = 2;

    // Same as before, except the "dependent" shipments need to have a
    // "required" shipment on their vehicle at the time of their *delivery*.
    IN_SAME_VEHICLE_AT_DELIVERY_TIME = 3;
  }

  // List of alternative shipment types required by the
  // `dependent_shipment_types`.
  repeated string required_shipment_type_alternatives = 1;

  // All shipments with a type in the `dependent_shipment_types` field require
  // at least one shipment of type `required_shipment_type_alternatives` to be
  // visited on the same route.
  //
  // NOTE: Chains of requirements such that a `shipment_type` depends on itself
  // are not allowed.
  repeated string dependent_shipment_types = 2;

  // Mode applied to the requirement.
  RequirementMode requirement_mode = 3;
}

// Encapsulates a set of optional conditions to satisfy when calculating
// vehicle routes. This is similar to `RouteModifiers` in the Google Maps
// Platform Routes Preferred API; see:
// https://developers.google.com/maps/documentation/routes/reference/rest/v2/RouteModifiers.
message RouteModifiers {
  // Specifies whether to avoid toll roads where reasonable. Preference will be
  // given to routes not containing toll roads. Applies only to motorized travel
  // modes.
  bool avoid_tolls = 2;

  // Specifies whether to avoid highways where reasonable. Preference will be
  // given to routes not containing highways. Applies only to motorized travel
  // modes.
  bool avoid_highways = 3;

  // Specifies whether to avoid ferries where reasonable. Preference will be
  // given to routes not containing travel by ferries. Applies only to motorized
  // travel modes.
  bool avoid_ferries = 4;

  // Optional. Specifies whether to avoid navigating indoors where reasonable.
  // Preference will be given to routes not containing indoor navigation.
  // Applies only to the `WALKING` travel mode.
  bool avoid_indoor = 5 [(google.api.field_behavior) = OPTIONAL];
}

// Models a vehicle in a shipment problem. Solving a shipment problem will
// build a route starting from `start_location` and ending at `end_location`
// for this vehicle. A route is a sequence of visits (see `ShipmentRoute`).
message Vehicle {
  // Travel modes which can be used by vehicles.
  //
  // These should be a subset of the Google Maps Platform Routes Preferred API
  // travel modes, see:
  // https://developers.google.com/maps/documentation/routes_preferred/reference/rest/Shared.Types/RouteTravelMode.
  enum TravelMode {
    // Unspecified travel mode, equivalent to `DRIVING`.
    TRAVEL_MODE_UNSPECIFIED = 0;

    // Travel mode corresponding to driving directions (car, ...).
    DRIVING = 1;

    // Travel mode corresponding to walking directions.
    WALKING = 2;
  }

  // Policy on how a vehicle can be unloaded. Applies only to shipments having
  // both a pickup and a delivery.
  //
  // Other shipments are free to occur anywhere on the route independent of
  // `unloading_policy`.
  enum UnloadingPolicy {
    // Unspecified unloading policy; deliveries must just occur after their
    // corresponding pickups.
    UNLOADING_POLICY_UNSPECIFIED = 0;

    // Deliveries must occur in reverse order of pickups
    LAST_IN_FIRST_OUT = 1;

    // Deliveries must occur in the same order as pickups
    FIRST_IN_FIRST_OUT = 2;
  }

  // Defines a load limit applying to a vehicle, e.g. "this truck may only
  // carry up to 3500 kg". See
  // [load_limits][google.maps.routeoptimization.v1.Vehicle.load_limits].
  message LoadLimit {
    // Interval of acceptable load amounts.
    message Interval {
      // A minimum acceptable load. Must be ≥ 0.
      // If they're both specified,
      // [min][google.maps.routeoptimization.v1.Vehicle.LoadLimit.Interval.min]
      // must be ≤
      // [max][google.maps.routeoptimization.v1.Vehicle.LoadLimit.Interval.max].
      int64 min = 1;

      // A maximum acceptable load. Must be ≥ 0. If unspecified, the maximum
      // load is unrestricted by this message.
      // If they're both specified,
      // [min][google.maps.routeoptimization.v1.Vehicle.LoadLimit.Interval.min]
      // must be ≤
      // [max][google.maps.routeoptimization.v1.Vehicle.LoadLimit.Interval.max].
      optional int64 max = 2;
    }

    // The maximum acceptable amount of load.
    optional int64 max_load = 1;

    // A soft limit of the load. See
    // [cost_per_unit_above_soft_max][google.maps.routeoptimization.v1.Vehicle.LoadLimit.cost_per_unit_above_soft_max].
    int64 soft_max_load = 2;

    // If the load ever exceeds
    // [soft_max_load][google.maps.routeoptimization.v1.Vehicle.LoadLimit.soft_max_load]
    // along this vehicle's route, the following cost penalty applies (only once
    // per vehicle): (load -
    // [soft_max_load][google.maps.routeoptimization.v1.Vehicle.LoadLimit.soft_max_load])
    // * [cost_per_unit_above_soft_max][google.maps.routeoptimization.v1.Vehicle.LoadLimit.cost_per_unit_above_soft_max]. All costs
    // add up and must be in the same unit as
    // [Shipment.penalty_cost][google.maps.routeoptimization.v1.Shipment.penalty_cost].
    double cost_per_unit_above_soft_max = 3;

    // The acceptable load interval of the vehicle at the start of the route.
    Interval start_load_interval = 4;

    // The acceptable load interval of the vehicle at the end of the route.
    Interval end_load_interval = 5;
  }

  // A limit defining a maximum duration of the route of a vehicle. It can be
  // either hard or soft.
  //
  // When a soft limit field is defined, both the soft max threshold and its
  // associated cost must be defined together.
  message DurationLimit {
    // A hard limit constraining the duration to be at most max_duration.
    google.protobuf.Duration max_duration = 1;

    // A soft limit not enforcing a maximum duration limit, but when violated
    // makes the route incur a cost. This cost adds up to other costs defined in
    // the model, with the same unit.
    //
    // If defined, `soft_max_duration` must be nonnegative. If max_duration is
    // also defined, `soft_max_duration` must be less than max_duration.
    google.protobuf.Duration soft_max_duration = 2;

    // Cost per hour incurred if the `soft_max_duration` threshold is violated.
    // The additional cost is 0 if the duration is under the threshold,
    // otherwise the cost depends on the duration as follows:
    // ```
    //   cost_per_hour_after_soft_max * (duration - soft_max_duration)
    // ```
    // The cost must be nonnegative.
    optional double cost_per_hour_after_soft_max = 3;

    // A soft limit not enforcing a maximum duration limit, but when violated
    // makes the route incur a cost, quadratic in the duration. This cost adds
    // up to other costs defined in the model, with the same unit.
    //
    // If defined, `quadratic_soft_max_duration` must be nonnegative. If
    // `max_duration` is also defined, `quadratic_soft_max_duration` must be
    // less than `max_duration`, and the difference must be no larger than one
    // day:
    //
    //    `max_duration - quadratic_soft_max_duration <= 86400 seconds`
    google.protobuf.Duration quadratic_soft_max_duration = 4;

    // Cost per square hour incurred if the
    // `quadratic_soft_max_duration` threshold is violated.
    //
    // The additional cost is 0 if the duration is under the threshold,
    // otherwise the cost depends on the duration as follows:
    //
    // ```
    //   cost_per_square_hour_after_quadratic_soft_max *
    //   (duration - quadratic_soft_max_duration)^2
    // ```
    //
    // The cost must be nonnegative.
    optional double cost_per_square_hour_after_quadratic_soft_max = 5;
  }

  // The user-defined display name of the vehicle.
  // It can be up to 63 characters long and may use UTF-8 characters.
  string display_name = 32;

  // The travel mode which affects the roads usable by the vehicle and its
  // speed. See also `travel_duration_multiple`.
  TravelMode travel_mode = 1;

  // A set of conditions to satisfy that affect the way routes are calculated
  // for the given vehicle.
  RouteModifiers route_modifiers = 2;

  // Geographic location where the vehicle starts before picking up any
  // shipments. If not specified, the vehicle starts at its first pickup.
  // If the shipment model has duration and distance matrices, `start_location`
  // must not be specified.
  google.type.LatLng start_location = 3;

  // Waypoint representing a geographic location where the vehicle starts before
  // picking up any shipments. If neither `start_waypoint` nor `start_location`
  // is specified, the vehicle starts at its first pickup.
  // If the shipment model has duration and distance matrices, `start_waypoint`
  // must not be specified.
  Waypoint start_waypoint = 4;

  // Geographic location where the vehicle ends after it has completed its last
  // `VisitRequest`. If not specified the vehicle's `ShipmentRoute` ends
  // immediately when it completes its last `VisitRequest`.
  // If the shipment model has duration and distance matrices, `end_location`
  // must not be specified.
  google.type.LatLng end_location = 5;

  // Waypoint representing a geographic location where the vehicle ends after
  // it has completed its last `VisitRequest`. If neither `end_waypoint` nor
  // `end_location` is specified, the vehicle's `ShipmentRoute` ends immediately
  // when it completes its last `VisitRequest`.
  // If the shipment model has duration and distance matrices, `end_waypoint`
  // must not be specified.
  Waypoint end_waypoint = 6;

  // Specifies tags attached to the start of the vehicle's route.
  //
  // Empty or duplicate strings are not allowed.
  repeated string start_tags = 7;

  // Specifies tags attached to the end of the vehicle's route.
  //
  // Empty or duplicate strings are not allowed.
  repeated string end_tags = 8;

  // Time windows during which the vehicle may depart its start location.
  // They must be within the global time limits (see
  // [ShipmentModel.global_*][google.maps.routeoptimization.v1.ShipmentModel.global_start_time]
  // fields). If unspecified, there is no limitation besides those global time
  // limits.
  //
  // Time windows belonging to the same repeated field must be disjoint, i.e. no
  // time window can overlap with or be adjacent to another, and they must be in
  // chronological order.
  //
  // `cost_per_hour_after_soft_end_time` and `soft_end_time` can only be set if
  // there is a single time window.
  repeated TimeWindow start_time_windows = 9;

  // Time windows during which the vehicle may arrive at its end location.
  // They must be within the global time limits (see
  // [ShipmentModel.global_*][google.maps.routeoptimization.v1.ShipmentModel.global_start_time]
  // fields). If unspecified, there is no limitation besides those global time
  // limits.
  //
  // Time windows belonging to the same repeated field must be disjoint, i.e. no
  // time window can overlap with or be adjacent to another, and they must be in
  // chronological order.
  //
  // `cost_per_hour_after_soft_end_time` and `soft_end_time` can only be set if
  // there is a single time window.
  repeated TimeWindow end_time_windows = 10;

  // Specifies a multiplicative factor that can be used to increase or decrease
  // travel times of this vehicle. For example, setting this to 2.0 means
  // that this vehicle is slower and has travel times that are twice what they
  // are for standard vehicles. This multiple does not affect visit durations.
  // It does affect cost if `cost_per_hour` or `cost_per_traveled_hour` are
  // specified. This must be in the range [0.001, 1000.0]. If unset, the vehicle
  // is standard, and this multiple is considered 1.0.
  //
  // WARNING: Travel times will be rounded to the nearest second after this
  // multiple is applied but before performing any numerical operations, thus,
  // a small multiple may result in a loss of precision.
  //
  // See also `extra_visit_duration_for_visit_type` below.
  optional double travel_duration_multiple = 11;

  // Unloading policy enforced on the vehicle.
  UnloadingPolicy unloading_policy = 12;

  // Capacities of the vehicle (weight, volume, # of pallets for example).
  // The keys in the map are the identifiers of the type of load, consistent
  // with the keys of the
  // [Shipment.load_demands][google.maps.routeoptimization.v1.Shipment.load_demands]
  // field. If a given key is absent from this map, the corresponding capacity
  // is considered to be limitless.
  map<string, LoadLimit> load_limits = 30;

  // Vehicle costs: all costs add up and must be in the same unit as
  // [Shipment.penalty_cost][google.maps.routeoptimization.v1.Shipment.penalty_cost].
  //
  // Cost per hour of the vehicle route. This cost is applied to the total time
  // taken by the route, and includes travel time, waiting time, and visit time.
  // Using `cost_per_hour` instead of just `cost_per_traveled_hour` may result
  // in additional latency.
  double cost_per_hour = 16;

  // Cost per traveled hour of the vehicle route. This cost is applied only to
  // travel time taken by the route (i.e., that reported in
  // [ShipmentRoute.transitions][google.maps.routeoptimization.v1.ShipmentRoute.transitions]),
  // and excludes waiting time and visit time.
  double cost_per_traveled_hour = 17;

  // Cost per kilometer of the vehicle route. This cost is applied to the
  // distance reported in the
  // [ShipmentRoute.transitions][google.maps.routeoptimization.v1.ShipmentRoute.transitions]
  // and does not apply to any distance implicitly traveled from the
  // `arrival_location` to the `departure_location` of a single `VisitRequest`.
  double cost_per_kilometer = 18;

  // Fixed cost applied if this vehicle is used to handle a shipment.
  double fixed_cost = 19;

  // This field only applies to vehicles when their route does not serve any
  // shipments. It indicates if the vehicle should be considered as used or not
  // in this case.
  //
  // If true, the vehicle goes from its start to its end location even if it
  // doesn't serve any shipments, and time and distance costs resulting from its
  // start --> end travel are taken into account.
  //
  // Otherwise, it doesn't travel from its start to its end location, and no
  // `break_rule` or delay (from `TransitionAttributes`) are scheduled for this
  // vehicle. In this case, the vehicle's `ShipmentRoute` doesn't contain any
  // information except for the vehicle index and label.
  bool used_if_route_is_empty = 20;

  // Limit applied to the total duration of the vehicle's route. In a given
  // `OptimizeToursResponse`, the route duration of a vehicle is the
  // difference between its `vehicle_end_time` and `vehicle_start_time`.
  DurationLimit route_duration_limit = 21;

  // Limit applied to the travel duration of the vehicle's route. In a given
  // `OptimizeToursResponse`, the route travel duration is the sum of all its
  // [transitions.travel_duration][google.maps.routeoptimization.v1.ShipmentRoute.Transition.travel_duration].
  DurationLimit travel_duration_limit = 22;

  // Limit applied to the total distance of the vehicle's route. In a given
  // `OptimizeToursResponse`, the route distance is the sum of all its
  // [transitions.travel_distance_meters][google.maps.routeoptimization.v1.ShipmentRoute.Transition.travel_distance_meters].
  DistanceLimit route_distance_limit = 23;

  // Specifies a map from visit_types strings to durations. The duration is time
  // in addition to
  // [VisitRequest.duration][google.maps.routeoptimization.v1.Shipment.VisitRequest.duration]
  // to be taken at visits with the specified `visit_types`. This extra visit
  // duration adds cost if `cost_per_hour` is specified. Keys (i.e.
  // `visit_types`) cannot be empty strings.
  //
  // If a visit request has multiple types, a duration will be added for each
  // type in the map.
  map<string, google.protobuf.Duration> extra_visit_duration_for_visit_type =
      24;

  // Describes the break schedule to be enforced on this vehicle.
  // If empty, no breaks will be scheduled for this vehicle.
  BreakRule break_rule = 25;

  // Specifies a label for this vehicle. This label is reported in the response
  // as the `vehicle_label` of the corresponding
  // [ShipmentRoute][google.maps.routeoptimization.v1.ShipmentRoute].
  string label = 27;

  // If true, `used_if_route_is_empty` must be false, and this vehicle will
  // remain unused.
  //
  // If a shipment is performed by an ignored vehicle in
  // `injected_first_solution_routes`, it is skipped in the first solution but
  // is free to be performed in the response.
  //
  // If a shipment is performed by an ignored vehicle in
  // `injected_solution_constraint` and any related pickup/delivery is
  // constrained to remain on the vehicle (i.e., not relaxed to level
  // `RELAX_ALL_AFTER_THRESHOLD`), it is skipped in the response.
  // If a shipment has a non-empty `allowed_vehicle_indices` field and all of
  // the allowed vehicles are ignored, it is skipped in the response.
  bool ignore = 28;
}

// Time windows constrain the time of an event, such as the arrival time at a
// visit, or the start and end time of a vehicle.
//
// Hard time window bounds, `start_time` and `end_time`, enforce the earliest
// and latest time of the event, such that `start_time <= event_time <=
// end_time`. The soft time window lower bound, `soft_start_time`, expresses a
// preference for the event to happen at or after `soft_start_time` by incurring
// a cost proportional to how long before soft_start_time the event occurs. The
// soft time window upper bound, `soft_end_time`, expresses a preference for the
// event to happen at or before `soft_end_time` by incurring a cost proportional
// to how long after `soft_end_time` the event occurs. `start_time`, `end_time`,
// `soft_start_time` and `soft_end_time` should be within the global time limits
// (see
// [ShipmentModel.global_start_time][google.maps.routeoptimization.v1.ShipmentModel.global_start_time]
// and
// [ShipmentModel.global_end_time][google.maps.routeoptimization.v1.ShipmentModel.global_end_time])
// and should respect:
// ```
//   0 <= `start_time` <= `end_time` and
//   0 <= `start_time` <= `soft_start_time` and
//   0 <= `soft_end_time` <= `end_time`.
// ```
message TimeWindow {
  // The hard time window start time. If unspecified it will be set to
  // `ShipmentModel.global_start_time`.
  google.protobuf.Timestamp start_time = 1;

  // The hard time window end time. If unspecified it will be set to
  // `ShipmentModel.global_end_time`.
  google.protobuf.Timestamp end_time = 2;

  // The soft start time of the time window.
  google.protobuf.Timestamp soft_start_time = 3;

  // The soft end time of the time window.
  google.protobuf.Timestamp soft_end_time = 4;

  // A cost per hour added to other costs in the model if the event occurs
  // before soft_start_time, computed as:
  //
  // ```
  //    max(0, soft_start_time - t.seconds)
  //                           * cost_per_hour_before_soft_start_time / 3600,
  // t being the time of the event.
  // ```
  //
  // This cost must be positive, and the field can only be set if
  // soft_start_time has been set.
  optional double cost_per_hour_before_soft_start_time = 5;

  // A cost per hour added to other costs in the model if the event occurs after
  // `soft_end_time`, computed as:
  //
  // ```
  //    max(0, t.seconds - soft_end_time.seconds)
  //                     * cost_per_hour_after_soft_end_time / 3600,
  // t being the time of the event.
  // ```
  //
  // This cost must be positive, and the field can only be set if
  // `soft_end_time` has been set.
  optional double cost_per_hour_after_soft_end_time = 6;
}

// A limit defining a maximum distance which can be traveled. It can be either
// hard or soft.
//
// If a soft limit is defined, both `soft_max_meters` and
// `cost_per_kilometer_above_soft_max` must be defined and be nonnegative.
message DistanceLimit {
  // A hard limit constraining the distance to be at most max_meters. The limit
  // must be nonnegative.
  optional int64 max_meters = 1;

  // A soft limit not enforcing a maximum distance limit, but when violated
  // results in a cost which adds up to other costs defined in the model,
  // with the same unit.
  //
  // If defined soft_max_meters must be less than max_meters and must be
  // nonnegative.
  optional int64 soft_max_meters = 2;

  // Cost per kilometer incurred, increasing up to `soft_max_meters`, with
  // formula:
  // ```
  //   min(distance_meters, soft_max_meters) / 1000.0 *
  //   cost_per_kilometer_below_soft_max.
  // ```
  // This cost is not supported in `route_distance_limit`.
  optional double cost_per_kilometer_below_soft_max = 4;

  // Cost per kilometer incurred if distance is above `soft_max_meters` limit.
  // The additional cost is 0 if the distance is under the limit, otherwise the
  // formula used to compute the cost is the following:
  // ```
  //   (distance_meters - soft_max_meters) / 1000.0 *
  //   cost_per_kilometer_above_soft_max.
  // ```
  // The cost must be nonnegative.
  optional double cost_per_kilometer_above_soft_max = 3;
}

// Specifies attributes of transitions between two consecutive visits on a
// route. Several `TransitionAttributes` may apply to the same transition: in
// that case, all extra costs add up and the strictest constraint or limit
// applies (following natural "AND" semantics).
message TransitionAttributes {
  // Tags defining the set of (src->dst) transitions these attributes apply to.
  //
  // A source visit or vehicle start matches iff its
  // [VisitRequest.tags][google.maps.routeoptimization.v1.Shipment.VisitRequest.tags]
  // or
  // [Vehicle.start_tags][google.maps.routeoptimization.v1.Vehicle.start_tags]
  // either contains `src_tag` or does not contain `excluded_src_tag` (depending
  // on which of these two fields is non-empty).
  string src_tag = 1;

  // See `src_tag`. Exactly one of `src_tag` and `excluded_src_tag` must be
  // non-empty.
  string excluded_src_tag = 2;

  // A destination visit or vehicle end matches iff its
  // [VisitRequest.tags][google.maps.routeoptimization.v1.Shipment.VisitRequest.tags]
  // or [Vehicle.end_tags][google.maps.routeoptimization.v1.Vehicle.end_tags]
  // either contains `dst_tag` or does not contain `excluded_dst_tag` (depending
  // on which of these two fields is non-empty).
  string dst_tag = 3;

  // See `dst_tag`. Exactly one of `dst_tag` and `excluded_dst_tag` must be
  // non-empty.
  string excluded_dst_tag = 4;

  // Specifies a cost for performing this transition. This is in the same unit
  // as all other costs in the model and must not be negative. It is applied on
  // top of all other existing costs.
  double cost = 5;

  // Specifies a cost per kilometer applied to the distance traveled while
  // performing this transition. It adds up to any
  // [Vehicle.cost_per_kilometer][google.maps.routeoptimization.v1.Vehicle.cost_per_kilometer]
  // specified on vehicles.
  double cost_per_kilometer = 6;

  // Specifies a limit on the distance traveled while performing this
  // transition.
  //
  // As of 2021/06, only soft limits are supported.
  DistanceLimit distance_limit = 7;

  // Specifies a delay incurred when performing this transition.
  //
  // This delay always occurs *after* finishing the source visit and *before*
  // starting the destination visit.
  google.protobuf.Duration delay = 8;
}

// Encapsulates a waypoint. Waypoints mark arrival and departure locations of
// VisitRequests, and start and end locations of Vehicles.
message Waypoint {
  // Different ways to represent a location.
  oneof location_type {
    // A point specified using geographic coordinates, including an optional
    // heading.
    Location location = 1;

    // The POI Place ID associated with the waypoint.
    string place_id = 2;
  }

  // Optional. Indicates that the location of this waypoint is meant to have a
  // preference for the vehicle to stop at a particular side of road. When you
  // set this value, the route will pass through the location so that the
  // vehicle can stop at the side of road that the location is biased towards
  // from the center of the road. This option doesn't work for the 'WALKING'
  // travel mode.
  bool side_of_road = 3 [(google.api.field_behavior) = OPTIONAL];
}

// Encapsulates a location (a geographic point, and an optional heading).
message Location {
  // The waypoint's geographic coordinates.
  google.type.LatLng lat_lng = 1;

  // The compass heading associated with the direction of the flow of traffic.
  // This value is used to specify the side of the road to use for pickup and
  // drop-off. Heading values can be from 0 to 360, where 0 specifies a heading
  // of due North, 90 specifies a heading of due East, etc.
  optional int32 heading = 2;
}

// Rules to generate time breaks for a vehicle (e.g. lunch breaks). A break
// is a contiguous period of time during which the vehicle remains idle at its
// current position and cannot perform any visit. A break may occur:
//
// * during the travel between two visits (which includes the time right
//   before or right after a visit, but not in the middle of a visit), in
//   which case it extends the corresponding transit time between the visits,
// * or before the vehicle start (the vehicle may not start in the middle of
//   a break), in which case it does not affect the vehicle start time.
// * or after the vehicle end (ditto, with the vehicle end time).
message BreakRule {
  // The sequence of breaks (i.e. their number and order) that apply to each
  // vehicle must be known beforehand. The repeated `BreakRequest`s define
  // that sequence, in the order in which they must occur. Their time windows
  // (`earliest_start_time` / `latest_start_time`) may overlap, but they must
  // be compatible with the order (this is checked).
  message BreakRequest {
    // Required. Lower bound (inclusive) on the start of the break.
    google.protobuf.Timestamp earliest_start_time = 1
        [(google.api.field_behavior) = REQUIRED];

    // Required. Upper bound (inclusive) on the start of the break.
    google.protobuf.Timestamp latest_start_time = 2
        [(google.api.field_behavior) = REQUIRED];

    // Required. Minimum duration of the break. Must be positive.
    google.protobuf.Duration min_duration = 3
        [(google.api.field_behavior) = REQUIRED];
  }

  // One may further constrain the frequency and duration of the breaks
  // specified above, by enforcing a minimum break frequency, such as
  // "There must be a break of at least 1 hour every 12 hours". Assuming that
  // this can be interpreted as "Within any sliding time window of 12h, there
  // must be at least one break of at least one hour", that example would
  // translate to the following `FrequencyConstraint`:
  // ```
  // {
  //    min_break_duration { seconds: 3600 }         # 1 hour.
  //    max_inter_break_duration { seconds: 39600 }  # 11 hours (12 - 1 = 11).
  // }
  // ```
  //
  // The timing and duration of the breaks in the solution will respect all
  // such constraints, in addition to the time windows and minimum durations
  // already specified in the `BreakRequest`.
  //
  // A `FrequencyConstraint` may in practice apply to non-consecutive breaks.
  // For example, the following schedule honors the "1h every 12h" example:
  // ```
  //   04:00 vehicle start
  //    .. performing travel and visits ..
  //   09:00 1 hour break
  //   10:00 end of the break
  //    .. performing travel and visits ..
  //   12:00 20-min lunch break
  //   12:20 end of the break
  //    .. performing travel and visits ..
  //   21:00 1 hour break
  //   22:00 end of the break
  //    .. performing travel and visits ..
  //   23:59 vehicle end
  // ```
  message FrequencyConstraint {
    // Required. Minimum break duration for this constraint. Nonnegative.
    // See description of `FrequencyConstraint`.
    google.protobuf.Duration min_break_duration = 1
        [(google.api.field_behavior) = REQUIRED];

    // Required. Maximum allowed span of any interval of time in the route that
    // does not include at least partially a break of `duration >=
    // min_break_duration`. Must be positive.
    google.protobuf.Duration max_inter_break_duration = 2
        [(google.api.field_behavior) = REQUIRED];
  }

  // Sequence of breaks. See the `BreakRequest` message.
  repeated BreakRequest break_requests = 1;

  // Several `FrequencyConstraint` may apply. They must all be satisfied by
  // the `BreakRequest`s of this `BreakRule`. See `FrequencyConstraint`.
  repeated FrequencyConstraint frequency_constraints = 2;
}

// A vehicle's route can be decomposed, along the time axis, like this (we
// assume there are n visits):
// ```
//   |            |            |          |       |  T[2], |        |      |
//   | Transition |  Visit #0  |          |       |  V[2], |        |      |
//   |     #0     |    aka     |   T[1]   |  V[1] |  ...   | V[n-1] | T[n] |
//   |  aka T[0]  |    V[0]    |          |       | V[n-2],|        |      |
//   |            |            |          |       | T[n-1] |        |      |
//   ^            ^            ^          ^       ^        ^        ^      ^
// vehicle    V[0].start   V[0].end     V[1].   V[1].    V[n].    V[n]. vehicle
//  start     (arrival)   (departure)   start   end      start    end     end
// ```
// Note that we make a difference between:
//
// * "punctual events", such as the vehicle start and end and each visit's start
//   and end (aka arrival and departure). They happen at a given second.
// * "time intervals", such as the visits themselves, and the transition between
//   visits. Though time intervals can sometimes have zero duration, i.e. start
//   and end at the same second, they often have a positive duration.
//
// Invariants:
//
// * If there are n visits, there are n+1 transitions.
// * A visit is always surrounded by a transition before it (same index) and a
//   transition after it (index + 1).
// * The vehicle start is always followed by transition #0.
// * The vehicle end is always preceded by transition #n.
//
// Zooming in, here is what happens during a `Transition` and a `Visit`:
// ```
// ---+-------------------------------------+-----------------------------+-->
//    |           TRANSITION[i]             |           VISIT[i]          |
//    |                                     |                             |
//    |  * TRAVEL: the vehicle moves from   |      PERFORM the visit:     |
//    |    VISIT[i-1].departure_location to |                             |
//    |    VISIT[i].arrival_location, which |  * Spend some time:         |
//    |    takes a given travel duration    |    the "visit duration".    |
//    |    and distance                     |                             |
//    |                                     |  * Load or unload           |
//    |  * BREAKS: the driver may have      |    some quantities from the |
//    |    breaks (e.g. lunch break).       |    vehicle: the "demand".   |
//    |                                     |                             |
//    |  * WAIT: the driver/vehicle does    |                             |
//    |    nothing. This can happen for     |                             |
//    |    many reasons, for example when   |                             |
//    |    the vehicle reaches the next     |                             |
//    |    event's destination before the   |                             |
//    |    start of its time window         |                             |
//    |                                     |                             |
//    |  * DELAY: *right before* the next   |                             |
//    |    arrival. E.g. the vehicle and/or |                             |
//    |    driver spends time unloading.    |                             |
//    |                                     |                             |
// ---+-------------------------------------+-----------------------------+-->
//    ^                                     ^                             ^
// V[i-1].end                           V[i].start                    V[i].end
// ```
// Lastly, here is how the TRAVEL, BREAKS, DELAY and WAIT can be arranged
// during a transition.
//
// * They don't overlap.
// * The DELAY is unique and *must* be a contiguous period of time right
//   before the next visit (or vehicle end). Thus, it suffice to know the
//   delay duration to know its start and end time.
// * The BREAKS are contiguous, non-overlapping periods of time. The
//   response specifies the start time and duration of each break.
// * TRAVEL and WAIT are "preemptable": they can be interrupted several times
//   during this transition. Clients can assume that travel happens "as soon as
//   possible" and that "wait" fills the remaining time.
//
// A (complex) example:
// ```
//                                TRANSITION[i]
// --++-----+-----------------------------------------------------------++-->
//   ||     |       |           |       |           |         |         ||
//   ||  T  |   B   |     T     |       |     B     |         |    D    ||
//   ||  r  |   r   |     r     |   W   |     r     |    W    |    e    ||
//   ||  a  |   e   |     a     |   a   |     e     |    a    |    l    ||
//   ||  v  |   a   |     v     |   i   |     a     |    i    |    a    ||
//   ||  e  |   k   |     e     |   t   |     k     |    t    |    y    ||
//   ||  l  |       |     l     |       |           |         |         ||
//   ||     |       |           |       |           |         |         ||
// --++-----------------------------------------------------------------++-->
// ```
message ShipmentRoute {
  // A visit performed during a route. This visit corresponds to a pickup or a
  // delivery of a `Shipment`.
  message Visit {
    // Index of the `shipments` field in the source
    // [ShipmentModel][google.maps.routeoptimization.v1.ShipmentModel].
    int32 shipment_index = 1;

    // If true the visit corresponds to a pickup of a `Shipment`. Otherwise, it
    // corresponds to a delivery.
    bool is_pickup = 2;

    // Index of `VisitRequest` in either the pickup or delivery field of the
    // `Shipment` (see `is_pickup`).
    int32 visit_request_index = 3;

    // Time at which the visit starts. Note that the vehicle may arrive earlier
    // than this at the visit location. Times are consistent with the
    // `ShipmentModel`.
    google.protobuf.Timestamp start_time = 4;

    // Total visit load demand as the sum of the shipment and the visit request
    // `load_demands`. The values are negative if the visit is a delivery.
    // Demands are reported for the same types as the
    // [Transition.loads][google.maps.routeoptimization.v1.ShipmentRoute.Transition]
    // (see this field).
    map<string, Shipment.Load> load_demands = 11;

    // Extra detour time due to the shipments visited on the route before the
    // visit and to the potential waiting time induced by time windows.
    // If the visit is a delivery, the detour is computed from the corresponding
    // pickup visit and is equal to:
    // ```
    // start_time(delivery) - start_time(pickup)
    // - (duration(pickup) + travel duration from the pickup location
    // to the delivery location).
    // ```
    // Otherwise, it is computed from the vehicle `start_location` and is equal
    // to:
    // ```
    // start_time - vehicle_start_time - travel duration from
    // the vehicle's `start_location` to the visit.
    // ```
    google.protobuf.Duration detour = 6;

    // Copy of the corresponding `Shipment.label`, if specified in the
    // `Shipment`.
    string shipment_label = 7;

    // Copy of the corresponding
    // [VisitRequest.label][google.maps.routeoptimization.v1.Shipment.VisitRequest.label],
    // if specified in the `VisitRequest`.
    string visit_label = 8;
  }

  // Transition between two events on the route. See the description of
  // [ShipmentRoute][google.maps.routeoptimization.v1.ShipmentRoute].
  //
  // If the vehicle does not have a `start_location` and/or `end_location`, the
  // corresponding travel metrics are 0.
  message Transition {
    // Travel duration during this transition.
    google.protobuf.Duration travel_duration = 1;

    // Distance traveled during the transition.
    double travel_distance_meters = 2;

    // When traffic is requested via
    // [OptimizeToursRequest.consider_road_traffic]
    // [google.maps.routeoptimization.v1.OptimizeToursRequest.consider_road_traffic],
    // and the traffic info couldn't be retrieved for a `Transition`, this
    // boolean is set to true. This may be temporary (rare hiccup in the
    // realtime traffic servers) or permanent (no data for this location).
    bool traffic_info_unavailable = 3;

    // Sum of the delay durations applied to this transition. If any, the delay
    // starts exactly `delay_duration` seconds before the next event (visit or
    // vehicle end). See
    // [TransitionAttributes.delay][google.maps.routeoptimization.v1.TransitionAttributes.delay].
    google.protobuf.Duration delay_duration = 4;

    // Sum of the duration of the breaks occurring during this transition, if
    // any. Details about each break's start time and duration are stored in
    // [ShipmentRoute.breaks][google.maps.routeoptimization.v1.ShipmentRoute.breaks].
    google.protobuf.Duration break_duration = 5;

    // Time spent waiting during this transition. Wait duration corresponds to
    // idle time and does not include break time. Also note that this wait time
    // may be split into several non-contiguous intervals.
    google.protobuf.Duration wait_duration = 6;

    // Total duration of the transition, provided for convenience. It is equal
    // to:
    //
    // * next visit `start_time` (or `vehicle_end_time` if this is the last
    // transition) - this transition's `start_time`;
    // * if `ShipmentRoute.has_traffic_infeasibilities` is false, the following
    // additionally holds: `total_duration = travel_duration + delay_duration
    // + break_duration + wait_duration`.
    google.protobuf.Duration total_duration = 7;

    // Start time of this transition.
    google.protobuf.Timestamp start_time = 8;

    // The encoded polyline representation of the route followed during the
    // transition.
    // This field is only populated if [populate_transition_polylines]
    // [google.maps.routeoptimization.v1.OptimizeToursRequest.populate_transition_polylines]
    // is set to true.
    EncodedPolyline route_polyline = 9;

    // Output only. An opaque token that can be passed to [Navigation
    // SDK](https://developers.google.com/maps/documentation/navigation) to
    // reconstruct the route during navigation, and, in the event of rerouting,
    // honor the original intention when the route was created. Treat this token
    // as an opaque blob.  Don't compare its value across requests as its value
    // may change even if the service returns the exact same route. This field
    // is only populated if [populate_transition_polylines]
    // [google.maps.routeoptimization.v1.OptimizeToursRequest.populate_transition_polylines]
    // is set to true.
    string route_token = 12 [(google.api.field_behavior) = OUTPUT_ONLY];

    // Vehicle loads during this transition, for each type that either appears
    // in this vehicle's
    // [Vehicle.load_limits][google.maps.routeoptimization.v1.Vehicle.load_limits],
    // or that have non-zero
    // [Shipment.load_demands][google.maps.routeoptimization.v1.Shipment.load_demands]
    // on some shipment performed on this route.
    //
    // The loads during the first transition are the starting loads of the
    // vehicle route. Then, after each visit, the visit's `load_demands` are
    // either added or subtracted to get the next transition's loads, depending
    // on whether the visit was a pickup or a delivery.
    map<string, VehicleLoad> vehicle_loads = 11;
  }

  // Reports the actual load of the vehicle at some point along the route,
  // for a given type (see
  // [Transition.vehicle_loads][google.maps.routeoptimization.v1.ShipmentRoute.Transition.vehicle_loads]).
  message VehicleLoad {
    // The amount of load on the vehicle, for the given type. The unit of load
    // is usually indicated by the type. See
    // [Transition.vehicle_loads][google.maps.routeoptimization.v1.ShipmentRoute.Transition.vehicle_loads].
    int64 amount = 1;
  }

  // The encoded representation of a polyline. More information on polyline
  // encoding can be found here:
  // https://developers.google.com/maps/documentation/utilities/polylinealgorithm
  // https://developers.google.com/maps/documentation/javascript/reference/geometry#encoding.
  message EncodedPolyline {
    // String representing encoded points of the polyline.
    string points = 1;
  }

  // Data representing the execution of a break.
  message Break {
    // Start time of a break.
    google.protobuf.Timestamp start_time = 1;

    // Duration of a break.
    google.protobuf.Duration duration = 2;
  }

  // Vehicle performing the route, identified by its index in the source
  // `ShipmentModel`.
  int32 vehicle_index = 1;

  // Label of the vehicle performing this route, equal to
  // `ShipmentModel.vehicles(vehicle_index).label`, if specified.
  string vehicle_label = 2;

  // Time at which the vehicle starts its route.
  google.protobuf.Timestamp vehicle_start_time = 5;

  // Time at which the vehicle finishes its route.
  google.protobuf.Timestamp vehicle_end_time = 6;

  // Ordered sequence of visits representing a route.
  // visits[i] is the i-th visit in the route.
  // If this field is empty, the vehicle is considered as unused.
  repeated Visit visits = 7;

  // Ordered list of transitions for the route.
  repeated Transition transitions = 8;

  // When
  // [OptimizeToursRequest.consider_road_traffic][google.maps.routeoptimization.v1.OptimizeToursRequest.consider_road_traffic],
  // is set to true, this field indicates that inconsistencies in route timings
  // are predicted using traffic-based travel duration estimates. There may be
  // insufficient time to complete traffic-adjusted travel, delays, and breaks
  // between visits, before the first visit, or after the last visit, while
  // still satisfying the visit and vehicle time windows. For example,
  //
  // ```
  //   start_time(previous_visit) + duration(previous_visit) +
  //   travel_duration(previous_visit, next_visit) > start_time(next_visit)
  // ```
  //
  // Arrival at next_visit will likely happen later than its current
  // time window due the increased estimate of travel time
  // `travel_duration(previous_visit, next_visit)` due to traffic. Also, a break
  // may be forced to overlap with a visit due to an increase in travel time
  // estimates and visit or break time window restrictions.
  bool has_traffic_infeasibilities = 9;

  // The encoded polyline representation of the route.
  // This field is only populated if
  // [OptimizeToursRequest.populate_polylines][google.maps.routeoptimization.v1.OptimizeToursRequest.populate_polylines]
  // is set to true.
  EncodedPolyline route_polyline = 10;

  // Breaks scheduled for the vehicle performing this route.
  // The `breaks` sequence represents time intervals, each starting at the
  // corresponding `start_time` and lasting `duration` seconds.
  repeated Break breaks = 11;

  // Duration, distance and load metrics for this route. The fields of
  // [AggregatedMetrics][google.maps.routeoptimization.v1.AggregatedMetrics] are
  // summed over all
  // [ShipmentRoute.transitions][google.maps.routeoptimization.v1.ShipmentRoute.transitions]
  // or
  // [ShipmentRoute.visits][google.maps.routeoptimization.v1.ShipmentRoute.visits],
  // depending on the context.
  AggregatedMetrics metrics = 12;

  // Cost of the route, broken down by cost-related request fields.
  // The keys are proto paths, relative to the input OptimizeToursRequest, e.g.
  // "model.shipments.pickups.cost", and the values are the total cost
  // generated by the corresponding cost field, aggregated over the whole route.
  // In other words, costs["model.shipments.pickups.cost"] is the sum of all
  // pickup costs over the route. All costs defined in the model are reported in
  // detail here with the exception of costs related to TransitionAttributes
  // that are only reported in an aggregated way as of 2022/01.
  map<string, double> route_costs = 17;

  // Total cost of the route. The sum of all costs in the cost map.
  double route_total_cost = 18;
}

// Specifies details of unperformed shipments in a solution. For trivial cases
// and/or if we are able to identify the cause for skipping, we report the
// reason here.
message SkippedShipment {
  // If we can explain why the shipment was skipped, reasons will be listed
  // here. If the reason is not the same for all vehicles, `reason` will have
  // more than 1 element. A skipped shipment cannot have duplicate reasons,
  // i.e. where all fields are the same except for `example_vehicle_index`.
  // Example:
  // ```
  // reasons {
  //   code: DEMAND_EXCEEDS_VEHICLE_CAPACITY
  //   example_vehicle_index: 1
  //   example_exceeded_capacity_type: "Apples"
  // }
  // reasons {
  //   code: DEMAND_EXCEEDS_VEHICLE_CAPACITY
  //   example_vehicle_index: 3
  //   example_exceeded_capacity_type: "Pears"
  // }
  // reasons {
  //   code: CANNOT_BE_PERFORMED_WITHIN_VEHICLE_DISTANCE_LIMIT
  //   example_vehicle_index: 1
  // }
  // ```
  // The skipped shipment is incompatible with all vehicles. The reasons may
  // be different for all vehicles but at least one vehicle's "Apples"
  // capacity would be exceeded (including vehicle 1), at least one vehicle's
  // "Pears" capacity would be exceeded (including vehicle 3) and at least one
  // vehicle's distance limit would be exceeded (including vehicle 1).
  message Reason {
    // Code identifying the reason type. The order here is meaningless. In
    // particular, it gives no indication of whether a given reason will
    // appear before another in the solution, if both apply.
    enum Code {
      // This should never be used.
      CODE_UNSPECIFIED = 0;

      // There is no vehicle in the model making all shipments infeasible.
      NO_VEHICLE = 1;

      // The demand of the shipment exceeds a vehicle's capacity for some
      // capacity types, one of which is `example_exceeded_capacity_type`.
      DEMAND_EXCEEDS_VEHICLE_CAPACITY = 2;

      // The minimum distance necessary to perform this shipment, i.e. from
      // the vehicle's `start_location` to the shipment's pickup and/or delivery
      // locations and to the vehicle's end location exceeds the vehicle's
      // `route_distance_limit`.
      //
      // Note that for this computation we use the geodesic distances.
      CANNOT_BE_PERFORMED_WITHIN_VEHICLE_DISTANCE_LIMIT = 3;

      // The minimum time necessary to perform this shipment, including travel
      // time, wait time and service time exceeds the vehicle's
      // `route_duration_limit`.
      //
      // Note: travel time is computed in the best-case scenario, namely as
      // geodesic distance x 36 m/s (roughly 130 km/hour).
      CANNOT_BE_PERFORMED_WITHIN_VEHICLE_DURATION_LIMIT = 4;

      // Same as above but we only compare minimum travel time and the
      // vehicle's `travel_duration_limit`.
      CANNOT_BE_PERFORMED_WITHIN_VEHICLE_TRAVEL_DURATION_LIMIT = 5;

      // The vehicle cannot perform this shipment in the best-case scenario
      // (see `CANNOT_BE_PERFORMED_WITHIN_VEHICLE_DURATION_LIMIT` for time
      // computation) if it starts at its earliest start time: the total time
      // would make the vehicle end after its latest end time.
      CANNOT_BE_PERFORMED_WITHIN_VEHICLE_TIME_WINDOWS = 6;

      // The `allowed_vehicle_indices` field of the shipment is not empty and
      // this vehicle does not belong to it.
      VEHICLE_NOT_ALLOWED = 7;
    }

    // Refer to the comments of Code.
    Code code = 1;

    // If the reason is related to a shipment-vehicle incompatibility, this
    // field provides the index of one relevant vehicle.
    optional int32 example_vehicle_index = 2;

    // If the reason code is `DEMAND_EXCEEDS_VEHICLE_CAPACITY`, documents one
    // capacity type that is exceeded.
    string example_exceeded_capacity_type = 3;
  }

  // The index corresponds to the index of the shipment in the source
  // `ShipmentModel`.
  int32 index = 1;

  // Copy of the corresponding
  // [Shipment.label][google.maps.routeoptimization.v1.Shipment.label], if
  // specified in the `Shipment`.
  string label = 2;

  // A list of reasons that explain why the shipment was skipped. See comment
  // above `Reason`. If we are unable to understand why a shipment was skipped,
  // reasons will not be set.
  repeated Reason reasons = 3;
}

// Aggregated metrics for
// [ShipmentRoute][google.maps.routeoptimization.v1.ShipmentRoute] (resp. for
// [OptimizeToursResponse][google.maps.routeoptimization.v1.OptimizeToursResponse]
// over all
// [Transition][google.maps.routeoptimization.v1.ShipmentRoute.Transition]
// and/or [Visit][google.maps.routeoptimization.v1.ShipmentRoute.Visit] (resp.
// over all [ShipmentRoute][google.maps.routeoptimization.v1.ShipmentRoute])
// elements.
message AggregatedMetrics {
  // Number of shipments performed. Note that a pickup and delivery pair only
  // counts once.
  int32 performed_shipment_count = 1;

  // Total travel duration for a route or a solution.
  google.protobuf.Duration travel_duration = 2;

  // Total wait duration for a route or a solution.
  google.protobuf.Duration wait_duration = 3;

  // Total delay duration for a route or a solution.
  google.protobuf.Duration delay_duration = 4;

  // Total break duration for a route or a solution.
  google.protobuf.Duration break_duration = 5;

  // Total visit duration for a route or a solution.
  google.protobuf.Duration visit_duration = 6;

  // The total duration should be equal to the sum of all durations above.
  // For routes, it also corresponds to:
  // ```
  // [ShipmentRoute.vehicle_end_time][google.maps.routeoptimization.v1.ShipmentRoute.vehicle_end_time]
  // -
  // [ShipmentRoute.vehicle_start_time][google.maps.routeoptimization.v1.ShipmentRoute.vehicle_start_time]
  // ```
  google.protobuf.Duration total_duration = 7;

  // Total travel distance for a route or a solution.
  double travel_distance_meters = 8;

  // Maximum load achieved over the entire route (resp. solution), for each of
  // the quantities on this route (resp. solution), computed as the maximum over
  // all
  // [Transition.vehicle_loads][google.maps.routeoptimization.v1.ShipmentRoute.Transition.vehicle_loads]
  // (resp.
  // [ShipmentRoute.metrics.max_loads][google.maps.routeoptimization.v1.AggregatedMetrics.max_loads].
  map<string, ShipmentRoute.VehicleLoad> max_loads = 9;
}

// Solution injected in the request including information about which visits
// must be constrained and how they must be constrained.
message InjectedSolutionConstraint {
  // For a group of vehicles, specifies at what threshold(s) constraints on
  // visits will be relaxed and to which level. Shipments listed in
  // the `skipped_shipment` field are constrained to be skipped; i.e., they
  // cannot be performed.
  message ConstraintRelaxation {
    // If `relaxations` is empty, the start time and sequence of all visits
    // on `routes` are fully constrained and no new visits may be inserted or
    // added to those routes. Also, a vehicle's start and end time in
    // `routes` is fully constrained, unless the vehicle is empty (i.e., has no
    // visits and has `used_if_route_is_empty` set to false in the model).
    //
    // `relaxations(i).level` specifies the constraint relaxation level applied
    // to a visit #j that satisfies:
    //
    //   * `route.visits(j).start_time >= relaxations(i).threshold_time` AND
    //   * `j + 1 >= relaxations(i).threshold_visit_count`
    //
    // Similarly, the vehicle start is relaxed to `relaxations(i).level` if it
    // satisfies:
    //
    //   * `vehicle_start_time >= relaxations(i).threshold_time` AND
    //   * `relaxations(i).threshold_visit_count == 0`
    // and the vehicle end is relaxed to `relaxations(i).level` if it satisfies:
    //   * `vehicle_end_time >= relaxations(i).threshold_time` AND
    //   * `route.visits_size() + 1 >= relaxations(i).threshold_visit_count`
    //
    // To apply a relaxation level if a visit meets the `threshold_visit_count`
    // OR the `threshold_time` add two `relaxations` with the same `level`:
    // one with only `threshold_visit_count` set and the other with only
    // `threshold_time` set. If a visit satisfies the conditions of multiple
    // `relaxations`, the most relaxed level applies. As a result, from the
    // vehicle start through the route visits in order to the vehicle end, the
    // relaxation level becomes more relaxed: i.e., the relaxation level is
    // non-decreasing as the route progresses.
    //
    // The timing and sequence of route visits that do not satisfy the
    // threshold conditions of any `relaxations` are fully constrained
    // and no visits may be inserted into these sequences. Also, if a
    // vehicle start or end does not satisfy the conditions of any
    // relaxation the time is fixed, unless the vehicle is empty.
    message Relaxation {
      // Expresses the different constraint relaxation levels, which are
      // applied for a visit and those that follow when it satisfies the
      // threshold conditions.
      //
      // The enumeration below is in order of increasing relaxation.
      enum Level {
        // Implicit default relaxation level: no constraints are relaxed,
        // i.e., all visits are fully constrained.
        //
        // This value must not be explicitly used in `level`.
        LEVEL_UNSPECIFIED = 0;

        // Visit start times and vehicle start/end times will be relaxed, but
        // each visit remains bound to the same vehicle and the visit sequence
        // must be observed: no visit can be inserted between them or before
        // them.
        RELAX_VISIT_TIMES_AFTER_THRESHOLD = 1;

        // Same as `RELAX_VISIT_TIMES_AFTER_THRESHOLD`, but the visit sequence
        // is also relaxed: visits can only be performed by this vehicle, but
        // can potentially become unperformed.
        RELAX_VISIT_TIMES_AND_SEQUENCE_AFTER_THRESHOLD = 2;

        // Same as `RELAX_VISIT_TIMES_AND_SEQUENCE_AFTER_THRESHOLD`, but the
        // vehicle is also relaxed: visits are completely free at or after the
        // threshold time and can potentially become unperformed.
        RELAX_ALL_AFTER_THRESHOLD = 3;
      }

      // The constraint relaxation level that applies when the conditions
      // at or after `threshold_time` AND at least `threshold_visit_count` are
      // satisfied.
      Level level = 1;

      // The time at or after which the relaxation `level` may be applied.
      google.protobuf.Timestamp threshold_time = 2;

      // The number of visits at or after which the relaxation `level` may be
      // applied. If `threshold_visit_count` is 0 (or unset), the `level` may be
      // applied directly at the vehicle start.
      //
      // If it is `route.visits_size() + 1`, the `level` may only be applied to
      // the vehicle end. If it is more than `route.visits_size() + 1`,
      // `level` is not applied at all for that route.
      int32 threshold_visit_count = 3;
    }

    // All the visit constraint relaxations that will apply to visits on
    // routes with vehicles in `vehicle_indices`.
    repeated Relaxation relaxations = 1;

    // Specifies the vehicle indices to which the visit constraint
    // `relaxations` apply. If empty, this is considered the default and the
    // `relaxations` apply to all vehicles that are not specified in other
    // `constraint_relaxations`. There can be at most one default, i.e., at
    // most one constraint relaxation field is allowed empty
    // `vehicle_indices`. A vehicle index can only be listed once, even within
    // several `constraint_relaxations`.
    //
    // A vehicle index is mapped the same as
    // [ShipmentRoute.vehicle_index][google.maps.routeoptimization.v1.ShipmentRoute.vehicle_index],
    // if `interpret_injected_solutions_using_labels` is true (see `fields`
    // comment).
    repeated int32 vehicle_indices = 2;
  }

  // Routes of the solution to inject. Some routes may be omitted from the
  // original solution. The routes and skipped shipments must satisfy the basic
  // validity assumptions listed for `injected_first_solution_routes`.
  repeated ShipmentRoute routes = 1;

  // Skipped shipments of the solution to inject. Some may be omitted from the
  // original solution. See the `routes` field.
  repeated SkippedShipment skipped_shipments = 2;

  // For zero or more groups of vehicles, specifies when and how much to relax
  // constraints. If this field is empty, all non-empty vehicle routes are
  // fully constrained.
  repeated ConstraintRelaxation constraint_relaxations = 3;
}

// Describes an error or warning encountered when validating an
// `OptimizeToursRequest`.
message OptimizeToursValidationError {
  // Specifies a context for the validation error. A `FieldReference` always
  // refers to a given field in this file and follows the same hierarchical
  // structure. For example, we may specify element #2 of `start_time_windows`
  // of vehicle #5 using:
  // ```
  // name: "vehicles" index: 5 sub_field { name: "end_time_windows" index: 2 }
  // ```
  // We however omit top-level entities such as `OptimizeToursRequest` or
  // `ShipmentModel` to avoid crowding the message.
  message FieldReference {
    // Name of the field, e.g., "vehicles".
    string name = 1;

    oneof index_or_key {
      // Index of the field if repeated.
      int32 index = 2;

      // Key if the field is a map.
      string key = 4;
    }

    // Recursively nested sub-field, if needed.
    FieldReference sub_field = 3;
  }

  // A validation error is defined by the pair (`code`, `display_name`) which
  // are always present.
  //
  // The fields following this section provide more context about the error.
  //
  // *MULTIPLE ERRORS*:
  // When there are multiple errors, the validation process tries to output
  // several of them. Much like a compiler, this is an imperfect process. Some
  // validation errors will be "fatal", meaning that they stop the entire
  // validation process. This is the case for `display_name="UNSPECIFIED"`
  // errors, among others. Some errors may cause the validation process to skip
  // other errors.
  //
  // *STABILITY*:
  // `code` and `display_name` should be very stable. But new codes and
  // display names may appear over time, which may cause a given (invalid)
  // request to yield a different (`code`, `display_name`) pair because the new
  // error hid the old one. For example, see "MULTIPLE ERRORS".
  int32 code = 1;

  // The error display name.
  string display_name = 2;

  // An error context may involve 0, 1 (most of the time) or more fields. For
  // example, referring to vehicle #4 and shipment #2's first pickup can be
  // done as follows:
  // ```
  // fields { name: "vehicles" index: 4}
  // fields { name: "shipments" index: 2 sub_field {name: "pickups" index: 0} }
  // ```
  // Note, however, that the cardinality of `fields` should not change for a
  // given error code.
  repeated FieldReference fields = 3;

  // Human-readable string describing the error. There is a 1:1 mapping
  // between `code` and `error_message` (when code != "UNSPECIFIED").
  //
  // *STABILITY*: Not stable: the error message associated to a given `code` may
  // change (hopefully to clarify it) over time. Please rely on the
  // `display_name` and `code` instead.
  string error_message = 4;

  // May contain the value(s) of the field(s). This is not always available. You
  // should absolutely not rely on it and use it only for manual model
  // debugging.
  string offending_values = 5;
}

// Specify an input for
// [BatchOptimizeTours][google.maps.routeoptimization.v1.RouteOptimizationService.BatchOptimizeTours].
message InputConfig {
  // Required.
  oneof source {
    // A Google Cloud Storage location. This must be a single object (file).
    GcsSource gcs_source = 1;
  }

  // Required. The input data format.
  DataFormat data_format = 2 [(google.api.field_behavior) = REQUIRED];
}

// Specify a destination for
// [BatchOptimizeTours][google.maps.routeoptimization.v1.RouteOptimizationService.BatchOptimizeTours]
// results.
message OutputConfig {
  // Required.
  oneof destination {
    // The Google Cloud Storage location to write the output to.
    GcsDestination gcs_destination = 1;
  }

  // Required. The output data format.
  DataFormat data_format = 2 [(google.api.field_behavior) = REQUIRED];
}

// The Google Cloud Storage location where the input file will be read from.
message GcsSource {
  // Required. URI of a Google Cloud Storage object with the format
  // `gs://bucket/path/to/object`.
  string uri = 1 [(google.api.field_behavior) = REQUIRED];
}

// The Google Cloud Storage location where the output file(s) will be written
// to.
message GcsDestination {
  // Required. Google Cloud Storage URI.
  string uri = 1 [(google.api.field_behavior) = REQUIRED];
}

// Data formats for input and output files.
enum DataFormat {
  // Invalid value, format must not be UNSPECIFIED.
  DATA_FORMAT_UNSPECIFIED = 0;

  // JavaScript Object Notation.
  JSON = 1;

  // Protocol Buffers text format.  See
  // https://protobuf.dev/reference/protobuf/textformat-spec/
  PROTO_TEXT = 2;
}
