Skip to content
Open
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
30 changes: 21 additions & 9 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,28 @@ that may never merge. They are not releases and are not listed here.

## Unreleased

- `mapbox isochrone`, how far you can get from a point in a given time or
distance, for driving (with or without live traffic), walking, or
cycling, returned as GeoJSON polygons or linestrings. No subcommand:
like `mapbox directions` below, this API has one operation, so there's
nothing a second word would disambiguate; see `spec::FLATTENED_SERVICES`.
Hand-authored into `custom-openapi/` for the same reason `mapbox
directions` was: no upstream spec exists yet. Reuses `directions`'s fix
for a spec parameter named `profile` colliding with the global
`--profile` flag (`ARG_NAME_OVERRIDES` already covered the mechanism,
this is just a second row, not a second fix) and its free-form (not
`enum`) routing profile, for the same OEM-account reason.

- `mapbox directions`, routes between 2-25 waypoints for driving (with
or without live traffic), walking, or cycling. No subcommand: this API
has one operation, so — like `mapbox usage` — there's nothing a second
has one operation, so, like `mapbox usage`, there's nothing a second
word would disambiguate; see `spec::FLATTENED_SERVICES`. Hand-authored
into `custom-openapi/` rather than waiting on an upstream spec — the whole
Navigation API category had no CLI coverage before this; excludes the
~30 electric-vehicle-routing parameters (`engine=electric` and everything
under it), which describe one vehicle's charge/discharge curve down to
the watt and are a poor fit for a hand-typed CLI flag — left for a
follow-up.
into `custom-openapi/` rather than waiting on an upstream spec. The
whole Navigation API category had no CLI coverage before this; excludes
the ~30 electric-vehicle-routing parameters (`engine=electric` and
everything under it), which describe one vehicle's charge/discharge
curve down to the watt and are a poor fit for a hand-typed CLI flag.
Left for a follow-up.

The routing profile (`mapbox/driving` etc.) is a free-form value, not a
fixed set of four: an early version rejected anything else client-side,
Expand All @@ -43,8 +55,8 @@ that may never merge. They are not releases and are not listed here.
outright; and a path parameter whose every legitimate value contains a
literal `/` (`mapbox/driving`) was being percent-encoded to `%2F` by the
same escaping that stops a free-text value from smuggling in extra path
segments — safe to skip for a parameter named in a small table
(`UNESCAPED_PATH_PARAMS`) as one whose values are trusted to carry that
segments. That's safe to skip for a parameter named in a small table
(`UNESCAPED_PATH_PARAMS`) whose values are trusted to carry that
character on purpose.

