Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ that may never merge. They are not releases and are not listed here.

## Unreleased

- `mapbox optimization submit`/`get`/`list`, solving a multi-vehicle,
multi-stop routing problem (v2 — v1 is retired) as an asynchronous job:
submit the problem, poll `get` for the solved routes, `list` to see every
job's status without tracking ids yourself. Hand-authored into
`custom-openapi/` for the same reason the other commands added this
session were: no upstream spec exists yet. `submit`'s whole problem
(locations, vehicles, services/shipments, options) goes through `--data`
as one JSON document, the same shape `styles create` already uses for a
body with no sensible per-field flag.

- `mapbox feedback list`/`get`, reading feedback submitted against Mapbox
API responses — filterable, sortable, paginated. Hand-authored into
`custom-openapi/` for the same reason the Navigation commands were: no
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ mapbox geocoder *
mapbox isochrone *
mapbox map-matching *
mapbox matrix *
mapbox optimization *
mapbox search *
mapbox sprites *
mapbox static *
Expand Down
127 changes: 127 additions & 0 deletions custom-openapi/optimization/openapi/optimization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
openapi: "3.0.0"
# `parse_spec` turns `info.description` below into this service's clap
# `long_about`, so it also reaches `mapbox optimization --help`, `--schema`
# and `generate-skills` output. Keep it to API prose only — the provenance
# below is for whoever edits this file, not for a CLI user:
#
# Hand-authored down to the parameters documented at
# docs.mapbox.com/api/navigation/optimization (v2 — v1 is retired). See
# `custom-openapi/README.md` for how a file like this is wired in.
info:
title: "Mapbox Optimization API v2"
description: >-
Solves a multi-vehicle, multi-stop routing problem — which vehicle
visits which stop, and in what order — as an asynchronous job: submit
it, then poll for the solved routes.
version: "0.0.0"
servers:
- url: https://api.mapbox.com
description: Optimization API v2
paths:
/optimized-trips/v2:
post:
operationId: submit
summary: Submit a routing problem to solve.
description: >-
Accepted (202) with a job `id` and a `status`; poll `mapbox
optimization get <id>` until it stops answering 202. Up to 1000
locations per problem. `--data` carries the whole problem as one
JSON document — there is no per-field flag for something this
shape. Top-level fields: `version` (always `1`), `locations`
(`name` + `[longitude, latitude]` `coordinates`, every name unique),
`vehicles` (`name`, and optionally `routing_profile` —
`mapbox/driving` by default — `start_location`/`end_location`,
`capacities`, `capabilities`, `earliest_start`/`latest_end`,
`breaks`, `loading_policy`: `any`/`fifo`/`lifo`), `services` — a
single stop to visit (`name`, `location`, `duration`,
`requirements`, `service_times`) — and/or `shipments` — a
pickup-then-dropoff pair (`name`, `from`, `to`, `size`,
`requirements`, `pickup_duration`/`dropoff_duration`,
`pickup_times`/`dropoff_times`); at least one of `services` or
`shipments` is required. `options.objectives` picks what to
optimize for: `min-total-travel-duration` or
`min-schedule-completion-time`.
parameters:
- name: "access_token"
in: query
required: true
description: "Mapbox API Access Token"
schema:
type: string
minLength: 1
requestBody:
required: true
content:
application/json:
schema:
type: object
responses:
"202":
description: >-
Accepted: `{"id": "<uuid>", "status": "ok|pending|processing"}`.
Not solved yet — poll `optimization get`.
"401":
description: Unauthorized
"422":
description: Unprocessable Entity — a malformed problem.

get:
operationId: list
summary: List every submitted routing problem and its status.
description: >-
Every job this account has submitted, each as `{"id", "status"}` —
`status` is `pending`, `processing`, or `complete`. Does not return
the solved routes themselves; `optimization get <id>` does, once
`status` is `complete`.
parameters:
- name: "access_token"
in: query
required: true
description: "Mapbox API Access Token"
schema:
type: string
minLength: 1
responses:
"200":
description: "A JSON array of `{id, status}` objects, one per submitted job."
"401":
description: Unauthorized

