Skip to main content

peaknav

Python tools for PeakNav, the 3D mountain viewer — in two halves that share nothing but a namespace:

  • peaknav.terrain — pure Python. The elevation of any coordinate on Earth, from the same compressed ASTER dataset the app renders — with summit heights corrected against surveyed values — downloaded once per area (a ~30 MB archive covers a 4×4 block of tiles) and cached. One dependency: Pillow.
  • peaknav.headless — a standard-library client for the real PeakNav renderer running off-screen: camera control, view options, rendered frames. Needs a Java runtime, a display (the window is hidden, but GL still needs one), and the renderer jar — which is fetched on demand rather than shipped; see below.
  • peaknav.jupyter — an interactive view inside a notebook, driven entirely over the renderer's REST API. pip install peaknav[jupyter].
>>> from peaknav.terrain import elevation_at
>>> elevation_at(45.9417, 7.7480)          # the Breithorn
4160

(The dataset carries summit corrections against surveyed heights. Raw ASTER, like every stereo DEM, rounds off sharp spires — it clipped the Matterhorn to about 4040 m — but the corrected data tops out at 4484, the surveyed 4478 to within the encoding's 4 m step. At ~30 m pixels the summit can still sit a pixel or two from the coordinate you have for a peak — the classic Matterhorn coordinate reads 4312 — so scan a few arcseconds around a spire's coordinate for its true top; examples/01_elevation.ipynb shows how.)

from peaknav.headless import PeakNavHeadless

with PeakNavHeadless(45.9763, 7.6586) as nav:
    nav.move_to(45.9763, 7.6586, download_timeout_ms=600_000, await_tiles_ms=120_000)
    nav.look(bearing_deg=230, pitch_deg=-4)
    nav.set_altitude_asl(3200)
    nav.set_view(sky=True, sky_mode="day", labels=["peaks", "roads"])
    nav.wait(tiles_timeout_ms=60_000, settle_ms=1_000)
    nav.save_frame("matterhorn.png")

The renderer speaks plain HTTP, self-described at /openapi.json — anything that can curl can drive it; this client adds process lifecycle (the JVM dies with the with block) and nothing magical.

Install

pip install peaknav                        # once published; from source:
pip install -e "peaknav-python[dev]"

Examples

Four notebooks in examples/, in increasing order of what they need from the machine:

Notebook Shows Needs
01_elevation.ipynb elevation of any coordinate, a profile along a line, the tile encoding network only
02_renderer_over_rest.ipynb driving the renderer over REST, frames back — no widgets Java, a display, the jar
03_interactive_widget.ipynb the PeakNavViewer widget, and driving it from code the above + peaknav[jupyter]
04_panorama_sweep.ipynb scripted rendering: a full-circle panorama and a short flight Java, a display, the jar

They are stored without outputs — rendered frames would dominate every diff, and a stale picture beside changed code is worse than none.

Tests

pytest                                     # doctests in every module, plus tests/
PEAKNAV_NETWORK_TESTS=1 pytest tests/test_terrain.py     # also hit the live dataset

The doctests are the documentation's examples, so an example that stops working fails the suite rather than misleading a reader. Tests that need the network, the jar, or a display skip themselves with a reason.

In a notebook

from peaknav.headless import PeakNavHeadless
from peaknav.jupyter import PeakNavViewer

nav = PeakNavHeadless(46.0207, 7.7491)          # or .attach("http://127.0.0.1:8080")
PeakNavViewer(nav, bearing_deg=230, pitch_deg=-4, altitude_m=3200)

That last line is the widget: pan and tilt buttons, a height control, coordinates to type, display toggles and the rendered view. pip install peaknav[jupyter] — it needs ipywidgets, which the base package does not install.

The widget is a REST client and nothing else. Every control becomes a documented HTTP call on the renderer's own server (POST /camera, POST /position, POST /view, GET /frame), made through the client you hand it. It never starts a renderer, never looks for a jar and never touches a subprocess — so it drives one you started, or one already running elsewhere, with no difference in the code.