- `MAPBOX_CLI_EXTRA_QUERY` appends raw query parameters to every request, in
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,10 @@ mapbox styles *
mapbox tilesets *
```

`mapbox directions` is the one exception: its API has a single operation,
so there's a bare command with no subcommand at all, the same shape
`mapbox usage` already has — see [docs/commands.md](./docs/commands.md) for
its own parameters.
`mapbox directions` and `mapbox isochrone` are the exceptions: each API has
a single operation, so there's a bare command with no subcommand at all,
the same shape `mapbox usage` already has, see
[docs/commands.md](./docs/commands.md) for their own parameters.

A command group is not the same thing as a spec file: which one an operation
belongs to is decided per operation. So `sprites` and `tilesets` are each
Expand Down
170 changes: 170 additions & 0 deletions custom-openapi/isochrone/openapi/isochrone.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
openapi: "3.0.0"
# `parse_spec` turns `info.description` below into this service's clap
# `long_about`, so it also reaches `mapbox isochrone --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/isochrone. See `custom-openapi/README.md`
# for how a file like this is wired in, and `directions.yaml`'s header for
# why `profile` needs `ARG_NAME_OVERRIDES` in `src/spec.rs` — the same
# reason applies here.
info:
title: "Mapbox Isochrone API"
description: >-
How far you can get from a point in a given time or distance, for
driving (with or without live traffic), walking, or cycling — as a
GeoJSON polygon or line per contour.
version: "0.0.0"
servers:
- url: https://api.mapbox.com
description: Isochrone API
paths:
/isochrone/v1/{profile}/{coordinates}:
get:
operationId: contours
summary: Isochrone contours around one point.
description: >-
Returns one contour per value in `--contours-minutes` or
`--contours-meters` (exactly one of the two is required; not
enforced before the request goes out — the API answers 422 if both
or neither are given), as GeoJSON linestrings or, with `--polygons`,
polygons.
parameters:
- name: "profile"
in: path
required: true
# Not an `enum`: the four documented values are what's public, but
# not what's exhaustive — some customers (OEM agreements, mainly)
# have additional profiles never published to docs.mapbox.com.
# An `enum` here becomes a clap `PossibleValuesParser` that
# rejects anything else client-side, which would break this CLI
# for exactly the accounts that most need a routing profile
# named beyond `driving`/`walking`/`cycling`. Same fix as
# `directions.yaml`'s `profile`.
description: >-
The routing profile — `mapbox/driving-traffic` (accounts for
live traffic), `mapbox/driving`, `mapbox/walking`, or
`mapbox/cycling` are documented, but not necessarily
exhaustive: some accounts have additional profiles of their
own. Sent exactly as typed; the API is the authority on
whether a value is valid, not this description.
schema:
type: string
minLength: 1
example: "mapbox/driving"
- name: "coordinates"
in: path
required: true
description: "The isochrone center, `{longitude},{latitude}`."
schema:
type: string
minLength: 1
example: "-122.42,37.78"
- name: "access_token"
in: query
required: true
description: "Mapbox API Access Token"
schema:
type: string
minLength: 1
# Prose rather than an `enum`: up to 4 comma-separated integers, and
# the command builder turns a spec `enum` into a clap
# `PossibleValuesParser`, which accepts one value and would refuse a
# list. Same reasoning as `directions.yaml`'s `annotations`.
- name: "contours_minutes"
in: query
required: false
description: >-
Up to 4 times in minutes, 1-60, comma-separated and increasing —
one contour per value. Exactly one of this or
`--contours-meters` is required.
schema:
type: string
example: "5,10,15,20"
- name: "contours_meters"
in: query
required: false
description: >-
Up to 4 distances in meters, 1-100000, comma-separated and
increasing — one contour per value. Exactly one of this or
`--contours-minutes` is required.
schema:
type: string
example: "1000,5000"
- name: "contours_colors"
in: query
required: false
description: >-
A hex color per contour (no `#`), comma-separated — must match
the contour count.
schema:
type: string
example: "ff0000,00ff00"
- name: "polygons"
in: query
required: false
description: >-
Return each contour as a GeoJSON polygon instead of a
linestring.
schema:
type: boolean
- name: "denoise"
in: query
required: false
# A number before the range, not after: `first_sentence` in
# `src/main.rs` cuts a `--help` line at the first `.`, and
# `0.0-1.0:` on its own left `--help` showing just `0`.
# `--schema` and `docs/commands.md` still show it in full.
description: >-
A smaller value removes more of the smaller contours, 0.0-1.0,
defaulting to 1.0.
schema:
type: number
minimum: 0
maximum: 1
- name: "generalize"
in: query
required: false
description: >-
Douglas-Peucker simplification tolerance in meters. A higher
value is a coarser, smaller contour.
schema:
type: number
# Prose rather than an `enum`, for the reason given on
# `contours_minutes` above.
- name: "exclude"
in: query
required: false
description: >-
Road types to route around, comma-separated. Options are
`motorway`, `toll`, `ferry`, `unpaved`, `cash_only_tolls`.
schema:
type: string
example: "motorway,toll"
- name: "depart_at"
in: query
required: false
description: >-
Departure time, ISO 8601 — for `mapbox/driving-traffic`, which
live traffic conditions to route against.
schema:
type: string
responses:
"200":
description: >-
A GeoJSON `FeatureCollection`, one feature per contour, each
carrying `contour` (the minute or meter value), `metric`
(`time` or `distance`), and rendering hints (`color`,
`opacity`, and — with `--polygons` — `fill`/`fill-opacity`).
"401":
description: Unauthorized
"403":
description: Forbidden
"404":
description: Not Found — an invalid profile.
"422":
description: >-
Unprocessable Entity — invalid coordinates, an out-of-range
contour value, or neither/both of `contours_minutes` and
`contours_meters` given.
99 changes: 99 additions & 0 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ nests, and is typed `mapbox styles draft get`.
[geocoder.reverse](#mapbox-geocoder-reverse) ·
[geocoder.batch](#mapbox-geocoder-batch)

**[Isochrone](#isochrone)** — [isochrone](#mapbox-isochrone)

**[Search](#search)** — [search.forward](#mapbox-search-forward) ·
[search.reverse](#mapbox-search-reverse) ·
[search.category](#mapbox-search-category) ·
Expand Down Expand Up @@ -1235,6 +1237,103 @@ A query malformed enough that its own list can't be built falls the whole
batch back to pretty-printed JSON, same as one broken feature does for a
single query.

---
## Isochrone

How far you can get from a point in a given time or distance, for driving
(with or without live traffic), walking, or cycling. Curated by hand down to
the parameters documented at docs.mapbox.com/api/navigation/isochrone — see
`custom-openapi/README.md` for why this command group doesn't come from the
vendored specs the way most others do.

### `mapbox isochrone`

One contour per value in `--contours-minutes` or `--contours-meters`, as
GeoJSON around the given center point. No subcommand: this API has one
operation, so there's nothing a second word would disambiguate, the same
reason `mapbox directions` has none either.

#### Parameters

`<routing-profile>` and `<coordinates>` (both positional) are required.
`<routing-profile>` is sent exactly as typed, not checked against a fixed
list: `mapbox/driving-traffic`, `mapbox/driving`, `mapbox/walking` and
`mapbox/cycling` are documented, but some accounts (OEM agreements, mainly)
have additional profiles of their own that were never published, the API
is the authority on whether a value is valid, not this page. `<coordinates>`
is one `{longitude},{latitude}` pair, unlike `mapbox directions`, this
command takes a single center point, not a list of waypoints.

Exactly one of `--contours-minutes` or `--contours-meters` is required by
the API, though nothing here enforces it before the request goes out.

| Parameter | Effect |
| --- | --- |
| `--contours-minutes <mins>` | Up to 4 times in minutes, 1-60, comma-separated and increasing. One contour per value. |
| `--contours-meters <meters>` | Up to 4 distances in meters, 1-100000, comma-separated and increasing. One contour per value. |
| `--contours-colors <hex,...>` | A hex color per contour (no `#`), comma-separated — must match the contour count. |
| `--polygons` | Return each contour as a GeoJSON polygon instead of a linestring. |
| `--denoise <0.0-1.0>` | A smaller value removes more of the smaller contours. Defaults to 1.0. |
| `--generalize <meters>` | Douglas-Peucker simplification tolerance — a higher value is a coarser, smaller contour. |
| `--exclude <types>` | Road types to route around, comma-separated (`motorway`, `toll`, `ferry`, `unpaved`, `cash_only_tolls`). |
| `--depart-at <ISO 8601>` | For `mapbox/driving-traffic`, which live traffic conditions to route against. |

#### Examples

```sh
mapbox isochrone mapbox/driving "-122.42,37.78" --contours-minutes 5,10,15
mapbox isochrone mapbox/walking "-122.42,37.78" --contours-minutes 5,10 --polygons
```

#### Outputs

Captured live against `mapbox/walking`, two 5- and 10-minute contours as
polygons. This response is a real GeoJSON `FeatureCollection`, unlike
`mapbox directions`'s response, but isochrone isn't one of the three
services (`search`, `geocoder`, `tilequery`) this CLI has a bespoke
list-per-feature rendering for yet (`output.rs`'s `list_rendering` is an
exact service allow-list, not a "looks like GeoJSON" test), so both output
modes print the same JSON, `-o text` pretty-printed and `-o json` on one
line, same shape as `mapbox directions`'s Outputs section above:

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

```json
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"color": "#bf4040",
"contour": 10,
"fill": "#bf4040",
"fill-opacity": 0.33,
"fillColor": "#bf4040",
"fillOpacity": 0.33,
"metric": "time",
"opacity": 0.33
},
"geometry": { "type": "Polygon", "coordinates": "…" }
}
]
}
```

</td><td>

```json
{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"color":"#bf4040","contour":10,"fill":"#bf4040","fill-opacity":0.33,"fillColor":"#bf4040","fillOpacity":0.33,"metric":"time","opacity":0.33},"geometry":{"type":"Polygon","coordinates":"…"}}]}
```

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

Trimmed to one of the two features (the response has one per
`--contours-minutes` value) and the polygon's coordinates, for length.

---

## Search
Expand Down
4 changes: 4 additions & 0 deletions src/remedy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ const SERVICE_DOCS: &[(&str, &str)] = &[
"geocoder",
"https://docs.mapbox.com/api/search/geocoding-v6/",
),
(
"isochrone",
"https://docs.mapbox.com/api/navigation/isochrone/",
),
("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
Loading
Loading