/optimized-trips/v2/{id}:
get:
operationId: get
summary: Retrieve a routing problem's solved routes.
description: >-
Answers 202 with no body while still solving; 200 with the solved
routes once done — one entry per vehicle, each a list of stops
(`start`, `service`, `pickup`, `dropoff`, `break`, `end`) with an
ETA and running odometer, plus a `dropped` list of any
service/shipment the solver couldn't fit in.
parameters:
- name: "id"
in: path
required: true
description: "The job id `optimization submit` returned."
schema:
type: string
minLength: 1
- name: "access_token"
in: query
required: true
description: "Mapbox API Access Token"
schema:
type: string
minLength: 1
responses:
"200":
description: >-
Solved: `routes` (one per vehicle, each a `vehicle` name and a
`stops` array — `type`, `location`, `eta`, `odometer`, and for
a `service`/`pickup`/`dropoff` stop the names it covers) and
`dropped` (`services`/`shipments` the solver left out).
"202":
description: "Still processing — no body. Poll again."
"401":
description: Unauthorized
"404":
description: Not Found — no job with that id.
184 changes: 184 additions & 0 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -1691,6 +1691,190 @@ Neither output mode has a bespoke rendering for this response, same as
`-o text` pretty-printed and `-o json` on one line. Dropped each waypoint's
own snap `distance` for length; the real response carries it too.

---
## Optimization

Solves a multi-vehicle, multi-stop routing problem — which vehicle visits
which stop, and in what order — as an asynchronous job (v2; v1 is
retired). Curated by hand down to the parameters documented at
docs.mapbox.com/api/navigation/optimization — see `custom-openapi/README.md`
for why this command group doesn't come from the vendored specs the way
most others do.

**vs. `matrix compute`**: that ranks reachability across many pairs;
this decides an actual visiting order for one or more vehicles, given
constraints (time windows, capacities, breaks) `matrix` knows nothing
about.

### `mapbox optimization submit`

Submits a routing problem. Accepted (202) with a job `id` and `status` —
`optimization get <id>` is the follow-up poll, not this command; solving
happens asynchronously and can take a while for a large problem.

#### Parameters

`--data`/`-d` carries the whole problem as one JSON document — there is no
per-field flag for something this shape. Top-level fields:

| Field | Effect |
| --- | --- |
| `version` | Always `1`. |
| `locations` | `name` (unique) + `coordinates` (`[longitude, latitude]`) per stop. |
| `vehicles` | `name` (unique); optionally `routing_profile` (`mapbox/driving` by default), `start_location`/`end_location`, `capacities`, `capabilities`, `earliest_start`/`latest_end`, `breaks`, `loading_policy` (`any`/`fifo`/`lifo`). |
| `services` | A single stop to visit: `name`, `location`, `duration`, `requirements`, `service_times`. At least one of `services`/`shipments` is required. |
| `shipments` | A pickup-then-dropoff pair: `name`, `from`, `to`, `size`, `requirements`, `pickup_duration`/`dropoff_duration`, `pickup_times`/`dropoff_times`. |
| `options.objectives` | What to optimize for: `min-total-travel-duration` or `min-schedule-completion-time`. |

#### Examples

```sh
mapbox optimization submit -d '{
"version": 1,
"locations": [
{"name": "depot", "coordinates": [-122.42, 37.78]},
{"name": "stop1", "coordinates": [-122.45, 37.91]},
{"name": "stop2", "coordinates": [-122.41, 37.80]}
],
"vehicles": [{"name": "van1", "start_location": "depot", "end_location": "depot"}],
"services": [
{"name": "svc1", "location": "stop1"},
{"name": "svc2", "location": "stop2"}
]
}'
```

#### Outputs

Captured live — one vehicle, two single-stop services:

<table>
<tr><th width="50%">Terminal — <code>-o text</code></th><th width="50%">Agent — <code>-o json</code></th></tr>
<tr><td>

```json
{ "id": "5f57b00c-a3a1-45de-89ca-1e24b3a23cc4.r1", "status": "ok" }
```

</td><td>

```json
{"id":"5f57b00c-a3a1-45de-89ca-1e24b3a23cc4.r1","status":"ok"}
```

</td></tr>
</table>

### `mapbox optimization get`

Retrieves a submitted problem's solved routes. Answers 202 with no body
while still solving; 200 with the routes once done.

#### Parameters

`<id>` (positional) is required — the job id `submit` returned.

#### Examples

```sh
mapbox optimization get 5f57b00c-a3a1-45de-89ca-1e24b3a23cc4.r1
```

#### Outputs

Captured live — the job submitted above, once solved: one route, a start,
two service stops in the order the solver chose, and an end, each with a
running `odometer` (meters) and `eta`:

