pixel-clarity 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. pixel_clarity-0.1.0/.gitignore +15 -0
  2. pixel_clarity-0.1.0/CHANGELOG.md +21 -0
  3. pixel_clarity-0.1.0/LICENSE +21 -0
  4. pixel_clarity-0.1.0/PKG-INFO +187 -0
  5. pixel_clarity-0.1.0/README.md +161 -0
  6. pixel_clarity-0.1.0/pyproject.toml +67 -0
  7. pixel_clarity-0.1.0/src/pixel_clarity/__init__.py +52 -0
  8. pixel_clarity-0.1.0/src/pixel_clarity/_backend.py +167 -0
  9. pixel_clarity-0.1.0/src/pixel_clarity/_node.py +41 -0
  10. pixel_clarity-0.1.0/src/pixel_clarity/_regions.py +79 -0
  11. pixel_clarity-0.1.0/src/pixel_clarity/_settings.py +99 -0
  12. pixel_clarity-0.1.0/src/pixel_clarity/_sources.py +314 -0
  13. pixel_clarity-0.1.0/src/pixel_clarity/_version.py +1 -0
  14. pixel_clarity-0.1.0/src/pixel_clarity/_wire.py +65 -0
  15. pixel_clarity-0.1.0/src/pixel_clarity/static/THIRD_PARTY_NOTICES.md +1469 -0
  16. pixel_clarity-0.1.0/src/pixel_clarity/static/aladinPanel.css +1 -0
  17. pixel_clarity-0.1.0/src/pixel_clarity/static/aladinPanel.js +12 -0
  18. pixel_clarity-0.1.0/src/pixel_clarity/static/backend.mjs +25 -0
  19. pixel_clarity-0.1.0/src/pixel_clarity/static/build.json +5 -0
  20. pixel_clarity-0.1.0/src/pixel_clarity/static/frame.js +1 -0
  21. pixel_clarity-0.1.0/src/pixel_clarity/static/settings.json +252 -0
  22. pixel_clarity-0.1.0/src/pixel_clarity/static/share.css +1 -0
  23. pixel_clarity-0.1.0/src/pixel_clarity/static/share.js +81 -0
  24. pixel_clarity-0.1.0/src/pixel_clarity/static/shareWorker.js +134 -0
  25. pixel_clarity-0.1.0/src/pixel_clarity/static/tab.js +62 -0
  26. pixel_clarity-0.1.0/src/pixel_clarity/static/table.css +1 -0
  27. pixel_clarity-0.1.0/src/pixel_clarity/static/table.js +4 -0
  28. pixel_clarity-0.1.0/src/pixel_clarity/static/webview.css +1 -0
  29. pixel_clarity-0.1.0/src/pixel_clarity/static/webview.js +185 -0
  30. pixel_clarity-0.1.0/src/pixel_clarity/static/widget.js +62 -0
  31. pixel_clarity-0.1.0/src/pixel_clarity/static/worker.mjs +25487 -0
  32. pixel_clarity-0.1.0/src/pixel_clarity/viewer.py +285 -0