The two layers are separable on purpose:

  • peaknav.jupyter.camera.ViewerCamera — where the camera is and what each movement sends. Plain Python, no dependencies; usable from a script, and what the tests drive with a stub client to check exactly which REST calls come out.
  • peaknav.jupyter.viewer.PeakNavViewer — the ipywidgets face on it.

Moving the camera from another cell is fine; call viewer.sync_from_camera() afterwards so the sliders and the picture agree again. For a single picture with no controls at all, peaknav.jupyter.show(nav) needs only IPython.

The renderer jar

The renderer is 75 MB of Java. Putting it inside the wheel would make everyone who only wants peaknav.terrain — pure Python, one dependency — download it too, so it is found rather than shipped. First hit wins:

  1. the jar= argument to PeakNavHeadless;
  2. $PEAKNAV_HEADLESS_JAR, to point a whole session at one build;
  3. headless/build/libs/ of a PeakNavApp checkout, so a developer's own build always beats a download;
  4. the cache, $XDG_CACHE_HOME/peaknav/jars (%LOCALAPPDATA% on Windows);
  5. the release asset, downloaded into that cache, once.

$PEAKNAV_NO_DOWNLOAD forbids step 5 — on a build machine that should not reach the network it turns a silent 75 MB fetch into an error naming what is missing. A download is checked before it is cached: structurally (a readable zip containing the renderer's entry point, which is what catches a truncated file or the wrong asset) and, when a digest is pinned for that version, against it.

from peaknav.headless import ensure_jar
ensure_jar()          # fetch it now rather than on the first render

No release carries the renderer jar yet. peaknav-1.1.0.jar on the releases page is the desktop application and has no renderer inside it. Until peaknav-headless-<version>.jar is attached to a release, use a local build (./gradlew :headless:renderJar) or $PEAKNAV_HEADLESS_JAR; step 5 fails with a message saying exactly that. When publishing one, raise JAR_VERSION in peaknav/headless/jar.py to that release and record its sha256sum in KNOWN_SHA256 in the same commit.

Documentation

Built from the docstrings — no separate prose to drift out of date:

pdoc peaknav peaknav.terrain peaknav.headless -o docs/   # static HTML into docs/

Build for PyPI

python -m build                            # sdist + wheel into dist/
twine upload dist/*

The elevation encoding, briefly

Each zoom-8 slippy tile is a JPEG + PNG pair: the PNG names each pixel's 1024 m band (128 + floor(e/1024)), the JPEG the position inside it in 4 m steps, with odd bands flipped so band edges stay smooth gradients that JPEG compresses without ringing. peaknav.terrain.decode_elevation is the four-line inverse, doctested against an independent port of the dataset's encoder. The dataset ships the pairs packed one .tar.gz per zoom-6 tile — the same archives the app downloads — which the module unpacks into its cache on first use of an area. Summit queries default to the max of the four surrounding pixels (ASTER's ~30 m posting rarely centres a summit on one); pass sample="bilinear" for slopes and profiles.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

peaknav-0.0.2.tar.gz (39.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

peaknav-0.0.2-py3-none-any.whl (31.1 kB view details)

Uploaded Python 3

File details

Details for the file peaknav-0.0.2.tar.gz.

File metadata

  • Download URL: peaknav-0.0.2.tar.gz
  • Upload date:
  • Size: 39.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.3

File hashes

Hashes for peaknav-0.0.2.tar.gz
Algorithm Hash digest
SHA256 cb6ba6f4c233a49a5e6468f32243cd2d9bae61fc805b05d2de2fd02ce5cb8f19
MD5 d298c11d38a83d912e3efee1d2d4141a
BLAKE2b-256 c135e01452f273ccf03dc3313e48de1977933899b568f80f1144fdb5fc992bdc

See more details on using hashes here.

File details

Details for the file peaknav-0.0.2-py3-none-any.whl.

File metadata

  • Download URL: peaknav-0.0.2-py3-none-any.whl
  • Upload date:
  • Size: 31.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.3

File hashes

Hashes for peaknav-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 18996448073e385f0fabcc762c1a0351711c538cf3956b215d8fd7d94e8517ce
MD5 8f46ef96924c1e6b986cad9ddaea9a18
BLAKE2b-256 8db7ab9071b63799248106361ed1c7cf861933c8f5643800aa2998991eb2ab0a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page