Skip to content
Merged

V28 #35

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
3 changes: 2 additions & 1 deletion docs/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,8 @@ Support Policy
- **0.1.11**: End of life

### Python Support
- **Python 3.9+**: Fully supported
- **Python 3.10+**: Fully supported
- **Python 3.9**: End of life
- **Python 3.8**: End of life
- **Python 3.7**: End of life

Expand Down
26 changes: 26 additions & 0 deletions docs/source/cli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,32 @@ Basic Usage

phidown [OPTIONS]
phidown list [OPTIONS]
phidown skill add|remove [OPTIONS]

Local Agent Skills
------------------

Install the bundled phidown guidance into local agent tooling:

.. code-block:: bash

phidown skill add # Codex, default
phidown skill add --engine claude # Claude Code personal skill
phidown skill add --engine cursor # Cursor project rule
phidown skill add --engine all

Remove the matching local files:

.. code-block:: bash

phidown skill remove --engine all

Targets:

* Codex: ``$CODEX_HOME/skills/phidown`` or ``~/.codex/skills/phidown``
* Claude Code: ``~/.claude/skills/phidown``
* Cursor: ``.cursor/rules/phidown.mdc`` in the current project, or in
``--cursor-project-dir``

Download Commands
-----------------
Expand Down
2 changes: 1 addition & 1 deletion docs/source/getting_started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Prerequisites

Before you begin, make sure you have:

1. Python 3.9 or newer
1. Python 3.10 or newer
2. A Copernicus Data Space account: `<https://dataspace.copernicus.eu/>`_
3. S3 credentials from the `S3 Key Manager <https://eodata-s3keysmanager.dataspace.copernicus.eu/panel/s3-credentials>`_
4. A PhiSat-2 INSULA account if you plan to use ``--provider phisat2``
Expand Down
2 changes: 1 addition & 1 deletion docs/source/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Requirements

Phi-Down requires:

* Python 3.9 or newer
* Python 3.10 or newer
* ``s5cmd`` on your ``PATH`` for S3 downloads
* Copernicus Data Space credentials for authenticated downloads
* PhiSat-2 INSULA credentials if you plan to use ``--provider phisat2``
Expand Down
114 changes: 114 additions & 0 deletions main.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd -P)"

if [[ -n "${CONDA_BASE:-}" && -n "${ENV_PREFIX:-}" && -f "${CONDA_BASE}/bin/activate" ]]; then
# The Makefile activates the env before calling this script; this keeps direct
# script invocation and PBS execution deterministic too.
source "${CONDA_BASE}/bin/activate"
conda activate "${ENV_PREFIX}"
fi

BASE_DIR="${BASE_DIR:-${PROJECT_ROOT}}"
DATA_DIR="${DATA_DIR:-${BASE_DIR}/input_data}"
PY_SCRIPT_DIR="${PY_SCRIPT_DIR:-${BASE_DIR}/sarpyx/pyscripts}"
OUTPUT_PATH="${OUTPUT_PATH:-${OUTPUT_DIR:-${BASE_DIR}/OUT/worldsar_output}}"
OUTPUT_DIR="${OUTPUT_DIR:-${OUTPUT_PATH}}"
CUTS_OUTDIR="${CUTS_OUTDIR:-${TILES_DIR:-${BASE_DIR}/OUT/tiles}}"
TILES_DIR="${TILES_DIR:-${CUTS_OUTDIR}}"
DB_DIR="${DB_DIR:-${BASE_DIR}/OUT/DB}"
ENV_DIR="${ENV_DIR:-${BASE_DIR}/envs/worldsar-py313}"
SNAP_USER_DIR="${SNAP_USER_DIR:-${ENV_DIR}/opt/.snap}"
GRID_PATH="${GRID_PATH:-${BASE_DIR}/grid/grid_10km.geojson}"
GPT_MEMORY="${GPT_MEMORY:-64G}"
GPT_PARALLELISM="${GPT_PARALLELISM:-16}"
GPT_TIMEOUT="${GPT_TIMEOUT:-3600}"
SENTINEL_SUBAPS="${SENTINEL_SUBAPS:-2}"

PRODUCT_INPUT="${1:-${PRODUCT:-${WORLDSAR_PRODUCT:-}}}"
if [[ -z "${PRODUCT_INPUT}" ]]; then
echo "ERROR: Product name is required." >&2
echo "Usage: ${0##*/} <product_name>" >&2
echo "Or set PRODUCT=<product_name_or_path>" >&2
exit 2
fi