@@ -0,0 +1,15 @@
1
+ node_modules/
2
+ .venv/
3
+ __pycache__/
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ src/pixel_clarity/static/
8
+ .pytest_cache/
9
+ test-out/
10
+ .ipynb_checkpoints/
11
+ .DS_Store
12
+ # Notebooks for particular servers or data (kept out of the repository).
13
+ notebooks/local/
14
+ # Development notes for this checkout (kept out of the repository).
15
+ CLAUDE.md
@@ -0,0 +1,21 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 (2026-10-15)
4
+
5
+ - First version: `pc.show()` shows images, mosaics, RGB sets and stacks in a notebook with the
6
+ Pixel Clarity viewer (from Pixel Clarity 1.2.0). Reads local files, `s3://` objects and URLs where
7
+ they are, and arrays, HDUs, NDData, ASDF files and data models from the kernel.
8
+ - Connects directly to its process through jupyter-server-proxy (or to localhost), so the view
9
+ keeps working while a cell runs; through the kernel elsewhere. *Open in a new tab* for a full
10
+ window.
11
+ - Full-resolution export, shared pages and saved mosaics are written next to the notebook; the
12
+ Aladin Lite panel opens beside the viewer; the notebook's light or dark theme is followed.
13
+ - Regions both ways: `viewer.add_regions()` draws `regions` objects, a DS9 file or DS9 text on
14
+ the view; `viewer.regions` (and `viewer.regions_ds9`) reads back what is drawn, also from a tab.
15
+ - Tables: `pc.table()` (and `pc.show()` of a table file) opens CSV, TSV, ECSV and Parquet files,
16
+ and astropy, pandas and polars tables from the kernel, in the Pixel Clarity table viewer.
17
+ Sorting, filtering, statistics and plots are worked out on the server; exports are written next
18
+ to the notebook; *Overlay on image* draws the rows on the image viewer used last.
19
+ - Sized for small servers: worker threads and caches follow the container's CPU and memory limits,
20
+ and viewers left idle free their memory. A file read from a URL keeps its downloaded blocks
21
+ within its view's share of memory, so a mosaic of many URLs stays bounded.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Clarity Orbital Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,187 @@
1
+ Metadata-Version: 2.5
2
+ Name: pixel-clarity
3
+ Version: 0.1.0
4
+ Summary: Pixel Clarity in Jupyter: a fast viewer for large astronomical images, mosaics, cubes and tables, with the data kept on the server
5
+ Project-URL: Homepage, https://clarityorbital.com
6
+ Author: Clarity Orbital
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ License-File: src/pixel_clarity/static/THIRD_PARTY_NOTICES.md
10
+ Keywords: asdf,astronomy,fits,image viewer,jupyter,parquet,table viewer,widget
11
+ Classifier: Framework :: Jupyter
12
+ Classifier: Framework :: Jupyter :: JupyterLab
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Topic :: Scientific/Engineering :: Astronomy
16
+ Classifier: Topic :: Scientific/Engineering :: Visualization
17
+ Requires-Python: >=3.10
18
+ Requires-Dist: anywidget>=0.9
19
+ Requires-Dist: nodejs-wheel-binaries>=20
20
+ Requires-Dist: numpy
21
+ Provides-Extra: regions
22
+ Requires-Dist: regions; extra == 'regions'
23
+ Provides-Extra: s3
24
+ Requires-Dist: boto3; extra == 's3'
25
+ Description-Content-Type: text/markdown
26
+
27
+ # Pixel Clarity for Jupyter
28
+
29
+ A fast viewer for large astronomical images, mosaics, cubes and tables, in a notebook. It is the
30
+ [Pixel Clarity](https://clarityorbital.com) VS Code viewer as a Jupyter widget: the same stretch,
31
+ colors, regions and statistics, WCS readout, mosaics placed on the sky, RGB, stacks, data quality
32
+ masks, magnitudes, full-resolution PNG export, shared pages, the Aladin Lite sky panel, and the
33
+ table viewer for catalogs.
34
+
35
+ The data stays on the server. A process next to the kernel reads the files there (local paths, or
36
+ S3 objects and URLs by range requests) and the browser receives only the tiles, rows and values it
37
+ shows, so a 4k × 4k detector, a mosaic of many detectors or a catalog of millions of rows stays
38
+ quick to look at over an ordinary internet connection.
39
+
40
+ ```python
41
+ import pixel_clarity as pc
42
+
43
+ pc.show("image.asdf") # a file on the server
44
+ pc.show("s3://bucket/path/image.asdf") # read where it is, no download
45
+ pc.show(model) # a data model, HDU, NDData or array in the kernel
46
+ pc.mosaic("exposure/") # a folder of detector files, placed on the sky
47
+ pc.mosaic(files, color=True) # two or more filters as colors
48
+ pc.rgb("red.fits", "green.fits", "blue.fits")
49
+ pc.stack(sorted(glob("frames/*.fits"))) # frames one at a time, with a slider
50
+ pc.show("image.fits", cmap="viridis", scale="asinh", limits="99.5", height=700)
51
+ pc.table("catalog.parquet") # a table: sorted, filtered and plotted on the server
52
+ ```
53
+
54
+ ## Install
55
+
56
+ ```
57
+ %pip install pixel-clarity
58
+ ```
59
+
60
+ Restart the kernel afterwards (or use a new one). It needs JupyterLab 4 or Notebook 7 with
61
+ ipywidgets 8, which most hubs have. On a JupyterHub where packages installed in the kernel's
62
+ environment don't survive a server restart, `%pip install --user pixel-clarity` puts it in your
63
+ home directory instead.
64
+
65
+ It brings its own Node.js (`nodejs-wheel-binaries`) for the process that reads the data; set
66
+ `PIXEL_CLARITY_NODE` to use another (version 20 or later).
67
+
68
+ ## How it connects
69
+
70
+ The viewer reaches its process the quickest way the page allows, shown by the dot under it:
71
+
72
+ - **Green, direct:** through [jupyter-server-proxy](https://github.com/jupyterhub/jupyter-server-proxy)
73
+ (installed on many hubs), or straight to the server when the browser runs on the same machine.
74
+ Tiles never wait for the kernel, so the view keeps working while a cell runs. *Open in a new tab*
75
+ shows the same view in a browser tab of its own.
76
+ - **Amber, through the kernel:** widget messages relayed by the kernel. It works anywhere widgets
77
+ do, but the view waits while a cell runs. `pc.show(..., connection="kernel")` asks for this.
78
+
79
+ ## What it reads
80
+
81
+ - Paths: FITS (including tile-compressed `.fits.fz`), ASDF (including lz4 blocks and gwcs), HDF5,
82
+ NumPy `.npy`/`.npz`, TIFF and PNG. A folder or a glob pattern shows its image files; a
83
+ `.pcmosaic` file (saved from a mosaic's Detectors panel) opens that mosaic again.
84
+ - `s3://` URIs: public buckets anonymously; `anon=False` signs the URL with the kernel's AWS
85
+ credentials (`pip install 'pixel-clarity[s3]'`). An open `s3fs` file works too.
86
+ - `http(s)://` URLs to servers that answer Range requests.
87
+ - From the kernel: NumPy arrays (masked arrays as NaN), astropy HDUs, HDU lists and NDData/CCDData
88
+ (with their WCS), ASDF files and data models. These are written once to a temporary file
89
+ on the server's local disk (an unchanged array is written only once), removed when the kernel
90
+ stops.
91
+
92
+ ## Settings
93
+
94
+ Keywords of `show` (and `Viewer`), named after the VS Code extension's settings; `pc.settings()`
95
+ lists them with their values and defaults. Set them for every viewer with
96
+ `pc.defaults["cmap"] = "viridis"`.
97
+
98
+ | Keyword | Values |
99
+ |---|---|
100
+ | `scale` | `linear`, `log`, `pow`, `sqrt`, `squared`, `asinh`, `sinh`, `histeq`, `mtf` |
101
+ | `limits` | `minmax`, `zscale`, `99.5`, `99`, `98`, `95`, `90` |
102
+ | `cmap` | `gray`, `viridis`, `magma`, `inferno`, `heat`, `cool`, … |
103
+ | `invert`, `auto` | `True`/`False` (`auto`: Auto enhance on opening) |
104
+ | `zoom` | `fit` or `1` |
105
+ | `sidebar`, `cuts` | show the side panel, the cut plots (`cuts` is off by default in a notebook) |
106
+ | `mosaic_layout`, `match_sky`, `flux_calibrate`, `feather`, `trim_edges` | mosaic placement and level matching |
107
+ | `online`, `survey` | SIMBAD and Aladin Lite (CDS) from the browser |
108
+
109
+ Other keywords: `height` (pixels; the grip under the viewer changes it), `kind` (`single`,
110
+ `mosaic`, `stack`, `rgb`), `title`, `names`, `layout`, `active`, `color`, `groups`, `levels`,
111
+ `state` (a view saved from `viewer.state`), `out_dir` (where exports, shared pages and saved
112
+ mosaics are written; the kernel's working directory by default).
113
+
114
+ ## In the viewer
115
+
116
+ Everything in the VS Code viewer's toolbar and panels works here. Files it writes (a
117
+ full-resolution export, a shared page, a saved mosaic) are written on the server, next to the
118
+ notebook, and named in the line under the viewer. *Save view as shown*, region files and color
119
+ settings download to your computer. Live reload follows changes to local files.
120
+
121
+ A viewer nobody has used for five minutes (thirty if it is on screen) frees the memory its data
122
+ took; it reads the data again when you come back to it.
123
+
124
+ ## Regions from and to Python
125
+
126
+ ```python
127
+ v = pc.show("image.fits")
128
+ v.add_regions(regions) # `regions` objects (pixel or sky), a DS9 .reg file's path, or DS9 text
129
+ v.regions # what is drawn on the view now, as `regions` pixel regions
130
+ v.regions[0].to_sky(wcs)
131
+ print(v.regions_ds9) # the same as DS9 region text (image coordinates), no extra package needed
132
+ ```
133
+
134
+ `v.regions` needs the `regions` package (`pip install 'pixel-clarity[regions]'`). It reads what any
135
+ viewer of `v` last saved, including one opened in a tab of its own, even while a cell runs.
136
+ Coordinates are the image's pixels (a stack's frames, an RGB set's red image); for a mosaic, those
137
+ of the mosaic's own plane. Regions given in sky coordinates are placed with the image's WCS; a
138
+ viewer still opening gets them once its image is shown. More than 2000 shapes show as a catalog
139
+ overlay rather than editable regions.
140
+
141
+ ## Tables
142
+
143
+ ```python
144
+ pc.table("catalog.parquet") # CSV, TSV, ECSV (also .gz) or Parquet: a path, an s3:// URI or a URL
145
+ pc.table(t) # an astropy, pandas or polars table (or a structured array) in the kernel
146
+ pc.show("catalog.csv") # show() opens table files in the table viewer too
147
+ pc.table("catalog.csv", plot=True) # with the plot showing
148
+ ```
149
+
150
+ A table stays on the server as an image does: sorting, filtering (`mag < 20 and class == 'STAR'`),
151
+ computed columns, column statistics and plots are worked out there, and the page receives the
152
+ rows on screen. A Parquet file that would take more than a quarter of the server's memory asks
153
+ which columns to load. Tables from the kernel are written to a temporary file: Parquet when
154
+ pyarrow is installed (units, masks and times kept), else ECSV (astropy) or CSV (pandas).
155
+
156
+ *Export* writes CSV, TSV, ECSV or a region file next to the notebook. *Overlay on image* draws the
157
+ rows on the image viewer used last, named in the line under the table. `t.state` holds the
158
+ filters, sort, columns and plot; pass it as `state=` to open another the same way.
159
+
160
+ ## Not yet
161
+
162
+ - Opening files from JupyterLab's file browser: for now, `pc.show()` them from a cell.
163
+
164
+ ## Development
165
+
166
+ The viewer's sources are the VS Code extension's repository; `npm run build` bundles them, with
167
+ this package's own parts from `js/`, into `src/pixel_clarity/static/`.
168
+
169
+ ```
170
+ npm install
171
+ PC_SRC=../vs_code_imageviewer npm run build # PC_SRC defaults to ../vs_code_imageviewer
172
+ npm run typecheck
173
+ uv sync
174
+ uv run pytest
175
+ node scripts/e2e.mjs [--remote=<url>] [--mosaic=<folder>] [--dark] [--aladin] [--no-proxy] # JupyterLab in headless Chromium
176
+ uv build # the wheel, with the built scripts in it
177
+ ```
178
+
179
+ To release, set the version in `src/pixel_clarity/_version.py`, commit, and push a tag `v<version>`:
180
+ `.github/workflows/pypi.yml` builds against the extension's sources at `UPSTREAM_REF`, runs the
181
+ tests and publishes to PyPI.
182
+
183
+ `notebooks/local_try.ipynb` walks through the viewer by hand on your own machine, with test
184
+ images it makes itself.
185
+
186
+ MIT licensed. The bundled third-party code is listed in `static/THIRD_PARTY_NOTICES.md`; Aladin
187
+ Lite (LGPL) is loaded from CDS when its panel opens, not bundled.
@@ -0,0 +1,161 @@
1
+ # Pixel Clarity for Jupyter
2
+
3
+ A fast viewer for large astronomical images, mosaics, cubes and tables, in a notebook. It is the
4
+ [Pixel Clarity](https://clarityorbital.com) VS Code viewer as a Jupyter widget: the same stretch,
5
+ colors, regions and statistics, WCS readout, mosaics placed on the sky, RGB, stacks, data quality
6
+ masks, magnitudes, full-resolution PNG export, shared pages, the Aladin Lite sky panel, and the
7
+ table viewer for catalogs.
8
+
9
+ The data stays on the server. A process next to the kernel reads the files there (local paths, or
10
+ S3 objects and URLs by range requests) and the browser receives only the tiles, rows and values it
11
+ shows, so a 4k × 4k detector, a mosaic of many detectors or a catalog of millions of rows stays
12
+ quick to look at over an ordinary internet connection.
13
+
14
+ ```python
15
+ import pixel_clarity as pc
16
+
17
+ pc.show("image.asdf") # a file on the server
18
+ pc.show("s3://bucket/path/image.asdf") # read where it is, no download
19
+ pc.show(model) # a data model, HDU, NDData or array in the kernel
20
+ pc.mosaic("exposure/") # a folder of detector files, placed on the sky
21
+ pc.mosaic(files, color=True) # two or more filters as colors
22
+ pc.rgb("red.fits", "green.fits", "blue.fits")
23
+ pc.stack(sorted(glob("frames/*.fits"))) # frames one at a time, with a slider
24
+ pc.show("image.fits", cmap="viridis", scale="asinh", limits="99.5", height=700)
25
+ pc.table("catalog.parquet") # a table: sorted, filtered and plotted on the server
26
+ ```
27
+
28
+ ## Install
29
+
30
+ ```
31
+ %pip install pixel-clarity
32
+ ```
33
+
34
+ Restart the kernel afterwards (or use a new one). It needs JupyterLab 4 or Notebook 7 with
35
+ ipywidgets 8, which most hubs have. On a JupyterHub where packages installed in the kernel's
36
+ environment don't survive a server restart, `%pip install --user pixel-clarity` puts it in your
37
+ home directory instead.
38
+
39
+ It brings its own Node.js (`nodejs-wheel-binaries`) for the process that reads the data; set
40
+ `PIXEL_CLARITY_NODE` to use another (version 20 or later).
41
+
42
+ ## How it connects
43
+
44
+ The viewer reaches its process the quickest way the page allows, shown by the dot under it:
45
+
46
+ - **Green, direct:** through [jupyter-server-proxy](https://github.com/jupyterhub/jupyter-server-proxy)
47
+ (installed on many hubs), or straight to the server when the browser runs on the same machine.
48
+ Tiles never wait for the kernel, so the view keeps working while a cell runs. *Open in a new tab*
49
+ shows the same view in a browser tab of its own.
50
+ - **Amber, through the kernel:** widget messages relayed by the kernel. It works anywhere widgets
51
+ do, but the view waits while a cell runs. `pc.show(..., connection="kernel")` asks for this.
52
+
53
+ ## What it reads
54
+
55
+ - Paths: FITS (including tile-compressed `.fits.fz`), ASDF (including lz4 blocks and gwcs), HDF5,
56
+ NumPy `.npy`/`.npz`, TIFF and PNG. A folder or a glob pattern shows its image files; a
57
+ `.pcmosaic` file (saved from a mosaic's Detectors panel) opens that mosaic again.
58
+ - `s3://` URIs: public buckets anonymously; `anon=False` signs the URL with the kernel's AWS
59
+ credentials (`pip install 'pixel-clarity[s3]'`). An open `s3fs` file works too.
60
+ - `http(s)://` URLs to servers that answer Range requests.
61
+ - From the kernel: NumPy arrays (masked arrays as NaN), astropy HDUs, HDU lists and NDData/CCDData
62
+ (with their WCS), ASDF files and data models. These are written once to a temporary file
63
+ on the server's local disk (an unchanged array is written only once), removed when the kernel
64
+ stops.
65
+
66
+ ## Settings
67
+
68
+ Keywords of `show` (and `Viewer`), named after the VS Code extension's settings; `pc.settings()`
69
+ lists them with their values and defaults. Set them for every viewer with
70
+ `pc.defaults["cmap"] = "viridis"`.
71
+
72
+ | Keyword | Values |
73
+ |---|---|
74
+ | `scale` | `linear`, `log`, `pow`, `sqrt`, `squared`, `asinh`, `sinh`, `histeq`, `mtf` |
75
+ | `limits` | `minmax`, `zscale`, `99.5`, `99`, `98`, `95`, `90` |
76
+ | `cmap` | `gray`, `viridis`, `magma`, `inferno`, `heat`, `cool`, … |
77
+ | `invert`, `auto` | `True`/`False` (`auto`: Auto enhance on opening) |
78
+ | `zoom` | `fit` or `1` |
79
+ | `sidebar`, `cuts` | show the side panel, the cut plots (`cuts` is off by default in a notebook) |
80
+ | `mosaic_layout`, `match_sky`, `flux_calibrate`, `feather`, `trim_edges` | mosaic placement and level matching |
81
+ | `online`, `survey` | SIMBAD and Aladin Lite (CDS) from the browser |
82
+
83
+ Other keywords: `height` (pixels; the grip under the viewer changes it), `kind` (`single`,
84
+ `mosaic`, `stack`, `rgb`), `title`, `names`, `layout`, `active`, `color`, `groups`, `levels`,
85
+ `state` (a view saved from `viewer.state`), `out_dir` (where exports, shared pages and saved
86
+ mosaics are written; the kernel's working directory by default).
87
+
88
+ ## In the viewer
89
+
90
+ Everything in the VS Code viewer's toolbar and panels works here. Files it writes (a
91
+ full-resolution export, a shared page, a saved mosaic) are written on the server, next to the
92
+ notebook, and named in the line under the viewer. *Save view as shown*, region files and color
93
+ settings download to your computer. Live reload follows changes to local files.
94
+
95
+ A viewer nobody has used for five minutes (thirty if it is on screen) frees the memory its data
96
+ took; it reads the data again when you come back to it.
97
+
98
+ ## Regions from and to Python
99
+
100
+ ```python
101
+ v = pc.show("image.fits")
102
+ v.add_regions(regions) # `regions` objects (pixel or sky), a DS9 .reg file's path, or DS9 text
103
+ v.regions # what is drawn on the view now, as `regions` pixel regions
104
+ v.regions[0].to_sky(wcs)
105
+ print(v.regions_ds9) # the same as DS9 region text (image coordinates), no extra package needed
106
+ ```
107
+
108
+ `v.regions` needs the `regions` package (`pip install 'pixel-clarity[regions]'`). It reads what any
109
+ viewer of `v` last saved, including one opened in a tab of its own, even while a cell runs.
110
+ Coordinates are the image's pixels (a stack's frames, an RGB set's red image); for a mosaic, those
111
+ of the mosaic's own plane. Regions given in sky coordinates are placed with the image's WCS; a
112
+ viewer still opening gets them once its image is shown. More than 2000 shapes show as a catalog
113
+ overlay rather than editable regions.
114
+
115
+ ## Tables
116
+
117
+ ```python
118
+ pc.table("catalog.parquet") # CSV, TSV, ECSV (also .gz) or Parquet: a path, an s3:// URI or a URL
119
+ pc.table(t) # an astropy, pandas or polars table (or a structured array) in the kernel
120
+ pc.show("catalog.csv") # show() opens table files in the table viewer too
121
+ pc.table("catalog.csv", plot=True) # with the plot showing
122
+ ```
123
+
124
+ A table stays on the server as an image does: sorting, filtering (`mag < 20 and class == 'STAR'`),
125
+ computed columns, column statistics and plots are worked out there, and the page receives the
126
+ rows on screen. A Parquet file that would take more than a quarter of the server's memory asks
127
+ which columns to load. Tables from the kernel are written to a temporary file: Parquet when
128
+ pyarrow is installed (units, masks and times kept), else ECSV (astropy) or CSV (pandas).
129
+
130
+ *Export* writes CSV, TSV, ECSV or a region file next to the notebook. *Overlay on image* draws the
131
+ rows on the image viewer used last, named in the line under the table. `t.state` holds the
132
+ filters, sort, columns and plot; pass it as `state=` to open another the same way.
133
+
134
+ ## Not yet
135
+
136
+ - Opening files from JupyterLab's file browser: for now, `pc.show()` them from a cell.
137
+
138
+ ## Development
139
+
140
+ The viewer's sources are the VS Code extension's repository; `npm run build` bundles them, with
141
+ this package's own parts from `js/`, into `src/pixel_clarity/static/`.
142
+
143
+ ```
144
+ npm install
145
+ PC_SRC=../vs_code_imageviewer npm run build # PC_SRC defaults to ../vs_code_imageviewer
146
+ npm run typecheck
147
+ uv sync
148
+ uv run pytest
149
+ node scripts/e2e.mjs [--remote=<url>] [--mosaic=<folder>] [--dark] [--aladin] [--no-proxy] # JupyterLab in headless Chromium
150
+ uv build # the wheel, with the built scripts in it
151
+ ```
152
+
153
+ To release, set the version in `src/pixel_clarity/_version.py`, commit, and push a tag `v<version>`:
154
+ `.github/workflows/pypi.yml` builds against the extension's sources at `UPSTREAM_REF`, runs the
155
+ tests and publishes to PyPI.
156
+
157
+ `notebooks/local_try.ipynb` walks through the viewer by hand on your own machine, with test
158
+ images it makes itself.
159
+
160
+ MIT licensed. The bundled third-party code is listed in `static/THIRD_PARTY_NOTICES.md`; Aladin
161
+ Lite (LGPL) is loaded from CDS when its panel opens, not bundled.
@@ -0,0 +1,67 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "pixel-clarity"
7
+ dynamic = ["version"]
8
+ description = "Pixel Clarity in Jupyter: a fast viewer for large astronomical images, mosaics, cubes and tables, with the data kept on the server"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE", "src/pixel_clarity/static/THIRD_PARTY_NOTICES.md"]
12
+ authors = [{ name = "Clarity Orbital" }]
13
+ requires-python = ">=3.10"
14
+ dependencies = [
15
+ "anywidget>=0.9",
16
+ "numpy",
17
+ # The viewer's backend runs on Node.js; this wheel brings one where the kernel has none.
18
+ "nodejs-wheel-binaries>=20",
19
+ ]
20
+ classifiers = [
21
+ "Framework :: Jupyter",
22
+ "Framework :: Jupyter :: JupyterLab",
23
+ "Intended Audience :: Science/Research",
24
+ "Topic :: Scientific/Engineering :: Astronomy",
25
+ "Topic :: Scientific/Engineering :: Visualization",
26
+ "Programming Language :: Python :: 3",
27
+ ]
28
+ keywords = ["astronomy", "fits", "asdf", "image viewer", "table viewer", "parquet", "jupyter", "widget"]
29
+
30
+ [project.optional-dependencies]
31
+ # Signed URLs for private S3 buckets (public buckets need nothing).
32
+ s3 = ["boto3"]
33
+ # viewer.regions as astropy regions.
34
+ regions = ["regions"]
35
+
36
+ [project.urls]
37
+ Homepage = "https://clarityorbital.com"
38
+
39
+ [dependency-groups]
40
+ dev = [
41
+ "pytest>=8",
42
+ "astropy>=6",
43
+ "jupyterlab>=4",
44
+ "jupyter-server-proxy>=4",
45
+ "ipykernel",
46
+ "nbformat",
47
+ "nbclient",
48
+ "regions>=0.10",
49
+ "h5py>=3.16.0",
50
+ "pandas>=2.3.3",
51
+ "pyarrow>=25.0.1",
52
+ ]
53
+
54
+ [tool.hatch.version]
55
+ path = "src/pixel_clarity/_version.py"
56
+
57
+ [tool.hatch.build.targets.wheel]
58
+ packages = ["src/pixel_clarity"]
59
+ # Built by `npm run build` (gitignored), so listed explicitly.
60
+ artifacts = ["src/pixel_clarity/static/*"]
61
+
62
+ [tool.hatch.build.targets.sdist]
63
+ include = ["src/pixel_clarity", "README.md", "LICENSE", "CHANGELOG.md"]
64
+ artifacts = ["src/pixel_clarity/static/*"]
65
+
66
+ [tool.pytest.ini_options]
67
+ testpaths = ["tests"]
@@ -0,0 +1,52 @@
1
+ """Pixel Clarity in Jupyter: a fast viewer for large astronomical images, mosaics, cubes and tables.
2
+
3
+ import pixel_clarity as pc
4
+ pc.show("image.fits") # a file on the server
5
+ pc.show("s3://bucket/path/image.asdf") # read where it is, by range requests
6
+ pc.show(array) # an array in the kernel
7
+ pc.mosaic("exposure_folder/") # detectors placed on the sky
8
+ pc.rgb(r, g, b)
9
+ pc.table("catalog.parquet") # a table: sort, filter, plot on the server
10
+
11
+ The data stays on the server: a backend process next to the kernel reads it and the browser
12
+ receives only the tiles and values it shows.
13
+ """
14
+
15
+ from ._backend import backend_log
16
+ from ._settings import describe as _describe
17
+ from ._version import __version__
18
+ from .viewer import Viewer, defaults, mosaic, rgb, show, stack, table
19
+
20
+ __all__ = ["Viewer", "show", "rgb", "mosaic", "stack", "table", "defaults", "settings", "diagnostics", "backend_log", "__version__"]
21
+
22
+
23
+ def settings() -> None:
24
+ """Print the settings `show` takes, with their allowed values and defaults."""
25
+ print(_describe())
26
+
27
+
28
+ def diagnostics() -> dict:
29
+ """What the viewer's process sees here: Node, CPU and memory limits, the temporary folder."""
30
+ import os
31
+ import shutil
32
+ import subprocess
33
+
34
+ from ._backend import Backend
35
+ from ._node import find_node
36
+ from ._sources import temp_dir
37
+
38
+ node = find_node()
39
+ b = Backend.get()
40
+ tmp = temp_dir()
41
+ return {
42
+ "pixel_clarity": __version__,
43
+ "node": node,
44
+ "node_version": subprocess.run([node, "--version"], capture_output=True, text=True).stdout.strip(),
45
+ "cpus": b.info.get("cpus"),
46
+ "memory_gb": round(b.info.get("memory", 0) / 1e9, 1),
47
+ "pool_workers": b.info.get("poolWorkers"),
48
+ "backend_port": b.port,
49
+ "jupyterhub_prefix": os.environ.get("JUPYTERHUB_SERVICE_PREFIX"),
50
+ "temp_dir": str(tmp),
51
+ "temp_free_gb": round(shutil.disk_usage(tmp).free / 1e9, 1),
52
+ }