viewtif 0.1.5__tar.gz → 0.1.6__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.
@@ -0,0 +1,8 @@
1
+ # virtual environments
2
+ .venv
3
+ .venv-test
4
+
5
+ # build artifacts
6
+ dist/
7
+ build/
8
+ *.egg-info/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: viewtif
3
- Version: 0.1.5
3
+ Version: 0.1.6
4
4
  Summary: Simple GeoTIFF viewer with optional shapefile overlay.
5
5
  Project-URL: Homepage, https://github.com/nkeikon/tifviewer
6
6
  Project-URL: Source, https://github.com/nkeikon/tifviewer
@@ -19,6 +19,9 @@ Requires-Dist: shapely>=2.0; extra == 'geo'
19
19
  Description-Content-Type: text/markdown
20
20
 
21
21
  # viewtif
22
+ [![Downloads](https://static.pepy.tech/badge/viewtif)](https://pepy.tech/project/viewtif)
23
+ [![PyPI version](https://img.shields.io/pypi/v/viewtif)](https://pypi.org/project/viewtif/)
24
+ [![Python versions](https://img.shields.io/pypi/pyversions/viewtif)](https://pypi.org/project/viewtif/)
22
25
 
23
26
  A lightweight GeoTIFF viewer for quick visualization directly from the command line.
24
27
 
@@ -31,35 +34,61 @@ You can visualize single-band GeoTIFFs, RGB composites, and shapefile overlays i
31
34
  ```bash
32
35
  pip install viewtif
33
36
  ```
37
+ > **Note:** On Linux, you may need python3-tk, libqt5gui5, or PySide6 dependencies.
38
+ >
39
+ >`viewtif` requires a graphical display environment.
40
+ > It may not run properly on headless systems (e.g., HPC compute nodes or remote servers without X11 forwarding).
34
41
 
35
- If you want to enable shapefile overlays, install with optional dependencies:
42
+ ### Optional features
43
+ #### Shapefile overlay support
36
44
  ```bash
37
45
  pip install "viewtif[geo]"
38
46
  ```
39
- Note for macOS(zsh) users:
40
- Make sure to include the quotes, or zsh will interpret it as a pattern.
47
+ > **Note:** For macOS(zsh) users:
48
+ > Make sure to include the quotes, or zsh will interpret it as a pattern.
49
+
50
+ #### HDF/HDF5 support
51
+ ```bash
52
+ brew install gdal # macOS
53
+ sudo apt install gdal-bin python3-gdal # Linux
54
+ pip install GDAL
55
+ ```
56
+ > **Note:** GDAL is required to open `.hdf`, .`h5`, and `.hdf5` files. If it’s missing, viewtif will display: `RuntimeError: HDF support requires GDAL.`
41
57
 
42
58
  ## Quick Start
43
59
  ```bash
44
60
  # View a GeoTIFF
45
- viewtif examples/sample_data/ECOSTRESS_LST.tif
46
-
47
- # View with shapefile overlay
48
- viewtif examples/sample_data/ECOSTRESS_LST.tif \
49
- --shapefile examples/sample_data/Zip_Codes.shp
61
+ viewtif ECOSTRESS_LST.tif
50
62
 
51
63
  # View an RGB composite
52
64
  viewtif --rgbfiles \
53
- examples/sample_data/HLS_B04.tif \
54
- examples/sample_data/HLS_B03.tif \
55
- examples/sample_data/HLS_B02.tif
65
+ HLS_B04.tif \
66
+ HLS_B03.tif \
67
+ HLS_B02.tif
68
+
69
+ # View with shapefile overlay
70
+ viewtif ECOSTRESS_LST.tif \
71
+ --shapefile Zip_Codes.shp
72
+ ```
73
+ ### Update in v1.0.6
74
+ `viewtif` can open `.hdf`, `.h5`, and `.hdf5` files that contain multiple subdatasets. When opened, it lists available subdatasets and lets you view one by index. You can also specify a band to display (default is the first band) or change bands interactively with '[' and ']'.
75
+ ```bash
76
+ # List subdatasets
77
+ viewtif AG100.v003.33.-107.0001.h5
78
+
79
+ # View a specific subdataset
80
+ viewtif AG100.v003.33.-107.0001.h5 --subset 1
56
81
 
82
+ # View a specific subdataset and band
83
+ viewtif AG100.v003.33.-107.0001.h5 --subset 1 --band 3
57
84
  ```
85
+ > **Note:** Some datasets (perhaps the majority of .hdf files) lack CRS information encoded, so shapefile overlays may not work. In that case, viewtif will display:
86
+ `[WARN] raster lacks CRS/transform; cannot place overlays.`
58
87
 
59
88
  ## Controls
60
89
  | Key | Action |
61
90
  | -------------------- | --------------------------------------- |
62
- | `+` / `-` | Zoom in / out |
91
+ | `+` / `-` or mouse / trackpad | Zoom in / out |
63
92
  | Arrow keys or `WASD` | Pan |
64
93
  | `C` / `V` | Increase / decrease contrast |
65
94
  | `G` / `H` | Increase / decrease gamma |
@@ -68,8 +97,8 @@ viewtif --rgbfiles \
68
97
  | `R` | Reset view |
69
98
 
70
99
  ## Features
71
- - Command-line driven GeoTIFF viewer
72
- - Supports single-band or RGB composite display.
100
+ - Command-line driven GeoTIFF viewer.
101
+ - Supports single-band, RGB composite, and HDF/HDF5 subdatasets.
73
102
  - Optional shapefile overlay for geographic context.
74
103
  - Adjustable contrast, gamma, and colormap.
75
104
  - Fast preview using rasterio and PySide6.
@@ -78,14 +107,15 @@ viewtif --rgbfiles \
78
107
  - ECOSTRESS_LST.tif
79
108
  - Zip_Codes.shp and associated files
80
109
  - HLS_B04.tif, HLS_B03.tif, HLS_B02.tif (RGB sample)
110
+ - AG100.v003.33.-107.0001.h5 (HDF5 file)
81
111
 
82
112
  ## Credit & License
83
113
  `viewtif` was inspired by the NASA JPL Thermal Viewer — Semi-Automated Georeferencer (GeoViewer v1.12) developed by Jake Longenecker (University of Miami Rosenstiel School of Marine, Atmospheric & Earth Science) while at the NASA Jet Propulsion Laboratory, California Institute of Technology, with inspiration from JPL’s ECOSTRESS geolocation batch workflow by Andrew Alamillo. The original GeoViewer was released under the MIT License (2025) and may be freely adapted with citation.
84
114
 
85
- # Citation
86
- Longenecker, Jake; Lee, Christine; Hulley, Glynn; Cawse-Nicholson, Kerry; Purkis, Sam; Gleason, Art; Otis, Dan; Galdamez,Ileana; Meiseles, Jacquelyn. GeoViewer v1.12: NASA JPL Thermal Viewer—Semi-Automated Georeferencer User Guide & Reference Manual. Jet Propulsion Laboratory, California Institute of Technology, 2025. PDF.
115
+ ## Citation
116
+ Longenecker, Jake; Lee, Christine; Hulley, Glynn; Cawse-Nicholson, Kerry; Purkis, Sam; Gleason, Art; Otis, Dan; Galdamez, Ileana; Meiseles, Jacquelyn. GeoViewer v1.12: NASA JPL Thermal Viewer—Semi-Automated Georeferencer User Guide & Reference Manual. Jet Propulsion Laboratory, California Institute of Technology, 2025. PDF.
87
117
 
88
- # License
118
+ ## License
89
119
  This project is released under the MIT License.
90
120
 
91
121
  ## Contributors
@@ -0,0 +1,103 @@
1
+ # viewtif
2
+ [![Downloads](https://static.pepy.tech/badge/viewtif)](https://pepy.tech/project/viewtif)
3
+ [![PyPI version](https://img.shields.io/pypi/v/viewtif)](https://pypi.org/project/viewtif/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/viewtif)](https://pypi.org/project/viewtif/)
5
+
6
+ A lightweight GeoTIFF viewer for quick visualization directly from the command line.
7
+
8
+ You can visualize single-band GeoTIFFs, RGB composites, and shapefile overlays in a simple Qt-based window.
9
+
10
+ ---
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ pip install viewtif
16
+ ```
17
+ > **Note:** On Linux, you may need python3-tk, libqt5gui5, or PySide6 dependencies.
18
+ >
19
+ >`viewtif` requires a graphical display environment.
20
+ > It may not run properly on headless systems (e.g., HPC compute nodes or remote servers without X11 forwarding).
21
+
22
+ ### Optional features
23
+ #### Shapefile overlay support
24
+ ```bash
25
+ pip install "viewtif[geo]"
26
+ ```
27
+ > **Note:** For macOS(zsh) users:
28
+ > Make sure to include the quotes, or zsh will interpret it as a pattern.
29
+
30
+ #### HDF/HDF5 support
31
+ ```bash
32
+ brew install gdal # macOS
33
+ sudo apt install gdal-bin python3-gdal # Linux
34
+ pip install GDAL
35
+ ```
36
+ > **Note:** GDAL is required to open `.hdf`, .`h5`, and `.hdf5` files. If it’s missing, viewtif will display: `RuntimeError: HDF support requires GDAL.`
37
+
38
+ ## Quick Start
39
+ ```bash
40
+ # View a GeoTIFF
41
+ viewtif ECOSTRESS_LST.tif
42
+
43
+ # View an RGB composite
44
+ viewtif --rgbfiles \
45
+ HLS_B04.tif \
46
+ HLS_B03.tif \
47
+ HLS_B02.tif
48
+
49
+ # View with shapefile overlay
50
+ viewtif ECOSTRESS_LST.tif \
51
+ --shapefile Zip_Codes.shp
52
+ ```
53
+ ### Update in v1.0.6
54
+ `viewtif` can open `.hdf`, `.h5`, and `.hdf5` files that contain multiple subdatasets. When opened, it lists available subdatasets and lets you view one by index. You can also specify a band to display (default is the first band) or change bands interactively with '[' and ']'.
55
+ ```bash
56
+ # List subdatasets
57
+ viewtif AG100.v003.33.-107.0001.h5
58
+
59
+ # View a specific subdataset
60
+ viewtif AG100.v003.33.-107.0001.h5 --subset 1
61
+
62
+ # View a specific subdataset and band
63
+ viewtif AG100.v003.33.-107.0001.h5 --subset 1 --band 3
64
+ ```
65
+ > **Note:** Some datasets (perhaps the majority of .hdf files) lack CRS information encoded, so shapefile overlays may not work. In that case, viewtif will display:
66
+ `[WARN] raster lacks CRS/transform; cannot place overlays.`
67
+
68
+ ## Controls
69
+ | Key | Action |
70
+ | -------------------- | --------------------------------------- |
71
+ | `+` / `-` or mouse / trackpad | Zoom in / out |
72
+ | Arrow keys or `WASD` | Pan |
73
+ | `C` / `V` | Increase / decrease contrast |
74
+ | `G` / `H` | Increase / decrease gamma |
75
+ | `M` | Toggle colormap (`viridis` ↔ `magma`) |
76
+ | `[` / `]` | Previous / next band (single-band only) |
77
+ | `R` | Reset view |
78
+
79
+ ## Features
80
+ - Command-line driven GeoTIFF viewer.
81
+ - Supports single-band, RGB composite, and HDF/HDF5 subdatasets.
82
+ - Optional shapefile overlay for geographic context.
83
+ - Adjustable contrast, gamma, and colormap.
84
+ - Fast preview using rasterio and PySide6.
85
+
86
+ ## Example Data
87
+ - ECOSTRESS_LST.tif
88
+ - Zip_Codes.shp and associated files
89
+ - HLS_B04.tif, HLS_B03.tif, HLS_B02.tif (RGB sample)
90
+ - AG100.v003.33.-107.0001.h5 (HDF5 file)
91
+
92
+ ## Credit & License
93
+ `viewtif` was inspired by the NASA JPL Thermal Viewer — Semi-Automated Georeferencer (GeoViewer v1.12) developed by Jake Longenecker (University of Miami Rosenstiel School of Marine, Atmospheric & Earth Science) while at the NASA Jet Propulsion Laboratory, California Institute of Technology, with inspiration from JPL’s ECOSTRESS geolocation batch workflow by Andrew Alamillo. The original GeoViewer was released under the MIT License (2025) and may be freely adapted with citation.
94
+
95
+ ## Citation
96
+ Longenecker, Jake; Lee, Christine; Hulley, Glynn; Cawse-Nicholson, Kerry; Purkis, Sam; Gleason, Art; Otis, Dan; Galdamez, Ileana; Meiseles, Jacquelyn. GeoViewer v1.12: NASA JPL Thermal Viewer—Semi-Automated Georeferencer User Guide & Reference Manual. Jet Propulsion Laboratory, California Institute of Technology, 2025. PDF.
97
+
98
+ ## License
99
+ This project is released under the MIT License.
100
+
101
+ ## Contributors
102
+ - [@HarshShinde0](https://github.com/HarshShinde0) — added mouse-wheel and trackpad zoom support
103
+
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "viewtif"
7
- version = "0.1.5"
7
+ version = "0.1.6"
8
8
  description = "Simple GeoTIFF viewer with optional shapefile overlay."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -56,6 +56,7 @@ except Exception:
56
56
  # -------------------------- QGraphicsView tweaks -------------------------- #
57
57
  class RasterView(QGraphicsView):
58
58
  def __init__(self, *args, **kwargs):
59
+ import numpy as np
59
60
  super().__init__(*args, **kwargs)
60
61
  self.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform, False)
61
62
  self.setDragMode(QGraphicsView.DragMode.ScrollHandDrag)
@@ -101,6 +102,7 @@ class TiffViewer(QMainWindow):
101
102
  shapefiles: list[str] | None = None,
102
103
  shp_color: str = "white",
103
104
  shp_width: float = 2,
105
+ subset: int | None = None,
104
106
  ):
105
107
  super().__init__()
106
108
 
@@ -141,38 +143,118 @@ class TiffViewer(QMainWindow):
141
143
  self.tif_path = self.tif_path or (os.path.commonprefix([red, green, blue]) or red)
142
144
 
143
145
  elif tif_path:
144
- with rasterio.open(tif_path) as src:
145
- self._transform = src.transform
146
- self._crs = src.crs
147
- if rgb is not None:
148
- bands = [src.read(b, out_shape=(src.height // self._scale_arg, src.width // self._scale_arg))
149
- for b in rgb]
150
- arr = np.stack(bands, axis=-1).astype(np.float32)
151
- nd = src.nodata
152
- if nd is not None:
153
- arr[arr == nd] = np.nan
146
+ # --------------------- Detect HDF/HDF5 --------------------- #
147
+ if tif_path.lower().endswith((".hdf", ".h5", ".hdf5")):
148
+ try:
149
+ from osgeo import gdal
150
+ gdal.UseExceptions()
151
+
152
+ ds = gdal.Open(tif_path)
153
+ subs = ds.GetSubDatasets()
154
+
155
+ if not subs:
156
+ raise ValueError("No subdatasets found in HDF/HDF5 file.")
157
+
158
+ print(f"Found {len(subs)} subdatasets in {os.path.basename(tif_path)}:")
159
+ for i, (_, desc) in enumerate(subs):
160
+ print(f"[{i}] {desc}")
161
+
162
+ # Only list subsets if --subset not given
163
+ if subset is None:
164
+ print("\nUse --subset N to open a specific subdataset.")
165
+ sys.exit(0)
166
+
167
+ # Validate subset index
168
+ if subset < 0 or subset >= len(subs):
169
+ raise ValueError(f"Invalid subset index {subset}. Valid range: 0–{len(subs)-1}")
170
+
171
+ sub_name, desc = subs[subset]
172
+ print(f"\nOpening subdataset [{subset}]: {desc}")
173
+ sub_ds = gdal.Open(sub_name)
174
+
175
+ # --- Read once ---
176
+ arr = sub_ds.ReadAsArray().astype(np.float32)
177
+ #print(f"Raw array shape from GDAL: {arr.shape} (ndim={arr.ndim})")
178
+
179
+ # --- Normalize shape ---
180
+ arr = np.squeeze(arr)
181
+ if arr.ndim == 3:
182
+ # Convert from (bands, rows, cols) → (rows, cols, bands)
183
+ arr = np.transpose(arr, (1, 2, 0))
184
+ #print(f"Transposed to {arr.shape} (rows, cols, bands)")
185
+ elif arr.ndim == 2:
186
+ print("Single-band dataset.")
187
+ else:
188
+ raise ValueError(f"Unexpected array shape {arr.shape}")
189
+
190
+ # --- Downsample large arrays for responsiveness ---
191
+ h, w = arr.shape[:2]
192
+ if h * w > 4_000_000:
193
+ step = max(2, int((h * w / 4_000_000) ** 0.5))
194
+ arr = arr[::step, ::step] if arr.ndim == 2 else arr[::step, ::step, :]
195
+ print(f"⚠️ Large dataset preview: downsampled by {step}x")
196
+
197
+ # --- Final assignments ---
154
198
  self.data = arr
155
- self.band_count = 3
156
- else:
157
- arr = src.read(
158
- self.band,
159
- out_shape=(src.height // self._scale_arg, src.width // self._scale_arg)
160
- ).astype(np.float32)
161
- nd = src.nodata
162
- if nd is not None:
163
- arr[arr == nd] = np.nan
164
- self.data = arr
165
- self.band_count = src.count
166
-
167
- # single-band display range (fast stats or fallback)
168
- try:
169
- stats = src.stats(self.band)
170
- if stats and stats.min is not None and stats.max is not None:
171
- self.vmin, self.vmax = stats.min, stats.max
172
- else:
173
- raise ValueError("No stats in file")
174
- except Exception:
175
- self.vmin, self.vmax = np.nanmin(arr), np.nanmax(arr)
199
+ self._transform = None
200
+ self._crs = None
201
+ self.band_count = arr.shape[2] if arr.ndim == 3 else 1
202
+ self.band_index = 0
203
+ self.vmin, self.vmax = np.nanmin(arr), np.nanmax(arr)
204
+
205
+ if self.band_count > 1:
206
+ print(f"This subdataset has {self.band_count} bands — switch with [ and ] keys.")
207
+ else:
208
+ print("This subdataset has 1 band.")
209
+
210
+ # --- If user specified --band, start there ---
211
+ if self.band and self.band <= self.band_count:
212
+ self.band_index = self.band - 1
213
+ print(f"Opening band {self.band}/{self.band_count}")
214
+ else:
215
+ self.band_index = 0
216
+
217
+ except ImportError:
218
+ raise RuntimeError(
219
+ "HDF support requires GDAL.\n"
220
+ "Install it first (e.g., brew install gdal && pip install GDAL)"
221
+ )
222
+
223
+ # --------------------- Regular GeoTIFF --------------------- #
224
+ else:
225
+ with rasterio.open(tif_path) as src:
226
+ self._transform = src.transform
227
+ self._crs = src.crs
228
+ if rgb is not None:
229
+ bands = [src.read(b, out_shape=(src.height // self._scale_arg, src.width // self._scale_arg))
230
+ for b in rgb]
231
+ arr = np.stack(bands, axis=-1).astype(np.float32)
232
+ nd = src.nodata
233
+ if nd is not None:
234
+ arr[arr == nd] = np.nan
235
+ self.data = arr
236
+ self.band_count = 3
237
+ else:
238
+ arr = src.read(
239
+ self.band,
240
+ out_shape=(src.height // self._scale_arg, src.width // self._scale_arg)
241
+ ).astype(np.float32)
242
+ nd = src.nodata
243
+ if nd is not None:
244
+ arr[arr == nd] = np.nan
245
+ self.data = arr
246
+ self.band_count = src.count
247
+
248
+ # single-band display range (fast stats or fallback)
249
+ try:
250
+ stats = src.stats(self.band)
251
+ if stats and stats.min is not None and stats.max is not None:
252
+ self.vmin, self.vmax = stats.min, stats.max
253
+ else:
254
+ raise ValueError("No stats in file")
255
+ except Exception:
256
+ self.vmin, self.vmax = np.nanmin(arr), np.nanmax(arr)
257
+
176
258
  else:
177
259
  raise ValueError("Provide a TIFF path or --rgbfiles.")
178
260
 
@@ -339,6 +421,10 @@ class TiffViewer(QMainWindow):
339
421
  self.setWindowTitle(f"RGB ({', '.join(names)})")
340
422
  elif self.rgb_mode and self.rgb:
341
423
  self.setWindowTitle(f"RGB {self.rgb} — {os.path.basename(self.tif_path)}")
424
+ elif hasattr(self, "band_index"):
425
+ self.setWindowTitle(
426
+ f"Band {self.band_index + 1}/{self.band_count} — {os.path.basename(self.tif_path)}"
427
+ )
342
428
  else:
343
429
  self.setWindowTitle(f"Band {self.band}/{self.band_count} — {os.path.basename(self.tif_path)}")
344
430
 
@@ -371,10 +457,44 @@ class TiffViewer(QMainWindow):
371
457
  return rgb
372
458
 
373
459
  def update_pixmap(self):
374
- rgb = self._render_rgb()
460
+ # --- Select display data ---
461
+ if hasattr(self, "band_index"):
462
+ # HDF or scientific multi-band
463
+ if self.data.ndim == 3:
464
+ a = self.data[:, :, self.band_index]
465
+ else:
466
+ a = self.data
467
+ rgb = None
468
+ else:
469
+ # Regular GeoTIFF (could be RGB or single-band)
470
+ if self.rgb_mode: # user explicitly passed --rgb or --rgbtiles
471
+ rgb = self.data
472
+ a = None
473
+ else:
474
+ a = self.data
475
+ rgb = None
476
+ # ----------------------------
477
+
478
+ # --- Render image ---
479
+ if rgb is None:
480
+ # Grayscale rendering for single-band (scientific) data
481
+ finite = np.isfinite(a)
482
+ rng = max(np.nanmax(a) - np.nanmin(a), 1e-12)
483
+ norm = np.zeros_like(a, dtype=np.float32)
484
+ if np.any(finite):
485
+ norm[finite] = (a[finite] - np.nanmin(a)) / rng
486
+ norm = np.clip(norm, 0, 1)
487
+ norm = np.power(norm * self.contrast, self.gamma)
488
+ cmap = getattr(cm, self.cmap_name, cm.viridis)
489
+ rgb = (cmap(norm)[..., :3] * 255).astype(np.uint8)
490
+ else:
491
+ # True RGB mode (unchanged)
492
+ rgb = self._render_rgb()
493
+ # ----------------------
494
+
375
495
  h, w, _ = rgb.shape
376
496
  self._last_rgb = rgb
377
- qimg = QImage(self._last_rgb.data, w, h, 3 * w, QImage.Format.Format_RGB888)
497
+ qimg = QImage(rgb.data, w, h, 3 * w, QImage.Format.Format_RGB888)
378
498
  pix = QPixmap.fromImage(qimg)
379
499
  if self.pixmap_item is None:
380
500
  self.pixmap_item = QGraphicsPixmapItem(pix)
@@ -433,13 +553,24 @@ class TiffViewer(QMainWindow):
433
553
  self.cmap_name, self.alt_cmap_name = self.alt_cmap_name, self.cmap_name
434
554
  self.update_pixmap()
435
555
 
436
- # Band switch (single-band)
437
- elif not self.rgb_mode and k == Qt.Key.Key_BracketRight:
438
- new_band = self.band + 1 if self.band < self.band_count else 1
439
- self.load_band(new_band)
440
- elif not self.rgb_mode and k == Qt.Key.Key_BracketLeft:
441
- new_band = self.band - 1 if self.band > 1 else self.band_count
442
- self.load_band(new_band)
556
+ # Band switch
557
+ elif k == Qt.Key.Key_BracketRight:
558
+ if hasattr(self, "band_index"): # HDF/NetCDF mode
559
+ self.band_index = (self.band_index + 1) % self.band_count
560
+ self.update_pixmap()
561
+ self.update_title()
562
+ elif not self.rgb_mode: # GeoTIFF single-band mode
563
+ new_band = self.band + 1 if self.band < self.band_count else 1
564
+ self.load_band(new_band)
565
+
566
+ elif k == Qt.Key.Key_BracketLeft:
567
+ if hasattr(self, "band_index"): # HDF/NetCDF mode
568
+ self.band_index = (self.band_index - 1) % self.band_count
569
+ self.update_pixmap()
570
+ self.update_title()
571
+ elif not self.rgb_mode: # GeoTIFF single-band mode
572
+ new_band = self.band - 1 if self.band > 1 else self.band_count
573
+ self.load_band(new_band)
443
574
 
444
575
  elif k == Qt.Key.Key_R:
445
576
  self.contrast = 1.0
@@ -488,6 +619,7 @@ def run_viewer(
488
619
  shapefile=None,
489
620
  shp_color=None,
490
621
  shp_width=None,
622
+ subset=None,
491
623
  ):
492
624
  """Launch the TiffViewer app"""
493
625
  app = QApplication(sys.argv)
@@ -500,6 +632,7 @@ def run_viewer(
500
632
  shapefiles=shapefile,
501
633
  shp_color=shp_color,
502
634
  shp_width=shp_width,
635
+ subset=subset,
503
636
  )
504
637
  win.show()
505
638
  sys.exit(app.exec())
@@ -507,6 +640,7 @@ def run_viewer(
507
640
  import click
508
641
 
509
642
  @click.command()
643
+ @click.version_option("1.0.6", prog_name="viewtif")
510
644
  @click.argument("tif_path", required=False)
511
645
  @click.option("--band", default=1, show_default=True, type=int, help="Band number to display")
512
646
  @click.option("--scale", default=1.0, show_default=True, type=float, help="Scale factor for display")
@@ -515,9 +649,9 @@ import click
515
649
  @click.option("--shapefile", multiple=True, type=str, help="One or more shapefiles to overlay")
516
650
  @click.option("--shp-color", default="white", show_default=True, help="Overlay color (name or #RRGGBB).")
517
651
  @click.option("--shp-width", default=1.0, show_default=True, type=float, help="Overlay line width (screen pixels).")
518
- def main(tif_path, band, scale, rgb, rgbfiles, shapefile, shp_color, shp_width):
652
+ @click.option("--subset", default=None, type=int, help="Open specific subdataset index in .hdf/.h5 file")
653
+ def main(tif_path, band, scale, rgb, rgbfiles, shapefile, shp_color, shp_width, subset):
519
654
  """Lightweight GeoTIFF viewer."""
520
-
521
655
  # --- Warn early if shapefile requested but geopandas missing ---
522
656
  if shapefile and not HAVE_GEO:
523
657
  print(
@@ -535,9 +669,9 @@ def main(tif_path, band, scale, rgb, rgbfiles, shapefile, shp_color, shp_width):
535
669
  shapefile=shapefile,
536
670
  shp_color=shp_color,
537
671
  shp_width=shp_width,
672
+ subset=subset,
538
673
  )
539
674
 
540
-
541
675
  if __name__ == "__main__":
542
676
  main()
543
677
 
viewtif-0.1.5/.gitignore DELETED
@@ -1,4 +0,0 @@
1
- .venv/
2
- dist/
3
- __pycache__/
4
- *.pyc
viewtif-0.1.5/README.md DELETED
@@ -1,73 +0,0 @@
1
- # viewtif
2
-
3
- A lightweight GeoTIFF viewer for quick visualization directly from the command line.
4
-
5
- You can visualize single-band GeoTIFFs, RGB composites, and shapefile overlays in a simple Qt-based window.
6
-
7
- ---
8
-
9
- ## Installation
10
-
11
- ```bash
12
- pip install viewtif
13
- ```
14
-
15
- If you want to enable shapefile overlays, install with optional dependencies:
16
- ```bash
17
- pip install "viewtif[geo]"
18
- ```
19
- Note for macOS(zsh) users:
20
- Make sure to include the quotes, or zsh will interpret it as a pattern.
21
-
22
- ## Quick Start
23
- ```bash
24
- # View a GeoTIFF
25
- viewtif examples/sample_data/ECOSTRESS_LST.tif
26
-
27
- # View with shapefile overlay
28
- viewtif examples/sample_data/ECOSTRESS_LST.tif \
29
- --shapefile examples/sample_data/Zip_Codes.shp
30
-
31
- # View an RGB composite
32
- viewtif --rgbfiles \
33
- examples/sample_data/HLS_B04.tif \
34
- examples/sample_data/HLS_B03.tif \
35
- examples/sample_data/HLS_B02.tif
36
-
37
- ```
38
-
39
- ## Controls
40
- | Key | Action |
41
- | -------------------- | --------------------------------------- |
42
- | `+` / `-` | Zoom in / out |
43
- | Arrow keys or `WASD` | Pan |
44
- | `C` / `V` | Increase / decrease contrast |
45
- | `G` / `H` | Increase / decrease gamma |
46
- | `M` | Toggle colormap (`viridis` ↔ `magma`) |
47
- | `[` / `]` | Previous / next band (single-band only) |
48
- | `R` | Reset view |
49
-
50
- ## Features
51
- - Command-line driven GeoTIFF viewer
52
- - Supports single-band or RGB composite display.
53
- - Optional shapefile overlay for geographic context.
54
- - Adjustable contrast, gamma, and colormap.
55
- - Fast preview using rasterio and PySide6.
56
-
57
- ## Example Data
58
- - ECOSTRESS_LST.tif
59
- - Zip_Codes.shp and associated files
60
- - HLS_B04.tif, HLS_B03.tif, HLS_B02.tif (RGB sample)
61
-
62
- ## Credit & License
63
- `viewtif` was inspired by the NASA JPL Thermal Viewer — Semi-Automated Georeferencer (GeoViewer v1.12) developed by Jake Longenecker (University of Miami Rosenstiel School of Marine, Atmospheric & Earth Science) while at the NASA Jet Propulsion Laboratory, California Institute of Technology, with inspiration from JPL’s ECOSTRESS geolocation batch workflow by Andrew Alamillo. The original GeoViewer was released under the MIT License (2025) and may be freely adapted with citation.
64
-
65
- # Citation
66
- Longenecker, Jake; Lee, Christine; Hulley, Glynn; Cawse-Nicholson, Kerry; Purkis, Sam; Gleason, Art; Otis, Dan; Galdamez,Ileana; Meiseles, Jacquelyn. GeoViewer v1.12: NASA JPL Thermal Viewer—Semi-Automated Georeferencer User Guide & Reference Manual. Jet Propulsion Laboratory, California Institute of Technology, 2025. PDF.
67
-
68
- # License
69
- This project is released under the MIT License.
70
-
71
- ## Contributors
72
- - [@HarshShinde0](https://github.com/HarshShinde0) — added mouse-wheel and trackpad zoom support
73
-
File without changes