if [[ "${PRODUCT_INPUT}" == */* ]]; then
PROD_PATH="${PRODUCT_INPUT}"
PRODUCT_FROM_DATA_DIR=0
else
PROD_PATH="${DATA_DIR}/${PRODUCT_INPUT}"
PRODUCT_FROM_DATA_DIR=1
fi

if [[ -z "${GPT_PATH:-}" ]]; then
GPT_PATH="$(command -v gpt || true)"
fi

[[ -d "${BASE_DIR}" ]] || { echo "ERROR: BASE_DIR not found: ${BASE_DIR}" >&2; exit 2; }
if [[ "${PRODUCT_FROM_DATA_DIR}" -eq 1 ]]; then
[[ -d "${DATA_DIR}" ]] || { echo "ERROR: DATA_DIR not found: ${DATA_DIR}" >&2; exit 2; }
fi
[[ -d "${PY_SCRIPT_DIR}" ]] || { echo "ERROR: PY_SCRIPT_DIR not found: ${PY_SCRIPT_DIR}" >&2; exit 2; }
[[ -e "${PROD_PATH}" ]] || { echo "ERROR: Product not found: ${PROD_PATH}" >&2; exit 2; }
[[ -d "${SNAP_USER_DIR}" ]] || { echo "ERROR: SNAP_USER_DIR not found: ${SNAP_USER_DIR}" >&2; exit 2; }
[[ -f "${GRID_PATH}" ]] || { echo "ERROR: GRID_PATH not found: ${GRID_PATH}" >&2; exit 2; }
[[ -n "${GPT_PATH}" ]] || { echo "ERROR: gpt not found in PATH and GPT_PATH is unset." >&2; exit 2; }
if [[ "${GPT_PATH}" == */* ]]; then
[[ -x "${GPT_PATH}" ]] || { echo "ERROR: GPT_PATH is not executable: ${GPT_PATH}" >&2; exit 2; }
else
command -v "${GPT_PATH}" >/dev/null 2>&1 || { echo "ERROR: GPT command not found: ${GPT_PATH}" >&2; exit 2; }
fi

if command -v sarpyx >/dev/null 2>&1; then
SARPYX_CMD=(sarpyx)
else
SARPYX_CMD=(python -m sarpyx.cli.worldsar)
fi

read -r -a SENTINEL_SUBAP_ARGS <<< "${SENTINEL_SUBAPS}"
for subap in "${SENTINEL_SUBAP_ARGS[@]}"; do
[[ "${subap}" =~ ^[0-9]+$ ]] || { echo "ERROR: SENTINEL_SUBAPS must contain integers: ${SENTINEL_SUBAPS}" >&2; exit 2; }
[[ "${subap}" -ge 2 ]] || { echo "ERROR: SENTINEL_SUBAPS values must be >= 2: ${SENTINEL_SUBAPS}" >&2; exit 2; }
done

mkdir -p "${OUTPUT_PATH}" "${CUTS_OUTDIR}" "${DB_DIR}"
export SNAP_USERDIR="${SNAP_USER_DIR}"

args=(
--input "${PROD_PATH}"
--output "${OUTPUT_PATH}"
--cuts-outdir "${CUTS_OUTDIR}"
--gpt-path "${GPT_PATH}"
--grid-path "${GRID_PATH}"
--db-dir "${DB_DIR}"
--gpt-memory "${GPT_MEMORY}"
--gpt-parallelism "${GPT_PARALLELISM}"
--gpt-timeout "${GPT_TIMEOUT}"
--snap-userdir "${SNAP_USER_DIR}"
--sentinel-subaps "${SENTINEL_SUBAP_ARGS[@]}"
)

[[ -n "${ORBIT_TYPE:-}" ]] && args+=(--orbit-type "${ORBIT_TYPE}")
[[ "${ORBIT_CONTINUE_ON_FAIL:-0}" == "1" ]] && args+=(--orbit-continue-on-fail)
[[ -n "${SENTINEL_SWATH:-}" ]] && args+=(--sentinel-swath "${SENTINEL_SWATH}")
[[ -n "${SENTINEL_FIRST_BURST:-}" ]] && args+=(--sentinel-first-burst "${SENTINEL_FIRST_BURST}")
[[ -n "${SENTINEL_LAST_BURST:-}" ]] && args+=(--sentinel-last-burst "${SENTINEL_LAST_BURST}")
[[ -n "${SENTINEL_TC_SOURCE_BAND:-}" ]] && args+=(--sentinel-tc-source-band "${SENTINEL_TC_SOURCE_BAND}")
[[ "${SKIP_PREPROCESSING:-0}" == "1" ]] && args+=(--skip-preprocessing)

echo "WORLDSAR_MODE=${WORLDSAR_MODE:-}"
echo "BASE_DIR=${BASE_DIR}"
echo "DATA_DIR=${DATA_DIR}"
echo "PROD_PATH=${PROD_PATH}"
echo "OUTPUT_PATH=${OUTPUT_PATH}"
echo "CUTS_OUTDIR=${CUTS_OUTDIR}"
echo "DB_DIR=${DB_DIR}"
echo "SNAP_USER_DIR=${SNAP_USER_DIR}"
echo "GRID_PATH=${GRID_PATH}"
echo "GPT_PATH=${GPT_PATH}"
echo "SARPYX_CMD=${SARPYX_CMD[*]}"

exec "${SARPYX_CMD[@]}" "${args[@]}"
Loading
Loading