-
Notifications
You must be signed in to change notification settings - Fork 1
notebooks: binder-runnable reader demos (hhdc_viewer, waveform_viewer) #540
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 2 commits
Commits
Show all changes
44 commits
Select commit
Hold shift + click to select a range
ae00901
notebooks: binder-runnable reader demos (hhdc_viewer, waveform_viewer)
espg 1b96153
notebooks: share the viewers in viewers.py; say 'contain' in the cove…
espg 347e72e
viewers: binned tensor mode in the 3-D view, checked by default
espg bb797b2
notebooks: archive the stale examples off main; drop leaked .npy arti…
espg c0f38f8
fold review: ruff check + format clean on both notebooks (issue #328)
espg a92d84a
fold review: bin the shard's tensors as they arrive, not all at once …
espg dd59f40
fold review: put binned points at cell centres, not corners (issue #328)
espg a3bae14
fold review: grid_xy returns (col, row) so east and north are true (i…
espg ec66ce9
fold review: bound the figure registry in both interactive views (iss…
espg cf62491
fold review: split ATL03's chunk side from GEDI's gside (issue #328)
espg 20cdf48
fold review: cache the digest reads and make the empty-cell guard rea…
espg eefbb1b
fold review: normalize both sensors before ranking on the weaker one …
espg 2912736
fold review: export says whether it got the z resolution it asked for…
espg 3f8919b
fold review: stream the cube export instead of holding the shard in R…
espg 21da9fc
fold review: header says one %pip install and names the one zagg impo…
espg 487fa7f
fold review: cross-link the notebook pair, drop the dangling 07_minim…
espg 96f6d25
fold review: ignore the notebook tensor exports (issue #328)
espg 0df2d62
fold review: say 'no zagg public API', not 'nothing from zagg' (issue…
espg 5ab6913
fold review: drop the duplicated reader-only paragraph (issue #328)
espg 62ca510
fold review: pin the archived-notebook links to a commit permalink (i…
espg ef5497f
fold review: postBuild names the real zagg dependency and the shadowi…
espg 4410762
fold review: let ipywidgets come from viz rather than reinstalling it…
espg 5400b1d
fold review: say why open_shard's sweep is deliberate and no longer d…
espg eb7ef39
fold review: the block axes are face-local NE/NW, not compass east/no…
espg d3c26be
viewers: read the 3-D view's tensors per block, not per shard (PR #540)
espg 616fe4d
notebooks: descending block list; split the export cell; read one blo…
espg b7fd245
notebooks: say what the timing cell is for, and how to swap the polyg…
espg b9d7631
viewers: sort the merged 2x2 ATL03 digest; the CDF reads means as int…
espg d217dc8
notebooks: rewrite the coincident-waveform prose; cite the GEDI gain;…
espg 0d08fd0
notebooks: report reads honestly; co-registered voxel exports (PR #540)
espg d1372ae
notebooks: unstick the nth slider; block list from the tally, sorted …
espg 7141cf4
notebooks: opacity control, joint-cell ordering, shared shard pick, c…
espg f33fecc
notebooks: trim the added prose; show both z windows instead of quoti…
espg 06b001b
notebooks: move exports to export.py; isotropic 128^3 chips with repo…
espg 7f40f46
notebooks: reframe the export section around the two decisions it for…
espg cbd89c9
notebooks: scope the range-bin claim to the examples (PR #540)
espg 7ebed91
notebooks: open_shard -> viewer_stats; the read path is handles plus …
espg e59ce04
notebooks: paired_blocks helper; drop BLOCK_ORDER and the grid consta…
espg 2ff7a37
viewers: shade by weight ramps down from the slider, not up to opaque…
espg a076d57
fold review: fit_window clipped and mis-binned silently; unparsable m…
espg b1b3ab1
viewers: revert continuous_update; bound the nth slider to the block'…
espg 603a6db
viewers: read waveform digests a block at a time; the slider does no …
espg 5933e1b
viewers: keep column sums and gains, not block tensors (1.25 GiB -> ~…
espg 2c7fb10
viewers: opacity fires on release; nth needs no suppression now it re…
espg File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,239 @@ | ||
| { | ||
| "cells": [ | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "039315dd", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "# polygon \u2192 MOC \u2192 shard \u2192 3-D \u2192 numpy\n", | ||
| "\n", | ||
| "The whole zagg read stack in five functions, two `pip install`s, and zero\n", | ||
| "credentials. A geojson polygon becomes a morton MOC; the MOC checks itself\n", | ||
| "against the store's own coverage; the covered shards open with timings; one\n", | ||
| "shard renders in 3-D (ATL03 + GEDI together); the current view exports to a\n", | ||
| "numpy tensor and saves to disk. Everything below is reader-side \u2014 `mortie`\n", | ||
| "for the geometry, `moczarr` for the store \u2014 running anonymously against\n", | ||
| "public S3, binder-ready." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "f67cef5b", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "%pip install -q mortie \"moczarr[zagg]>=0.7\" matplotlib ipympl ipywidgets\n", | ||
| "%matplotlib widget\n", | ||
| "\n", | ||
| "import resource\n", | ||
| "import time\n", | ||
| "\n", | ||
| "import matplotlib.pyplot as plt\n", | ||
| "import numpy as np\n", | ||
| "import moczarr as mz\n", | ||
| "from mortie import moc\n", | ||
| "\n", | ||
| "# The drawing lives in viewers.py beside this notebook, so the cells below stay\n", | ||
| "# about the READ path. Both demo notebooks share it.\n", | ||
| "from viewers import BLOCK_ORDER, view3d\n", | ||
| "\n", | ||
| "# One store per product, each appendable. Coverage answers which ground the\n", | ||
| "# store holds; the store name never does.\n", | ||
| "STORES = {\n", | ||
| " \"atl03\": (\"s3://us-west-2.opendata.source.coop/englacial/zagg/demo/atl03_tdigest_o9.zarr\", \"19/h_tdigest_signal\"),\n", | ||
| " \"gedi\": (\"s3://us-west-2.opendata.source.coop/englacial/zagg/demo/gedi_flux_o9.zarr\", \"18/rx_flux\"),\n", | ||
| "}\n", | ||
| "S3 = {\"region\": \"us-west-2\", \"anonymous\": True}\n", | ||
| "\n", | ||
| "rss = lambda: resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 2**20 # noqa: E731" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "4cafa82b", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## One polygon in, covered shards out" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "933b33b2", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "aoi = {\"features\": [{\"geometry\": {\"coordinates\": [[ # a ~4 km box on the SERC tract\n", | ||
| " [-76.56, 38.87], [-76.50, 38.87], [-76.50, 38.91], [-76.56, 38.91], [-76.56, 38.87]\n", | ||
| "]]}}]}\n", | ||
| "\n", | ||
| "q = moc(aoi)\n", | ||
| "shards = None\n", | ||
| "for name, (root, _field) in STORES.items():\n", | ||
| " assert mz.coverage_moc(root, **S3).contains(q), f\"{name} does not contain the polygon\"\n", | ||
| " ids = set(mz.candidate_shards(root, aoi=q, **S3))\n", | ||
| " shards = ids if shards is None else shards & ids\n", | ||
| "shards = sorted(shards)\n", | ||
| "shards" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "1634ec9a", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## Open one shard \u2014 every dataset, timed" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "0300cfdb", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "def open_shard(shard):\n", | ||
| " \"\"\"Open the shard's leaf in every store; print open+sweep timing and RSS.\"\"\"\n", | ||
| " handles = {}\n", | ||
| " for name, (root, field) in STORES.items():\n", | ||
| " t0, r0 = time.perf_counter(), rss()\n", | ||
| " store = mz.open_leaf(root, shard, **S3)\n", | ||
| " _, element = mz.open_ragged(store, field)\n", | ||
| " n = sum(len(v) for _, v in mz.read_ragged(store, field))\n", | ||
| " print(f\"{name:6s} open+sweep {time.perf_counter() - t0:5.1f}s \"\n", | ||
| " f\"(+{rss() - r0:4.0f} MB) \u2014 {n:,} centroids, element {element}\")\n", | ||
| " handles[name] = (store, field)\n", | ||
| " return handles\n", | ||
| "\n", | ||
| "\n", | ||
| "handles = open_shard(shards[0])" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "138d32bc", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## The 3-D view \u2014 both sensors, exact centroids, time-aware" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "0e1b012c", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "view = view3d(handles, shards[0])\n" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "e70211aa", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## Export what you see \u2014 a numpy tensor, shaped your way" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "68119f92", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "def export(view, sensor, n_bins=64, resolution=1.0, fit=\"degrade_resolution\"):\n", | ||
| " \"\"\"The block on screen -> (rows, cols, n_bins) numpy tensor + z metadata.\n", | ||
| "\n", | ||
| " `n_bins`/`resolution` set the vertical shape; the xy shape follows the\n", | ||
| " sensor's cell order within the o12 block -- 2**(cell_order - 12) a side,\n", | ||
| " so 128 for ATL03's o19 cells and 64 for GEDI's o18.\n", | ||
| " \"\"\"\n", | ||
| " store, field = handles[sensor]\n", | ||
| " t, mask, (z0, dz), w = next(\n", | ||
| " b for b in mz.read_tensors(store, field, n_bins=n_bins, resolution=resolution,\n", | ||
| " block_order=BLOCK_ORDER, fit=fit)\n", | ||
| " if int(b[3]) == int(view.block)\n", | ||
| " )\n", | ||
| " print(f\"{sensor}: {t.shape} tensor, z = {z0:.1f} m + bin * {dz:g} m\")\n", | ||
| " return t, {\"z0\": z0, \"dz\": dz, \"block\": mz.morton_decimal(w)}\n", | ||
| "\n", | ||
| "\n", | ||
| "gedi, meta = export(view, \"gedi\", n_bins=128, resolution=0.5)\n", | ||
| "np.save(f\"gedi_{meta['block']}.npy\", gedi)\n", | ||
| "\n", | ||
| "atl03, meta = export(view, \"atl03\") # default 64 x 1 m bins\n", | ||
| "np.save(f\"atl03_{meta['block']}.npy\", atl03)" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "id": "585bbd96", | ||
| "source": [ | ||
| "# ---- second export: every block of the shard as a 128 x 128 x 128 cube ----\n", | ||
| "# ATL03's cells are o19, so an o12 block is 2**(19-12) = 128 cells a side;\n", | ||
| "# ask for 128 z-bins at 1 m and the tensor is a cube. One `read_tensors`\n", | ||
| "# sweep yields one cube per populated block, so a shard exports a SET.\n", | ||
| "#\n", | ||
| "# `fit=\"degrade_resolution\"` keeps a block whose trimmed relief exceeds 128 m\n", | ||
| "# rather than refusing it -- the z gain comes back per block, so the ones that\n", | ||
| "# did not fit at 1 m say so instead of silently pretending.\n", | ||
| "cubes = {}\n", | ||
| "for tensor, mask, (z0, dz), w in mz.read_tensors(\n", | ||
| " *handles[\"atl03\"], n_bins=128, resolution=1.0, block_order=BLOCK_ORDER,\n", | ||
| " fit=\"degrade_resolution\",\n", | ||
| "):\n", | ||
| " cubes[mz.morton_decimal(w)] = {\"tensor\": tensor, \"z0\": z0, \"dz\": dz,\n", | ||
| " \"cells\": int(mask.astype(bool).sum())}\n", | ||
| "\n", | ||
| "exact = [b for b, c in cubes.items() if c[\"dz\"] == 1.0]\n", | ||
| "print(f\"{len(cubes)} blocks -> {len(cubes)} cubes of {next(iter(cubes.values()))['tensor'].shape}\")\n", | ||
| "print(f\" {len(exact)} at the requested 1 m bins; \"\n", | ||
| " f\"{len(cubes) - len(exact)} degraded to fit their relief in 128 bins\")\n", | ||
| "for b, c in sorted(cubes.items())[:5]:\n", | ||
| " print(f\" {b} z = {c['z0']:8.1f} m + bin * {c['dz']:.2f} m \"\n", | ||
| " f\"{c['cells']:5,} populated cells {c['tensor'].nbytes / 2**20:.1f} MiB\")\n", | ||
| "\n", | ||
| "np.savez_compressed(\n", | ||
| " f\"atl03_cubes_{shards[0]}.npz\",\n", | ||
| " **{b: c[\"tensor\"] for b, c in cubes.items()},\n", | ||
| ")\n", | ||
| "print(f\"saved atl03_cubes_{shards[0]}.npz\")" | ||
| ], | ||
| "metadata": {}, | ||
| "execution_count": null, | ||
| "outputs": [] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "99e36ee1", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "Five functions, two libraries, one polygon \u2014 coverage, shards, timings,\n", | ||
| "the paired 3-D view, and tensors on disk." | ||
| ] | ||
| } | ||
| ], | ||
| "metadata": { | ||
| "kernelspec": { | ||
| "display_name": "Python 3 (ipykernel)", | ||
| "language": "python", | ||
| "name": "python3" | ||
| }, | ||
| "language_info": { | ||
| "codemirror_mode": { | ||
| "name": "ipython", | ||
| "version": 3 | ||
| }, | ||
| "file_extension": ".py", | ||
| "mimetype": "text/x-python", | ||
| "name": "python", | ||
| "nbconvert_exporter": "python", | ||
| "pygments_lexer": "ipython3", | ||
| "version": "3.13.1" | ||
| } | ||
| }, | ||
| "nbformat": 4, | ||
| "nbformat_minor": 5 | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.