Skip to content

CLI

The mapcv command follows the order you work in: init → plan → generate → info, plus split to redo splits, stats, card and verify to prepare a dataset for training and sharing, validate to check a config, doctor to check your installation and mcp to let an AI agent do all of this. The help blocks on this page are snapshots of mapcv --help from mapcv 0.2.0.

Command What it does Touches the network
mapcv init Write a config, guided or from a template no
mapcv plan Estimate tiles, patches, disk and memory only to read a remote GeoTIFF’s header or fetch labels.osm
mapcv generate Build or resume the dataset yes
mapcv info Summarize a dataset folder no
mapcv split Rewrite train/val/test lists from the manifest no
mapcv stats Per-band mean/std and class weights into stats.json no
mapcv card Write a dataset card (README.md) no
mapcv verify Check every file is there and unchanged no
mapcv export Hugging Face Parquet, WebDataset shards, Zarr or a TerraTorch data config no
mapcv validate Check a config’s fields no
mapcv cache Show or clear the on-disk tile cache no
mapcv mcp Serve mapcv to AI agents over MCP only when an agent generates
mapcv doctor Check the installation, extras, tile cache and network a few small requests (none with --offline)
mapcv --help
Usage: mapcv [OPTIONS] COMMAND [ARGS]...
Turn a region, imagery and labels into a ready-to-train segmentation, detection,
instance segmentation, classification, change detection or regression dataset.
Start with mapcv init, check the cost with mapcv plan, then build with mapcv generate.
╭─ Options ────────────────────────────────────────────────────────────────────────────╮
│ --version -V Show the mapcv version and exit. │
│ --quiet -q Hide progress output; still show errors and │
│ summaries. │
│ --no-color Print without colours, as NO_COLOR=1 does. │
│ --debug Show full tracebacks and mapcv's debug log (for │
│ bug reports). │
│ --debug-log FILE Also write the debug log to FILE. │
│ --install-completion Install completion for the current shell. │
│ --show-completion Show completion for the current shell, to copy │
│ it or customize the installation. │
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────────────╯
╭─ 1. Build a dataset ─────────────────────────────────────────────────────────────────╮
│ init Create a config: answer a few questions, or start from a template. │
│ plan Estimate tiles, patches, disk and memory for a config without downloading. │
│ generate Download imagery, rasterize labels and write patches, masks, manifest and │
│ splits. │
╰──────────────────────────────────────────────────────────────────────────────────────╯
╭─ 2. Use a dataset ───────────────────────────────────────────────────────────────────╮
│ info Summarize a generated dataset: source, shapes, class balance and splits. │
│ split Re-split an existing dataset from its manifest; no images are read. │
│ stats Per-band mean and std, class balance and class weights, written to │
│ stats.json. │
│ card Write a dataset card (README.md with Hugging Face metadata) for sharing. │
│ verify Check that every file is present and intact (and SHA256SUMS, when there is │
│ one). │
│ export Export a dataset: Hugging Face Parquet, WebDataset shards, Zarr or a │
│ TerraTorch config. │
╰──────────────────────────────────────────────────────────────────────────────────────╯
╭─ 3. Utilities ───────────────────────────────────────────────────────────────────────╮
│ validate Check a config without reading labels or imagery (use plan for estimates). │
│ cache Show or clear the on-disk cache of downloaded XYZ tiles. │
│ mcp Run an MCP server over stdio so AI agents can build datasets with mapcv. │
│ doctor Diagnose the installation, extras, tile cache and network. │
╰──────────────────────────────────────────────────────────────────────────────────────╯
Option Meaning
--version, -V Print mapcv <version> and exit
--quiet, -q Hide progress bars and progress messages. Put it before the command: mapcv -q generate mapcv.yaml
--no-color Print without colours (bold and dim stay), as the NO_COLOR environment variable does. Output that goes to a file or a pipe has no colours anyway
--debug After an error, also print the full traceback, and print mapcv’s debug log (imagery, chunks, tile cache, failed tiles and their causes, resume decisions) on stderr. For bug reports; tile URLs show only their host
--debug-log FILE Write the debug log, tracebacks included, to FILE (works with or without --debug)
--install-completion Install tab completion for the current shell
--show-completion Print the completion script
--help, -h Show help; works on every command

--quiet hides the progress bars, the “Opening imagery” spinner and the resume message. Plans, warnings, errors and summaries (including the mapcv split summary line) are still printed.

Results (plans, summaries, tables) go to stdout; errors, with the next step to take, go to stderr, so mapcv plan mapcv.yaml > plan.txt still shows a failure on the terminal. Output that goes to a file or a pipe, or to a terminal with TERM=dumb, has no colours, spinners or redrawn lines: generate prints Opening imagery… and then a plain line per tenth of the chunks instead of a progress bar:

Reading imagery and writing patches: 0/40 chunks (0%), 0s
Reading imagery and writing patches: 4/40 chunks (10%), 31s
…
Reading imagery and writing patches: 40/40 chunks (100%), 5m 12s

