// 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 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 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 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 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 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 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 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 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 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; }