<table>
<tr><th width="50%">Terminal — <code>-o text</code></th><th width="50%">Agent — <code>-o json</code></th></tr>
<tr><td>

```json
{
"dropped": { "services": [], "shipments": [] },
"routes": [
{
"vehicle": "van1",
"stops": [
{ "type": "start", "location": "depot", "eta": "1970-01-01T00:00:00Z", "odometer": 0 },
{
"type": "service",
"location": "stop1",
"eta": "1970-01-01T00:39:41Z",
"odometer": 25766,
"services": ["svc1"]
},
{
"type": "service",
"location": "stop2",
"eta": "1970-01-01T01:17:33Z",
"odometer": 51148,
"services": ["svc2"]
},
{ "type": "end", "location": "depot", "eta": "1970-01-01T01:34:07Z", "odometer": 54929 }
]
}
]
}
```

</td><td>

```json
{"dropped":{"services":[],"shipments":[]},"routes":[{"vehicle":"van1","stops":[{"type":"start","location":"depot","eta":"1970-01-01T00:00:00Z","odometer":0},{"type":"service","location":"stop1","eta":"1970-01-01T00:39:41Z","odometer":25766,"services":["svc1"]},{"type":"service","location":"stop2","eta":"1970-01-01T01:17:33Z","odometer":51148,"services":["svc2"]},{"type":"end","location":"depot","eta":"1970-01-01T01:34:07Z","odometer":54929}]}]}
```

</td></tr>
</table>

Neither output mode has a bespoke rendering for this response — both print
the same JSON, `-o text` pretty-printed and `-o json` on one line. Dropped
each stop's `location_metadata` (snapped vs. supplied coordinate) and `wait`
for length; the real response carries them too. The `1970-01-01` dates are
this problem's own timestamps — no `earliest_start`/time window was given,
so the solver counted elapsed seconds from epoch rather than a real clock.

### `mapbox optimization list`

Every routing problem this account has submitted, with its status —
`pending`, `processing`, or `complete`. Does not return the solved routes
themselves; `optimization get <id>` does, once `status` is `complete`.

#### Parameters

None.

#### Examples

```sh
mapbox optimization list
```

#### Outputs

Captured live, the same job as above, now complete:

<table>
<tr><th width="50%">Terminal — <code>-o text</code></th><th width="50%">Agent — <code>-o json</code></th></tr>
<tr><td>

```json
[{ "id": "5f57b00c-a3a1-45de-89ca-1e24b3a23cc4.r1", "status": "complete" }]
```

</td><td>

```json
[{"id":"5f57b00c-a3a1-45de-89ca-1e24b3a23cc4.r1","status":"complete"}]
```

</td></tr>
</table>

A bare JSON array, not wrapped in an `items`/`results` field — every entry
is just `{id, status}`.

---

## Search
Expand Down
4 changes: 4 additions & 0 deletions src/remedy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ const SERVICE_DOCS: &[(&str, &str)] = &[
"https://docs.mapbox.com/api/navigation/map-matching/",
),
("matrix", "https://docs.mapbox.com/api/navigation/matrix/"),
(
"optimization",
"https://docs.mapbox.com/api/navigation/optimization/",
),
("search", "https://docs.mapbox.com/api/search/search-box/"),
// Static Images and Static Tiles merged into one `static` command group
// (#116); neither upstream page covers both, so this points at Static
Expand Down
4 changes: 4 additions & 0 deletions src/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,10 @@ pub const CUSTOM_SPEC_ENTRIES: &[SpecEntry] = &[
name: "feedback",
yaml: include_str!("../custom-openapi/feedback/openapi/feedback.yaml"),
},
SpecEntry {
name: "optimization",
yaml: include_str!("../custom-openapi/optimization/openapi/optimization.yaml"),
},
];

/// The list the CLI actually generates commands from: [`MAPBOX_SPEC_ENTRIES`],
Expand Down
3 changes: 3 additions & 0 deletions tests/fixtures/api_command_surface.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ mapbox geocoder reverse | aliases: (none)
mapbox isochrone contours | aliases: (none)
mapbox map-matching match | aliases: (none)
mapbox matrix compute | aliases: (none)
mapbox optimization get | aliases: (none)
mapbox optimization list | aliases: (none)
mapbox optimization submit | aliases: (none)
mapbox search category | aliases: (none)
mapbox search forward | aliases: (none)
mapbox search list-category | aliases: (none)
Expand Down
Loading