Metadata-Version: 2.4
Name: haloviewer
Version: 0.2.0
Summary: Viewer, plotting and synchronisation tools for Halo Photonics wind lidar data
Author-email: Clemens Drüe <druee@uni-trier.de>
License: EUPL-1.2
Classifier: License :: OSI Approved :: European Union Public Licence 1.2 (EUPL 1.2)
Classifier: Programming Language :: Python :: 3
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: LICENSE-EUPL-1.2.txt
Requires-Dist: numpy
Requires-Dist: pandas
Requires-Dist: matplotlib
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Provides-Extra: docs
Requires-Dist: sphinx>=7; extra == "docs"
Dynamic: license-file

# HaloViewer

A viewer and plotting toolkit for Halo Photonics wind lidar data
(`.hpl` files) covering the **processed-product** tree
(`Proc/YYYY/YYYYMM/YYYYMMDD/*.hpl`). Every kind found there is
plottable: the instrument-processed **Processed Wind Profile** (a
single scan's height/speed/direction profile, or many combined into a
height/time History image), and the raw regular-scan kinds --
**VAD**, **Stare**, **Wind Profile** and **RHI** -- each of which gets
a distance/time intensity+beta History image built from their raw
per-gate data (no instrument-processed profile of their own). **RHI**
additionally gets its own **Profile** mode: a single scan's
distance/height cross section (radial velocity and beta), the only
other kind besides Processed Wind Profile with one. Any other kind
found on disk is still listed (so you can see what's in your data
tree) but reported as not yet implemented.

## Installation

### Conda (recommended -- minimal footprint on top of miniconda)

```bash
conda env create -f environment.yml
conda activate haloviewer
```

This only adds `numpy`, `pandas` and `matplotlib` on top of a base
Python; the GUI toolkit (Tkinter) ships with the standard `python`
conda package on Linux, macOS and Windows, so nothing extra is needed
for the GUI itself. Tested against Python 3.9+ on Ubuntu 22.04, 24.04
and 26.04, macOS and Windows.

### Plain pip

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

(On some minimal Linux distributions Tkinter is a separate OS package,
e.g. `sudo apt install python3-tk` on Debian/Ubuntu -- not needed when
using the conda environment above, since conda's `python` package
already includes it.)

### Version numbers

The version is derived from the git history by
[setuptools-scm](https://setuptools-scm.readthedocs.io) and written to
`haloviewer/_version.py` when the package is installed. To make a
release, tag it (e.g. `git tag v0.2.0`) and reinstall. Without git
metadata (e.g. installed from a zip) the version falls back to `0.1.0`.
Static project information (author, licence, ...) is in
`haloviewer/_metadata.py`.

## Contents

HaloViewer provides four ways to work with the data. Each has its own
page in the full documentation (see below).

**Graphic viewer -- `haloviewer`.**
A Tkinter desktop application for browsing a `Proc` tree: pick a
directory, a file kind and *Profile* or *History* mode, then step
through single files or whole time windows, with adjustable height,
distance and speed ranges.

```bash
haloviewer /path/to/Data/Proc
```

**Command-line tool -- `haloplot`.**
Plots files, directories or glob patterns straight to an image file,
without the GUI. File kind, plot mode and time range are inferred where
possible and can be set explicitly.

```bash
haloplot Proc/2026/202609 --kind RHI --start 24h -p rhi_24h.png
```

**Sync tool -- `halosync`.**
A separate GUI for selectively copying the `Metek`, `Proc` and `Raw`
trees from the lidar control PC to a backup or analysis disk. It skips
files that are already there, leaves out the file the lidar is still
writing, and can run on a fixed schedule.

```bash
halosync
```

**Python API -- `haloviewer.plot()`.**
The same functionality as `haloplot` for scripts and notebooks. It
returns a matplotlib `Figure` for further customisation.

```python
import haloviewer
fig = haloviewer.plot("Proc/2026/202609/20260919",
                      kind="Processed_Wind_Profile", start="24h")
```

### Documentation

The full documentation (Sphinx, in `docs/`) describes each tool in
detail and includes the API reference generated from the docstrings.
Build it from the project root; the HTML ends up in `build/html/`:

```bash
pip install -e ".[docs]"
sphinx-build -b html docs build/html
```

## Design

### File structure

The code is layered so each piece can be used, tested and understood
on its own:

| module | responsibility |
|---|---|
| `haloviewer.hpl` | `.hpl` file format parser (adapted from [cdruee/python-readmet](https://github.com/cdruee/python-readmet)'s `hpl` module) |
| `haloviewer.scan` | finds `.hpl` files under a root directory, classifies them by kind from the filename, indexes by timestamp |
| `haloviewer.data` | turns parsed files into plain numpy/pandas arrays ready to plot |
| `haloviewer.plotting` | **pure matplotlib**, no GUI toolkit imports: figure/axes creation and drawing functions |
| `haloviewer.api` | programmatic entry points (`plot`, `plot_file`, `plot_files`); `plot` also re-exported as `haloviewer.plot` |
| `haloviewer.cli` | command-line interface on top of `api` |
| `haloviewer.gui` | Tkinter desktop app; wires widgets to `scan`/`data`/`plotting` and contains no plotting logic itself |
| `haloviewer.halosync` | standalone data-sync GUI (`halosync` command); standard library only, independent of the other modules |
| `haloviewer._metadata` | static project information (name, author, licence, ...) |
| `haloviewer._version` | version number, generated by setuptools-scm at install time (not under version control) |

`plotting.py` never imports `tkinter`, and `gui.py` never calls
matplotlib drawing primitives directly -- it only calls functions in
`plotting.py`. This means the exact same plotting code is used by the
GUI, the CLI, and any script that imports `haloviewer.api`.

### Extending to more scan kinds

`Processed_Wind_Profile`, `VAD`, `Stare`, `Wind_Profile` and `RHI` are
all implemented; a user-defined pattern (`User1`...`User5` in the raw
`.hpl` header's `Scan type`) or a future Halo scan kind would follow
the same recipe:

1. Add a reader to `data.py` that turns a parsed `hpl.DataFile` (or a
   set of them) into plain arrays.
2. Add drawing function(s) to `plotting.py` that take those arrays and
   axes/figure objects -- reuse `create_timeseries_figure`'s two
   stacked, colour-mapped panels if that shape fits; that's what all
   three History flavours and RHI's own Profile scatter share.
3. Register the kind's capabilities in `scan.KIND_CAPABILITIES`
   (`supported=True`, its plot modes).
4. Wire the new mode(s) into `api.plot_file`/`plot_files`, and into
   `gui.HaloViewerApp._plot_kind` (which of the four load/render
   pipelines applies) and `_update_range_controls_enabled` (which of
   Height/Distance/Speed make sense for it).

The GUI will then automatically offer that kind and mode as soon as it
is discovered on disk -- no other GUI changes are needed.

### Tests

```bash
pip install -e ".[dev]"
pytest
```

## Licence

HaloViewer is licensed under the European Union Public Licence v1.2
(EUPL-1.2); see [`LICENSE`](LICENSE) for the full licence text.

`haloviewer/hpl.py` is adapted from the `hpl` module of
[cdruee/python-readmet](https://github.com/cdruee/python-readmet),
which is itself licensed under the EUPL-1.2.

## Copyright

(c) 2026 Clemens Drüe, Universität Trier

Developed with support of Anthropic Claude Opus 5.5.