Counts read 1 patch and 1,234 patches, sizes 750.0 KB and 3.3 MB (1 KB = 1,000 bytes) and durations 45s, 3m 07s or 2h 05m. A command suggested in the output quotes a path with spaces, so it can be copied as it is.

mapcv init --help
Usage: mapcv init [OPTIONS] [OUTPUT]
Create a config: answer a few questions, or start from a template.
╭─ Arguments ──────────────────────────────────────────────────────────────────────────╮
│ OUTPUT <path> Where to write the config. [default: mapcv.yaml] │
╰──────────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────────────╮
│ --template -t NAME Write a ready-made example instead of │
│ asking: xyz, sentinel2, geotiff, │
│ earth-engine, detection, instance, │
│ classification, change or regression. │
│ --interactive --no-interactive Ask questions (default: when run in a │
│ terminal without --template). │
│ --force -f Overwrite an existing file. │
│ --stdout Print the template instead of writing. │
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────────────╯
Examples:
mapcv init guided, writes mapcv.yaml
mapcv init --template xyz --stdout print a template
mapcv init my.yaml -t sentinel2 a ready-made example
mapcv init gee.yaml -t earth-engine Sentinel-2 from Earth Engine
mapcv init boxes.yaml -t detection boxes for COCO and YOLO
mapcv init masks.yaml -t instance a mask per object (COCO RLE)
mapcv init tiles.yaml -t classification a label per patch (CSV)
mapcv init pairs.yaml -t change before/after pairs and change masks
mapcv init heights.yaml -t regression float targets from a raster
  • Writes mapcv.yaml in the current folder, or the output path you give.

  • Guided (default in an interactive terminal without --template): four steps (imagery, area, labels, patches and output). For Google Earth Engine imagery (gee) it offers Sentinel-2 (10 m, with cloud filtering and masking), Landsat 8 (30 m), NAIP (about 0.6 m, United States) or any catalog image or collection, then asks for the dates, the cloud limit, your Cloud project and a zoom matched to the data’s resolution. The area can be a bounding box west,south,east,north or a vector file (.geojson/.json, .kml, .gpkg, .shp, .parquet/.geoparquet), whose polygon extent becomes the region (for a GeoPackage with several layers it asks which one); the wizard then offers to use the same file as labels and lists its attribute fields with example values (fields with more than 255 distinct values, such as IDs, are left out). With labels it also asks for the task: segmentation (the default), detection, instance or classification, for detection which box formats to write (COCO, YOLO or both), for instance whether to also write instance-ID PNGs, and for classification whether each patch gets one label or every class present. A label file ending in .tif/.tiff is taken as a label raster: the wizard shows its CRS, size and NoData, lists the values under the area with their share (from an overview or a sample of a large area), and writes a classes block that maps each value to an ID (the value itself when all are 1–254, else 1, 2, 3, … in value order; 0 is background) named value_<n>, for you to rename and merge; the task is then segmentation (set task: classification in the file to label patches from the raster instead).

  • Template (--template xyz, --template sentinel2, --template geotiff, --template earth-engine for a cloud-free Sentinel-2 composite from Google Earth Engine, --template detection for boxes or --template instance for masks from the quickstart buildings, --template classification for a label per patch, --template change for before/after pairs with a change mask, --template regression for float targets from a raster of values, or whenever stdin/stdout is not a terminal): writes a fully commented example. Add --stdout to print the template instead of writing a file.

  • With GeoTIFF / COG imagery the wizard asks for the file’s path or URL right after the imagery choice, opens it, shows its CRS, size, band count, data type, NoData value and overviews, offers the whole file as the area (press Enter), and picks png or npy output to match the data (for a file with more than 3 uint8 bands it asks which bands to use).

  • The wizard checks each answer where you give it and asks again: a bounding box that is not west,south,east,north (a box that looks like latitude,longitude is shown with the longitude first), a zoom outside 1–22, an Earth Engine date that is not YYYY-MM-DD, a tile URL without {z}, {x} and {y}, a Sentinel-2 product that is empty, and a label file that does not exist or has a suffix no label reader takes. After a bounding box it prints the centre (longitude, latitude), so a swapped pair shows. It does not ask for change detection or regression: those need before/after pairs or a raster of values, so start from mapcv init --template change or --template regression and edit the file.

  • Without a terminal (piped input, or --no-interactive) and without --template, init writes the xyz template for its example region (Lahore) and says so, naming --template.

  • An existing file is never overwritten without --force (the guided mode asks first, before its questions):

    Terminal window
    $ mapcv init --template xyz
    mapcv.yaml already exists.
    Add --force to overwrite it, or name another file.
  • Ctrl-C at a question stops with Interrupted. Nothing was written. (exit code 130). When the input ends before the last question (answers piped in that run out), nothing is written either and mapcv suggests --template (exit code 1).

  • The written file is validated. If it needs edits (for example, a custom tile URL entered in the wizard without {x}, {y} and {z}), mapcv still writes it and lists the fields to fix.

