# CLI Tools


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

## Installation

The `p4` CLI is installed automatically when you install `p4tools`:

``` bash
pip install -e .
```

This registers the `p4` entry point (defined via `console_scripts` in
`pyproject.toml`). The CLI depends on
[Typer](https://typer.tiangolo.com/) and
[Rich](https://rich.readthedocs.io/).

## Commands Overview

<table>
<colgroup>
<col style="width: 40%" />
<col style="width: 59%" />
</colgroup>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>p4 setup</code></td>
<td>Configure the Planet Four data root and database path</td>
</tr>
<tr>
<td><code>p4 info</code></td>
<td>Show current configuration and database summary</td>
</tr>
<tr>
<td><code>p4 db-stats</code></td>
<td>Show database statistics and top tiles/obsids by marking count</td>
</tr>
<tr>
<td><code>p4 cluster-tile</code></td>
<td>Cluster a single tile with optional inline plot</td>
</tr>
<tr>
<td><code>p4 cluster-obsid</code></td>
<td>Cluster all tiles for a HiRISE observation ID</td>
</tr>
<tr>
<td><code>p4 produce</code></td>
<td>Run full catalog production with parallel processing</td>
</tr>
</tbody>
</table>

## Parallel Execution Helper

[`run_parallel_with_progress`](https://michaelaye.github.io/p4tools/clis.html#run_parallel_with_progress)
is a general-purpose utility that wraps `ProcessPoolExecutor` with a
Rich progress bar. It is used by the
[`produce`](https://michaelaye.github.io/p4tools/clis.html#produce)
command to parallelize clustering and fnotching across hundreds of
observation IDs.

------------------------------------------------------------------------

<a
href="https://github.com/michaelaye/p4tools/blob/main/p4tools/clis.py#L41"
target="_blank" style="float:right; font-size:smaller">source</a>

### run_parallel_with_progress

``` python

def run_parallel_with_progress(
    func, # A *picklable* top-level function.  Must accept a single positional
argument (the item) plus any keyword arguments from *func_kwargs*.
    items, # Items to map over.
    max_workers:int=4, # Number of parallel processes.
    description:str='Processing', # Label shown in the progress bar.
    func_kwargs:dict | None=None, # Extra keyword arguments forwarded to *func*.
): # Successful results in the original item order (skipping failures).

```

\*Execute func(item, \*\*func_kwargs) in parallel with a Rich progress
bar.\*

## `p4 setup`

Configures the `~/.p4tools.ini` file with the path to the data root
directory where clustering results are stored. Optionally also stores
the path to the raw Planet Four Parquet database.

``` bash
p4 setup ~/planet4_data --db ~/planet4_data/p4_raw.parquet
```

This replaces the old interactive prompt that ran at import time when no
config file existed.

------------------------------------------------------------------------

<a
href="https://github.com/michaelaye/p4tools/blob/main/p4tools/clis.py#L100"
target="_blank" style="float:right; font-size:smaller">source</a>

### setup

``` python

def setup(
    data_root:str=<typer.models.ArgumentInfo object at 0x7f4846579820>,
    db:Optional[str]=<typer.models.OptionInfo object at 0x7f4848267890>
):

```

*Configure the Planet Four data root (and optionally the raw database
path).*

## `p4 info`

Displays the current configuration in a Rich table: config file
location, data root path, and all key/value pairs from the INI file.

``` bash
p4 info
```

------------------------------------------------------------------------

<a
href="https://github.com/michaelaye/p4tools/blob/main/p4tools/clis.py#L127"
target="_blank" style="float:right; font-size:smaller">source</a>

### info

``` python

def info(
    
):

```

*Show current configuration and database summary.*

## `p4 db-stats`

Shows summary statistics for a raw Planet Four database: total markings,
unique tiles and obsids, breakdown by marking type, averages, and the
top tiles/obsids ranked by number of markings. Useful for picking
example tile IDs and obsids to test with `cluster-tile` /
`cluster-obsid`.

``` bash
p4 db-stats --db ~/planet4_data/p4_raw.parquet
p4 db-stats --db ~/planet4_data/p4_raw.parquet --top 20
```

------------------------------------------------------------------------

<a
href="https://github.com/michaelaye/p4tools/blob/main/p4tools/clis.py#L152"
target="_blank" style="float:right; font-size:smaller">source</a>

### db_stats

``` python

def db_stats(
    db:Optional[str]=<typer.models.OptionInfo object at 0x7f4846579010>,
    top:int=<typer.models.OptionInfo object at 0x7f4846579430>
):

```

*Show database statistics and top tiles/obsids by marking count.*

## `p4 random-tile`

Picks a random tile whose marking count is within 50% of the database
average — i.e. a “typical” tile, useful for quick test runs.

``` bash
p4 random-tile
p4 random-tile --db ~/planet4_data/p4_raw.parquet
```

------------------------------------------------------------------------

<a
href="https://github.com/michaelaye/p4tools/blob/main/p4tools/clis.py#L206"
target="_blank" style="float:right; font-size:smaller">source</a>

### random_tile

``` python

def random_tile(
    db:Optional[str]=<typer.models.OptionInfo object at 0x7f484657a4e0>
):

```

*Print a random tile ID with a near-average marking count.*

## Terminal Image Display

[`_display_inline_image`](https://michaelaye.github.io/p4tools/clis.html#_display_inline_image)
renders a PNG/JPEG inline in the terminal using the [iTerm2 inline image
protocol](https://iterm2.com/documentation-images.html) (also supported
by WezTerm). Falls back to printing the file path on unsupported
terminals. Used by `cluster-tile` to show plots without leaving the CLI.

## `p4 cluster-tile`

Clusters citizen science markings on a single Planet Four tile using
DBSCAN, then displays a summary table with fan/blotch cluster counts.
With `--plot` (the default), it also renders a 3-panel figure showing
the tile image, clustered fans, and clustered blotches.

``` bash
# Cluster a single tile
p4 cluster-tile APF00003qk --db ~/planet4_data/p4_raw.parquet

# Without the inline plot
p4 cluster-tile APF00003qk --db ~/planet4_data/p4_raw.parquet --no-plot
```

------------------------------------------------------------------------

<a
href="https://github.com/michaelaye/p4tools/blob/main/p4tools/clis.py#L248"
target="_blank" style="float:right; font-size:smaller">source</a>

### cluster_tile

``` python

def cluster_tile(
    tile_id:str=<typer.models.ArgumentInfo object at 0x7f4846579d90>,
    db:Optional[str]=<typer.models.OptionInfo object at 0x7f4846578cb0>,
    savedir:Optional[str]=<typer.models.OptionInfo object at 0x7f484657af30>,
    plot:bool=<typer.models.OptionInfo object at 0x7f484657bf20>
):

```

*Cluster a single Planet Four tile and display the results.*

## `p4 cluster-obsid`

Clusters all tiles belonging to a HiRISE observation. This is the
obsid-level equivalent of `cluster-tile`. Optionally runs fnotching
(fan/blotch ambiguity resolution) after clustering.

``` bash
# Cluster only
p4 cluster-obsid ESP_011296_0975 --db ~/planet4_data/p4_raw.parquet

# Cluster + fnotch
p4 cluster-obsid ESP_011296_0975 --db ~/planet4_data/p4_raw.parquet --fnotch
```

------------------------------------------------------------------------

<a
href="https://github.com/michaelaye/p4tools/blob/main/p4tools/clis.py#L338"
target="_blank" style="float:right; font-size:smaller">source</a>

### cluster_obsid

``` python

def cluster_obsid(
    obsid:str=<typer.models.ArgumentInfo object at 0x7f48465793d0>,
    db:Optional[str]=<typer.models.OptionInfo object at 0x7f4846579e20>,
    savedir:Optional[str]=<typer.models.OptionInfo object at 0x7f4846579a30>,
    fnotch:bool=<typer.models.OptionInfo object at 0x7f4846579a60>
):

```

*Cluster all tiles for a HiRISE observation ID.*

## `p4 create-mosaic`

Downloads HiRISE RED4/RED5 CCD images and runs the ISIS pipeline to
create a RED45 mosaic cube for a single observation. Useful for testing
mosaic creation before running full production.

``` bash
p4 create-mosaic ESP_011350_0945
p4 create-mosaic ESP_011350_0945 --overwrite
```

------------------------------------------------------------------------

<a
href="https://github.com/michaelaye/p4tools/blob/main/p4tools/clis.py#L378"
target="_blank" style="float:right; font-size:smaller">source</a>

### create_mosaic

``` python

def create_mosaic(
    obsid:str=<typer.models.ArgumentInfo object at 0x7f484657a390>,
    overwrite:bool=<typer.models.OptionInfo object at 0x7f484657a3c0>
):

```

*Create a RED45 mosaic cube for a single observation ID.*

### Picklable Wrappers

[`_cluster_single`](https://michaelaye.github.io/p4tools/clis.html#_cluster_single)
and
[`_fnotch_single`](https://michaelaye.github.io/p4tools/clis.html#_fnotch_single)
are top-level wrapper functions that can be pickled by
`ProcessPoolExecutor`. They perform lazy imports to avoid serialization
issues with module-level state.

## `p4 produce`

The main catalog production command. Runs the full pipeline in four
phases:

1.  **Clustering** — Parallel DBSCAN clustering of all observation IDs
2.  **Fnotching** — Parallel fan/blotch ambiguity resolution
3.  **Post-processing** — L1C summaries, tile/marking coordinates,
    metadata
4.  **Marking IDs** — Assigns unique identifiers to all catalog entries

Uses
[`run_parallel_with_progress`](https://michaelaye.github.io/p4tools/clis.html#run_parallel_with_progress)
for phases 1 and 2, with configurable worker count. Supports `--dry-run`
to preview the work without executing.

``` bash
# Full production run
p4 produce v3.1 --db ~/planet4_data/p4_raw.parquet --workers 8

# Preview what would be done
p4 produce v3.1 --db ~/planet4_data/p4_raw.parquet --dry-run
```

------------------------------------------------------------------------

<a
href="https://github.com/michaelaye/p4tools/blob/main/p4tools/clis.py#L417"
target="_blank" style="float:right; font-size:smaller">source</a>

### produce

``` python

def produce(
    version:str=<typer.models.ArgumentInfo object at 0x7f4846579d60>,
    db:Optional[str]=<typer.models.OptionInfo object at 0x7f484657b7d0>,
    workers:int=<typer.models.OptionInfo object at 0x7f484657aff0>,
    dry_run:bool=<typer.models.OptionInfo object at 0x7f484657a3f0>
):

```

*Run full catalog production with parallel processing and Rich
progress.*