Terminal window
$ mapcv init # guided, writes mapcv.yaml
$ mapcv init s2.yaml -t sentinel2 # Sentinel-2 template into s2.yaml
$ mapcv init ortho.yaml -t geotiff # GeoTIFF/COG template into ortho.yaml
$ mapcv init --template xyz --stdout # print the XYZ template
$ mapcv init boxes.yaml -t detection # COCO + YOLO boxes from building footprints
$ mapcv init masks.yaml -t instance # COCO RLE masks, one per building footprint
$ mapcv init gee.yaml -t earth-engine # cloud-free Sentinel-2 composite from Earth Engine
$ mapcv init tiles.yaml -t classification # one label, or a set of labels, per patch
$ mapcv init change.yaml -t change # before/after pairs and a change mask
$ mapcv init height.yaml -t regression # float targets from a raster of values
mapcv plan --help
Usage: mapcv plan [OPTIONS] {config_path}
Estimate tiles, patches, disk and memory for a config without downloading.
╭─ Arguments ──────────────────────────────────────────────────────────────────────────╮
│ * config_path <path> Path to the YAML config. [required] │
╰──────────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────────────╮
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────────────╯
Example: mapcv plan mapcv.yaml

Loads the config, parses the label file and estimates the run without downloading imagery. A file the config reads that does not exist (labels.path, region.path, a local imagery.path, …) stops it with exit code 1 and the key that names it; mapcv validate only warns about missing files. It uses the network only to read the header of a remote (https://, s3://) GeoTIFF or label raster, and to fetch labels.osm from Overpass. The panel shows the task (unless it is segmentation), the region size in km, ground resolution, raster size, tile count and download size (XYZ), label polygons (or, for a label raster, its CRS, size and pixel size, read from the header only) and the class map, the number of objects (detection and instance), patch count, output size, split settings and memory per chunk. Warnings follow the panel: skipped label features, a missing label file, labels (or a label raster) that don’t intersect the region, no patch fitting the region, a patch_size larger than the raster, or very many small files. When the plan holds no patch, or lists a problem that stops generate (a grid of more than 3,000,000 patches, sources that cannot be stacked), plan (like generate) ends with exit code 1 and no Looks right? line. The patch count is computed from the raster’s size, so plan answers at once and uses little memory however fine the stride or large the region.

Estimates use typical tile and file sizes. For Sentinel-2 the raster size is computed on the UTM grid of the region (with the zarr extra installed); the product’s exact grid is known only once it is opened, so the real size can differ by a few pixels.

mapcv generate --help
Usage: mapcv generate [OPTIONS] {config_path}
Download imagery, rasterize labels and write patches, masks, manifest and splits.
Re-running the same command resumes an interrupted run.
╭─ Arguments ──────────────────────────────────────────────────────────────────────────╮
│ * config_path <path> Path to the YAML config. [required] │
╰──────────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────────────╮
│ --yes -y Don't ask before large downloads. │
│ --dry-run Only show the plan, like mapcv plan. │
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────────────╯
Examples:
mapcv generate mapcv.yaml
mapcv generate mapcv.yaml --yes (no prompt for large jobs, for CI)
  1. Prints the same plan as mapcv plan (and, like it, stops with exit code 1 when a file the config reads is missing or the plan holds no patch, or the grid is over the limit of 3,000,000 patches). With --dry-run it stops here.
  2. Large jobs — more than 20,000 tiles, more than 5 GB of estimated download plus output, or more than 1,000,000 patches — ask This is a large job. Start it? (default no; answering no exits with code 1). Without a terminal, generation refuses with exit code 2 unless you pass --yes.
  3. Opens the imagery (with an “Opening imagery…” spinner), reprojects labels, checks that an existing manifest.json in writer.staging_dir is compatible, then processes chunks with a progress bar (plain lines when the output is not a terminal; in a terminal narrower than 110 columns the bar drops its labels, then its ETA, then the bar itself, so no column is cut off), saving the manifest every few seconds and whenever the run stops (Ctrl-C, an error).
  4. Writes split lists if the config has a split block, then prints Dataset ready: patch count (and how many are new this run), shape and dtype, tiles fetched and failed, split sizes, elapsed time, the files and folders in the dataset (patch folders, manifest.json, splits/, patches.geojson, and the COCO, YOLO or label-table files of the task), the class balance, and next steps.

Warnings (skipped label features, labels outside the imagery, split leakage, more than half of the tiles failing) are printed once each, as ⚠ lines. Failures are reported as one Generation failed: … message without a traceback, followed by a hint to re-run; a refused resume is reported as Cannot resume: … instead. A folder that can’t be written and a full disk say so, with what to do:

Generation failed: permission denied: /data/ro/dataset
Make that folder writable, or set writer.staging_dir to a folder you can write to. Then run the same command again: finished chunks are kept and the run resumes.

Ctrl-C stops with Interrupted. Finished chunks are saved; run the same command again to resume. and exit code 130.

Resuming: run the same command again. Patches already in the manifest are skipped, and XYZ tiles are only fetched for chunks still to do. When everything is already written it prints Nothing left to do: all 77 patches are already written. Resuming refuses a folder made with different imagery, labels (including edits to the label file), sampler or writer settings, by mapcv 0.1, by mapcv 0.2 with sampler.mode: random, or by a newer mapcv; a 0.2 dataset resumes only with labels.ignore_index: null (see Large regions and resuming). A run with nothing new to do leaves the dataset as it is, split lists included, even if mapcv split changed them since; it warns when they differ from the config’s split settings. A run that writes no patch at all (every patch was dropped by sampler.max_empty_ratio or min_label_ratio) ends with No patches were written. and exit code 1, and leaves no manifest.json, split lists or patches.geojson: an empty dataset is no dataset, so verify, stats, split and export say that there is none instead of succeeding on nothing.

Terminal window
$ mapcv generate mapcv.yaml
$ mapcv generate mapcv.yaml --dry-run
$ mapcv -q generate mapcv.yaml --yes # CI: no prompts, no progress bars
mapcv info --help
Usage: mapcv info [OPTIONS] {staging_dir}
Summarize a generated dataset: source, shapes, class balance and splits.
╭─ Arguments ──────────────────────────────────────────────────────────────────────────╮
│ * staging_dir <path> Dataset directory containing manifest.json. [required] │
╰──────────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────────────╮
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────────────╯
Example: mapcv info dataset/

Reads manifest.json (version 1, 2 or 3) and splits/ and prints the task, the source and product, bands, patch count and shape, CRS, the mask’s ignore value, how many patches are padded, split sizes, manifest version and per-class pixel shares. An older manifest is shown with its version, for example version 2 (mapcv 0.2; read as version 3), and is not modified.

Terminal window
$ mapcv info dataset
╭─ dataset ────────────────────────────────────────────────────────────────────────────╮
│ Task segmentation │
│ Source xyz · esri_satellite │
│ Bands red, green, blue │
│ Patches 77 · 256×256×3 uint8 │
│ CRS EPSG:3857 │
│ Ignore mask value 255 marks pixels without imagery │
│ Splits train 52 (68%) · val 8 (10%) · test 17 (22%) │
│ Manifest version 3 │
╰──────────────────────────────────────────────────────────────────────────────────────╯
class id pixels
background 0 81.0%
building 1 19.0%
mapcv split --help
Usage: mapcv split [OPTIONS] {staging_dir}
Re-split an existing dataset from its manifest; no images are read.
╭─ Arguments ──────────────────────────────────────────────────────────────────────────╮
│ * staging_dir <path> Dataset directory containing manifest.json. [required] │
╰──────────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────────────╮
│ --test-ratio <float> Fraction of patches held out for testing. │
│ [default: 0.2] │
│ --val-ratio <float> Fraction of the remaining patches for validation. │
│ [default: 0.1] │
│ --labeled-ratios <float> Labeled fractions of train for semi-supervised │
│ lists (repeatable). Default: 0.1 0.2 0.3. │
│ --seed <int> Random seed. [default: 42] │
│ --strategy <str> spatial (leakage-safe blocks) | stratified | │
│ random | region (whole regions of an area of │
│ interest). │
│ [default: spatial] │
│ --block-size <int> Spatial block size in pixels (default: 4 × patch │
│ size). │
│ --sample-limit <int> Split only a sample of this many patches (a small │
│ benchmark from a large dataset); the other │
│ patches are in no split. │
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────────────╯
Examples:
mapcv split dataset/
mapcv split dataset/ --strategy spatial --block-size 2048 --test-ratio 0.15

Rewrites STAGING_DIR/splits/ (including split.json, which records the settings and counts) from the manifest only, and with it the files that depend on the split: patches.geojson, the COCO files, YOLO lists and dataset.yaml (detection, instance) and the label tables (classification); images are never opened, so it takes seconds even for large datasets. Options mirror the split config block; the split settings in your YAML are not read.

Option Default Notes
--test-ratio 0.2 Fraction of all patches for test
--val-ratio 0.1 Fraction of the rest for validation
--labeled-ratios 0.1 0.2 0.3 Repeat the option for several values: --labeled-ratios 0.05 --labeled-ratios 0.25
--seed 42
--strategy spatial spatial, stratified, random or region (a dataset made with region.path)
--block-size auto Pixels; default 4 × patch size, smaller on small rasters
--sample-limit none Split a subsample of this many patches, for a small benchmark from a large dataset. The other patches are in no split; the command says how many
Terminal window
$ mapcv split dataset --block-size 512 --labeled-ratios 0.05 --labeled-ratios 0.25
✓ Splits written to dataset/splits: train 52 (68%) · val 8 (10%) · test 17 (22%)
$ mapcv split overlap --strategy random
⚠ Patches overlap or repeat, so a 'random' split leaks pixels between train and test;
use strategy 'spatial'.
✓ Splits written to overlap/splits: train 196 (72%) · val 22 (8%) · test 55 (20%)

Old splits/<percent>/ folders for ratios you no longer request are left in place. An option value that is out of range is a usage error (exit code 2), named after the option:

Terminal window
$ mapcv split dataset --test-ratio 2
Usage: mapcv split [OPTIONS] {STAGING_DIR}
Try 'mapcv split -h' for help.
╭─ Error ──────────────────────────────────────────────────────────────────────╮
│ Invalid value for '--test-ratio': Input should be less than or equal to 1 │
╰──────────────────────────────────────────────────────────────────────────────╯
mapcv stats --help
Usage: mapcv stats [OPTIONS] {staging_dir}
Per-band mean and std, class balance and class weights, written to stats.json.
╭─ Arguments ──────────────────────────────────────────────────────────────────────────╮
│ * staging_dir <path> Dataset directory containing manifest.json. [required] │
╰──────────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────────────╮
│ --split <str> train, val, test or all: the patches to count. │
│ [default: train] │
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────────────╯
Examples:
mapcv stats dataset/ train split (all patches without splits)
mapcv stats dataset/ --split all

Reads every patch of a split and writes STAGING_DIR/stats.json:

  • Per source and band: mean, std (population), min, max and pixels (how many were counted). Feed mean and std to your normalisation, as TerraTorch’s means/stds or torchvision.transforms.Normalize.
  • Class balance: pixel counts, frequencies and median-frequency class weights (Eigen & Fergus: the median of the class frequencies divided by each class’s frequency, where a class’s frequency is its pixels over the pixels of the patches it appears in) for segmentation and change; object counts per class for detection and instance; patch counts per label for classification.
  • Targets (regression): mean, std, min and max of the target values.

Only the train split is counted by default, so validation and test pixels don’t leak into the normalisation; --split all counts every patch, and a dataset without split lists is always counted whole. A pixel counts when the mask doesn’t mark it as ignored (ignore_index: no imagery or no label); without a mask (image-only datasets), when it is finite and not the source’s NoData value on every band (all zero for XYZ tiles, where failed tiles are black). The statistics are accumulated per band in float64 with Welford/Chan merging, so they stay exact over billions of pixels.

Terminal window
$ mapcv stats dataset
source band mean std min max
image b1 124.991 71.8234 1 249
image b2 124.604 71.8503 1 249
image b3 125.002 71.7828 1 249
Class weights (median frequency): background 0.769, a 1.43
✓ 36 patches of the train split → dataset/stats.json

Counting an unfinished dataset (generate stopped before it finished: its manifest’s complete is false) prints a ⚠ line saying so, since the numbers cover only the patches written so far; run mapcv generate again with the dataset’s config to finish it first.

stats.json is written on request, not by generate: reading every patch back costs about as long as writing it, and generate’s output stays the same as before. Run it again after mapcv split.

mapcv card --help
Usage: mapcv card [OPTIONS] {staging_dir}
Write a dataset card (README.md with Hugging Face metadata) for sharing.
╭─ Arguments ──────────────────────────────────────────────────────────────────────────╮
│ * staging_dir <path> Dataset directory containing manifest.json. [required] │
╰──────────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────────────╮
│ --force Replace an existing README.md. │
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────────────╯
Example: mapcv card dataset/

Writes STAGING_DIR/README.md, a dataset card with Hugging Face dataset-card YAML metadata (license, task_categories, tags, size_categories), so the folder can be pushed to the Hub or archived as is. The card describes the imagery (type, product, CRS, ground sampling distance, bands and data type), the classes, the split sizes, the normalisation values (when stats.json exists, so run mapcv stats first), the files, the provenance (mapcv and manifest version, label file SHA-256, imagery fingerprints) and the provider’s credit line for built-in sources.

The licence is other until you change it: mapcv can’t know what the imagery provider’s terms allow (see Providers & licensing). An existing README.md is kept unless you pass --force. A dataset that generate did not finish gets a ⚠ line, because the card describes only the patches written so far.

mapcv verify --help
Usage: mapcv verify [OPTIONS] {STAGING_DIR}
Check that every file is present and intact (and SHA256SUMS, when there is one).
╭─ Arguments ──────────────────────────────────────────────────────────────────────────╮
│ * STAGING_DIR <path> Dataset directory containing manifest.json. [required] │
╰──────────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────────────╮
│ --deep Also decode every image and mask and check them against │
│ the manifest. │
│ --write-checksums Write SHA256SUMS after a successful check. │
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────────────╯
Examples:
mapcv verify dataset/
mapcv verify dataset/ --deep --write-checksums

Checks a dataset against its manifest: generate finished it (the manifest’s complete; an interrupted dataset fails with the advice to run mapcv generate again), every listed file exists, is not empty and is not cut short (a PNG ends with its end chunk, a JPEG with its end marker, an NPY file holds the bytes its header promises, a TIFF starts with a TIFF header), and the split lists name only patches of the manifest, share none and match splits/split.json (which an interrupted mapcv split leaves out). --deep also decodes every image and compares its shape with the source’s patch_shape, and decodes every mask: its size must be the patch size and its pixel count per class the one the manifest records, so a mask with an unknown value or another class is found. Files in the patch folders that the manifest doesn’t list (left by an interrupted run, or added by hand) are reported as a note, not a failure.

--write-checksums writes SHA256SUMS (the sha256sum format) over every file in the dataset folder (patches, manifest.json, splits/, annotations and label tables, patches.geojson, …), after a successful check, or when the only differences are files that mapcv split, stats and card rewrite (it says so and records their new hashes). A missing or changed patch still blocks it. When SHA256SUMS exists, verify checks every hash, so a copied, uploaded or downloaded dataset can be proven complete and unchanged; sha256sum -c SHA256SUMS in the folder checks the same thing without mapcv.

Terminal window
$ mapcv verify dataset --write-checksums
✓ 48 patches, 96 files present.
✓ SHA256SUMS written to dataset/SHA256SUMS
$ mapcv verify copy-of-dataset
✗ Masks/patch_0000012.png does not match its SHA256SUMS hash
1 problem found.
Copy the dataset again from where it came from, or generate it into a new folder.

Exit code 1 when anything is missing, empty, cut short, unreadable (--deep) or changed. Files the manifest doesn’t list are ⚠ lines and don’t fail the check.

mapcv split, mapcv stats and mapcv card rewrite files that SHA256SUMS covers (the split lists and the outputs that follow them, stats.json, README.md). verify names such a file as changed since SHA256SUMS was written (mapcv split, stats and card rewrite it); when they are the only changes, run mapcv verify --write-checksums to record the new hashes instead of copying the dataset again.

mapcv export --help
Usage: mapcv export [OPTIONS] {staging_dir}
Export a dataset: Hugging Face Parquet, WebDataset shards, Zarr or a TerraTorch
config.
╭─ Arguments ──────────────────────────────────────────────────────────────────────────╮
│ * staging_dir <path> Dataset directory containing manifest.json. [required] │
╰──────────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────────────╮
│ * --format -f <str> hf-parquet (Hugging Face), webdataset │
│ (tar shards), zarr (one store) or │
│ terratorch (a data config). │
│ [required] │
│ --out -o <path> hf-parquet, webdataset, zarr: a new │
│ folder (required). terratorch: the YAML │
│ file (default │
│ STAGING_DIR/terratorch.yaml). │
│ --shard-mb <int range> [x>=1] webdataset: largest shard size in MB. │
│ [default: 1000] │
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────────────╯
Examples:
mapcv export dataset/ --format hf-parquet --out dataset-hf/
mapcv export dataset/ --format terratorch writes dataset/terratorch.yaml

Writes a dataset in the form another tool reads; the dataset itself is not changed. A dataset that generate did not finish (its manifest’s complete is false) is refused with the advice to run mapcv generate again with its config: an export would leave out patches and split members. (Manifests from mapcv 0.2 and earlier have no such flag and are exported.)

OUT of hf-parquet, webdataset and zarr must be new or empty, or hold an earlier export of the same format, which is replaced whole (no split file or shard of it is left next to the new ones); a folder with other files is refused, and an export that fails part way removes what it wrote.

  • hf-parquet (needs pip install "mapcv[export]"): OUT/data/<split>-00000-of-00001.parquet for each split (all without split lists) and OUT/README.md, a dataset card whose metadata names the split files, so datasets.load_dataset("OUT") and the Hugging Face Hub read it as is. Each patch file is a {bytes, path} column holding the file exactly as written (PNG and JPEG columns are typed as images; NPY and GeoTIFF columns hold the bytes, for np.load(io.BytesIO(...)) or rasterio’s MemoryFile), next to name, row, col, crs, transform and, for classification, labels.
  • webdataset: OUT/<split>-000000.tar, … of at most --shard-mb MB (default 1000), and OUT/shards.json listing each split’s shards and sample counts. A sample is one patch: <name>.png (or .jpg, .npy, .tif: the image file as written), <name>.mask.<ext>, <name>.<source>.<ext> for further sources, and <name>.json (row, col, transform, crs, split, the manifest summary, and for detection and instance segmentation the COCO annotations). Samples follow the split lists’ order; the tars are byte-for-byte reproducible. Stream them with the webdataset library without unpacking.
  • zarr (the zarr extra): OUT is a Zarr group with images/<source> (N, C, H, W), masks (N, H, W), split (0 train, 1 val, 2 test, 255 none), row and col, one patch per chunk, plus copies of manifest.json, splits/ and the COCO files. MapcvDataset reads it like the dataset folder: MapcvDataset("dataset.zarr", split="train"). The store is in the zarr 2 format and needs zarr 2.x (what the extra installs); with zarr 3 the export stops with a message before writing anything.

All three pack the dataset into a few files for copying, object storage and fast reading; the dataset folder is unchanged and every other command keeps working on it. To change splits, run mapcv split on the folder and export again.

  • terratorch: the data: section of a TerraTorch training config, GenericNonGeoSegmentationDataModule (or GenericNonGeoPixelwiseRegressionDataModule) with the dataset’s Images/ and Masks/ folders, its split lists, num_classes, and the band means and stds of the train split (from stats.json, or computed). It covers single-source segmentation and regression datasets written as tif or png (TerraTorch reads files with GDAL, which cannot read NPY). A comment says which ignore_index the task needs: -1 for GeoTIFF masks, whose ignore value TerraTorch reads as missing, and 255 for PNG masks.
Terminal window
$ mapcv export dataset --format terratorch
✓ TerraTorch data config written to dataset/terratorch.yaml; paste it into your training config.
mapcv validate --help
Usage: mapcv validate [OPTIONS] {config_path}
Check a config without reading labels or imagery (use plan for estimates).
╭─ Arguments ──────────────────────────────────────────────────────────────────────────╮
│ * config_path <path> Path to the YAML config. [required] │
╰──────────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────────────╮
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────────────╯
Example: mapcv validate mapcv.yaml

Parses the YAML and runs every field check, prints the resolved settings, and warns about a local file the config reads that does not exist (labels.path, a local imagery.path, …). A path that names a folder, a named pipe or a device instead of an ordinary file stops it with exit code 1, as plan and generate do. It reads neither the label file nor any imagery; use mapcv plan for that.

Terminal window
$ mapcv validate bad.yaml
Config error in bad.yaml:
• region: region.west must be less than region.east
• imagery.source: the built-in 'google_satellite' source was removed in mapcv 0.2.0: Google
does not permit downloading its imagery for datasets (see MIGRATION.md and PROVIDERS.md)
• labels.path: labels.path must be a .geojson, .json, .kml, .gpkg, .shp, .parquet or
.geoparquet file, got 'buildings.kmz' (a KMZ is a zipped KML: unzip it and use the .kml inside)
Fix the fields above, or start from a working config with mapcv init.
$ mapcv validate unknown.yaml
Config error in unknown.yaml:
• imagery: imagery: set 'source' or 'url_template', not both
• sampler.stide: Extra inputs are not permitted
Fix the fields above, or start from a working config with mapcv init.
$ mapcv validate broken.yaml
Config error in broken.yaml: not valid YAML at line 4, column 3 (expected <block end>, but found
'-').
Check the indentation, colons and brackets there.
mapcv cache --help
Usage: mapcv cache [OPTIONS]
Show or clear the on-disk cache of downloaded XYZ tiles.
╭─ Options ────────────────────────────────────────────────────────────────────────────╮
│ --clear Delete the cached tiles. │
│ --expired With --clear: delete only the tiles that have expired. │
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────────────╯
Examples:
mapcv cache where it is and how big
mapcv cache --clear --expired delete only expired tiles

Shows where the tile cache is, how many tiles it holds, their size and how many have expired. --clear deletes every cached tile; --clear --expired deletes only the expired ones. Only .tile files mapcv wrote are touched.

Terminal window
$ mapcv cache
Tile cache: /home/me/.cache/mapcv/tiles
1,284 tiles, 31.6 MB, 96 expired
Set MAPCV_CACHE_DIR to move it, or imagery.cache: false to skip it.
$ mapcv cache --clear --expired
✓ Deleted 96 expired cached tiles from /home/me/.cache/mapcv/tiles
mapcv mcp --help
Usage: mapcv mcp [OPTIONS]
Run an MCP server over stdio so AI agents can build datasets with mapcv.
Needs pip install "mapcv[mcp]". Without --allow-write the server can only read,
validate and plan.
╭─ Options ────────────────────────────────────────────────────────────────────────────╮
│ --root <path> The only folder the server may read or write │
│ (default: the current folder). │
│ [default: .] │
│ --allow-write Also offer the tools that write: write_config, │
│ generate and split. │
│ [env var: MAPCV_MCP_ALLOW_WRITE] │
│ --allow-local-urls Let the tools connect to this machine and │
│ private networks (a local tile or GeoTIFF │
│ server). Without it they reach public addresses │
│ only. │
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────────────╯
Examples:
mapcv mcp read-only, in this folder
mapcv mcp --root ~/work --allow-write may write datasets under ~/work
mapcv mcp --allow-local-urls may also read a tile or GeoTIFF server on this
machine or a private network

Runs an MCP server over stdio so an AI agent can inspect labels, validate a config, plan, generate and inspect a dataset. It needs the mcp extra (pip install "mapcv[mcp]"); without it the command prints the install command and exits with code 1.

  • Read-only by default. Without --allow-write the tools write_config, generate and split do not exist on the server; with it they are offered (and stats and verify may write stats.json and SHA256SUMS). The environment variable MAPCV_MCP_ALLOW_WRITE=true does the same, for clients that configure servers through the environment.
  • --root is the only folder the server reads or writes (default: the current folder). Paths outside it, including the ones a config resolves to, are refused.
  • Public addresses only by default. The tools do not connect to this machine or a private network: a URL in a config that names one (localhost, 127.0.0.1, 10.x, 169.254.169.254, …), a host name that resolves to one, and a redirect to one are refused with the same message, whatever is listening there. --allow-local-urls lifts this, for a tile or GeoTIFF server you run yourself.
  • Progress bars are silenced, because stdout carries the protocol; logs go to stderr.

Install snippets for Claude Code, Claude Desktop, Cursor and VS Code, the tool list and the safety model are in Use mapcv with AI agents.

mapcv doctor --help
Usage: mapcv doctor [OPTIONS]
Diagnose the installation, extras, tile cache and network.
Takes a few seconds, for you and for bug reports. Changes nothing (it writes and
deletes one small probe file in the tile cache folder) and prints no secrets. Exits
with 1 if a check fails, such as a broken Rust extension or a tile cache folder that
can't be written; a warning does not fail the run.
╭─ Options ────────────────────────────────────────────────────────────────────────────╮
│ --json Print the checks as JSON (no colour), for bug reports and │
│ scripts. │
│ --offline Skip the network checks (no request leaves this machine). │
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────────────╯
Examples:
mapcv doctor check this installation
mapcv doctor --offline skip the network checks
mapcv doctor --json for bug reports and scripts

Diagnoses an installation in a few seconds. Run it first when something does not work, and paste its output into a bug report. It reads, and changes nothing but one small probe file in the tile cache folder, which it deletes at once. It never prints a secret: a credentials file is looked for but never opened, and a proxy is named but its value is not shown.

Section What it checks
mapcv mapcv version, Python version and implementation, the interpreter, the operating system and CPU (with the C library on Linux), and where mapcv is installed
Core The Rust extension loads and is the same version as the Python package; the version of each core dependency
Extras Whether zarr (Sentinel-2 EOPF), gee (Earth Engine, and whether credentials exist), parquet / export (pyarrow; the hint names mapcv[parquet] for GeoParquet labels and mapcv[export] for the Hugging Face export) and mcp (must be 2.x) are installed. A missing extra is info with the pip install "mapcv[…]" command, not a problem
Tile cache The cache folder (see MAPCV_CACHE_DIR), whether it can be written, and how many tiles it holds
Terminal Output encoding, whether output is a terminal, colour (NO_COLOR, --no-color) and width
Network Whether the Esri World Imagery tile service, the EOPF STAC catalog, the Earth Search STAC catalog and (with earthengine-api installed) Earth Engine answer, with the HTTP status and the time they took; and which proxy variables are set. Each request times out after 5 seconds and they run at the same time

Each line is ok, warn, FAIL or info, with a hint under it saying what to do next. The word is printed, so the report reads the same without colour. An unreachable service is a warn, not a failure: you may not use that provider. The command exits with 1 if a check fails (the Rust extension is missing or the wrong version, a core dependency is missing, or the tile cache folder can’t be written), and with 0 otherwise, warnings included.

Option Meaning
--offline Skip the network checks. No request leaves your machine
--json Print the checks as JSON instead of the table, with no colour and \n line endings, for bug reports and scripts

--json prints one object with ok (false if any check failed), offline and checks, a list of records with the keys section, name, status (ok, warn, fail or info), detail and hint (null when there is nothing to do).

Terminal window
$ mapcv doctor
mapcv
ok mapcv 0.3.0
ok Python 3.12.13 (CPython)
info Interpreter ~/mapcv-env/bin/python3
info System Linux 7.0.0-34-generic x86_64 (glibc 2.43)
info Location ~/mapcv-env/lib/python3.12/site-packages/mapcv
Core
ok Rust extension loaded, version 0.3.0
ok pillow 12.3.0
ok numpy 2.4.4
ok shapely 2.1.2
ok pydantic 2.13.5
ok typer 0.27.2
ok rich 15.0.0
ok pyyaml 6.0.3
ok typing-extensions 4.16.0
ok pyproj 3.7.2
ok pyshp 3.1.6
Extras
ok zarr xarray-eopf 0.2.9, zarr 2.18.7
ok gee earthengine-api 1.7.47
ok Earth Engine credentials found:
~/.config/gcloud/application_default_credentials
.json
ok parquet / export pyarrow 25.0.1
ok mcp mcp 2.3.0
Tile cache
ok Folder ~/.cache/mapcv/tiles, writable
info Contents 8 tiles, 136.1 KB
Terminal
ok Encoding utf-8
info Output not a terminal (piped or redirected)
info Colour off (NO_COLOR is set)
info Width 80 columns
Network
info Proxy none
ok Esri World Imagery tiles HTTP 200 in 1,459 ms
ok EOPF STAC catalog HTTP 200 in 682 ms
ok Earth Search STAC HTTP 200 in 1,141 ms
ok Earth Engine HTTP 404 in 645 ms (it answered)
All checks passed.
Paste this output, or the output of mapcv doctor --json, into a bug report.

Paths under your home folder are shown with ~. The example above comes from a real run, with the two install paths shortened.

Code When
0 Success (also generate --dry-run, and init when the written file still needs edits)
1 Config not found or invalid, a file the config reads is missing (plan, generate), generation failed or wrote no patch, resume refused, dataset or manifest not found, a file that can’t be read or written, file exists without --force, mapcv verify found a problem, mapcv doctor found a failed check, a declined large-job prompt, or the input ended during mapcv init’s questions
2 Invalid command-line usage: an unknown command or option, a missing argument, or an option value that is not allowed (stats --split nope, export --format coco, export without the --out its format needs, cache --expired without --clear, a split ratio out of range, mcp --root that is not a folder). Also a large job without a terminal and without --yes. mapcv with no command prints the help and exits with 2 too
130 Interrupted with Ctrl-C (any command; generate keeps the finished chunks)

Errors are printed on stderr, everything else on stdout.