viewtif 0.1.4__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/
viewtif-0.1.6/PKG-INFO ADDED
@@ -0,0 +1,123 @@
1
+ Metadata-Version: 2.4
2
+ Name: viewtif
3
+ Version: 0.1.6
4
+ Summary: Simple GeoTIFF viewer with optional shapefile overlay.
5
+ Project-URL: Homepage, https://github.com/nkeikon/tifviewer
6
+ Project-URL: Source, https://github.com/nkeikon/tifviewer
7
+ Project-URL: Issues, https://github.com/nkeikon/tifviewer/issues
8
+ Author: Keiko Nomura
9
+ License: MIT
10
+ Requires-Python: >=3.9
11
+ Requires-Dist: click>=8.1
12
+ Requires-Dist: matplotlib>=3.7
13
+ Requires-Dist: numpy>=1.23
14
+ Requires-Dist: pyside6>=6.5
15
+ Requires-Dist: rasterio>=1.3
16
+ Provides-Extra: geo
17
+ Requires-Dist: geopandas>=0.13; extra == 'geo'
18
+ Requires-Dist: shapely>=2.0; extra == 'geo'
19
+ Description-Content-Type: text/markdown
20
+
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/)
25
+
26
+ A lightweight GeoTIFF viewer for quick visualization directly from the command line.
27
+
28
+ You can visualize single-band GeoTIFFs, RGB composites, and shapefile overlays in a simple Qt-based window.
29
+
30
+ ---
31
+
32
+ ## Installation
33
+
34
+ ```bash
35
+ pip install viewtif
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).
41
+
42
+ ### Optional features
43
+ #### Shapefile overlay support
44
+ ```bash
45
+ pip install "viewtif[geo]"
46
+ ```
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.`
57
+
58
+ ## Quick Start
59
+ ```bash
60
+ # View a GeoTIFF
61
+ viewtif ECOSTRESS_LST.tif
62
+
63
+ # View an RGB composite
64
+ viewtif --rgbfiles \
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
81
+
82
+ # View a specific subdataset and band
83
+ viewtif AG100.v003.33.-107.0001.h5 --subset 1 --band 3
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.`
87
+
88
+ ## Controls
89
+ | Key | Action |
90
+ | -------------------- | --------------------------------------- |
91
+ | `+` / `-` or mouse / trackpad | Zoom in / out |
92
+ | Arrow keys or `WASD` | Pan |
93
+ | `C` / `V` | Increase / decrease contrast |
94
+ | `G` / `H` | Increase / decrease gamma |
95
+ | `M` | Toggle colormap (`viridis` ↔ `magma`) |
96
+ | `[` / `]` | Previous / next band (single-band only) |
97
+ | `R` | Reset view |
98
+
99
+ ## Features
100
+ - Command-line driven GeoTIFF viewer.
101
+ - Supports single-band, RGB composite, and HDF/HDF5 subdatasets.
102
+ - Optional shapefile overlay for geographic context.
103
+ - Adjustable contrast, gamma, and colormap.
104
+ - Fast preview using rasterio and PySide6.
105
+
106
+ ## Example Data
107
+ - ECOSTRESS_LST.tif
108
+ - Zip_Codes.shp and associated files
109
+ - HLS_B04.tif, HLS_B03.tif, HLS_B02.tif (RGB sample)
110
+ - AG100.v003.33.-107.0001.h5 (HDF5 file)
111
+
112
+ ## Credit & License
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.
114
+
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.
117
+
118
+ ## License
119
+ This project is released under the MIT License.
120
+
121
+ ## Contributors
122
+ - [@HarshShinde0](https://github.com/HarshShinde0) — added mouse-wheel and trackpad zoom support
123
+
@@ -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.4"
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,12 +56,38 @@ 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)
62
63
  self.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)
63
64
  self.setResizeAnchor(QGraphicsView.ViewportAnchor.AnchorViewCenter)
64
65
  self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
66
+ self._wheel_zoom_step = 1.2
67
+
68
+ def wheelEvent(self, event):
69
+ """Zoom in/out centered at the cursor position.
70
+
71
+ Uses a multiplicative scale per 15° wheel step.
72
+ """
73
+ delta = event.angleDelta().y()
74
+ if delta == 0:
75
+ # Trackpads may report pixelDelta; fall back to it if angleDelta is 0
76
+ pixel_delta = event.pixelDelta().y()
77
+ delta = pixel_delta
78
+
79
+ if delta == 0:
80
+ event.ignore()
81
+ return
82
+
83
+ steps = delta / 120.0 # 120 units per 15° step
84
+ if steps > 0:
85
+ factor = self._wheel_zoom_step ** steps
86
+ else:
87
+ factor = (1.0 / self._wheel_zoom_step) ** (-steps)
88
+
89
+ self.scale(factor, factor)
90
+ event.accept()
65
91
 
66
92
 
67
93
  # ------------------------------- Main Window ------------------------------ #
@@ -76,6 +102,7 @@ class TiffViewer(QMainWindow):
76
102
  shapefiles: list[str] | None = None,
77
103
  shp_color: str = "white",
78
104
  shp_width: float = 2,
105
+ subset: int | None = None,
79
106
  ):
80
107
  super().__init__()
81
108
 
@@ -116,38 +143,118 @@ class TiffViewer(QMainWindow):
116
143
  self.tif_path = self.tif_path or (os.path.commonprefix([red, green, blue]) or red)
117
144
 
118
145
  elif tif_path:
119
- with rasterio.open(tif_path) as src:
120
- self._transform = src.transform
121
- self._crs = src.crs
122
- if rgb is not None:
123
- bands = [src.read(b, out_shape=(src.height // self._scale_arg, src.width // self._scale_arg))
124
- for b in rgb]
125
- arr = np.stack(bands, axis=-1).astype(np.float32)
126
- nd = src.nodata
127
- if nd is not None:
128
- 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 ---
129
198
  self.data = arr
130
- self.band_count = 3
131
- else:
132
- arr = src.read(
133
- self.band,
134
- out_shape=(src.height // self._scale_arg, src.width // self._scale_arg)
135
- ).astype(np.float32)
136
- nd = src.nodata
137
- if nd is not None:
138
- arr[arr == nd] = np.nan
139
- self.data = arr
140
- self.band_count = src.count
141
-
142
- # single-band display range (fast stats or fallback)
143
- try:
144
- stats = src.stats(self.band)
145
- if stats and stats.min is not None and stats.max is not None:
146
- self.vmin, self.vmax = stats.min, stats.max
147
- else:
148
- raise ValueError("No stats in file")
149
- except Exception:
150
- 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
+
151
258
  else:
152
259
  raise ValueError("Provide a TIFF path or --rgbfiles.")
153
260
 
@@ -314,6 +421,10 @@ class TiffViewer(QMainWindow):
314
421
  self.setWindowTitle(f"RGB ({', '.join(names)})")
315
422
  elif self.rgb_mode and self.rgb:
316
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
+ )
317
428
  else:
318
429
  self.setWindowTitle(f"Band {self.band}/{self.band_count} — {os.path.basename(self.tif_path)}")
319
430
 
@@ -346,10 +457,44 @@ class TiffViewer(QMainWindow):
346
457
  return rgb
347
458
 
348
459
  def update_pixmap(self):
349
- 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
+
350
495
  h, w, _ = rgb.shape
351
496
  self._last_rgb = rgb
352
- 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)
353
498
  pix = QPixmap.fromImage(qimg)
354
499
  if self.pixmap_item is None:
355
500
  self.pixmap_item = QGraphicsPixmapItem(pix)
@@ -408,13 +553,24 @@ class TiffViewer(QMainWindow):
408
553
  self.cmap_name, self.alt_cmap_name = self.alt_cmap_name, self.cmap_name
409
554
  self.update_pixmap()
410
555
 
411
- # Band switch (single-band)
412
- elif not self.rgb_mode and k == Qt.Key.Key_BracketRight:
413
- new_band = self.band + 1 if self.band < self.band_count else 1
414
- self.load_band(new_band)
415
- elif not self.rgb_mode and k == Qt.Key.Key_BracketLeft:
416
- new_band = self.band - 1 if self.band > 1 else self.band_count
417
- 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)
418
574
 
419
575
  elif k == Qt.Key.Key_R:
420
576
  self.contrast = 1.0
@@ -463,6 +619,7 @@ def run_viewer(
463
619
  shapefile=None,
464
620
  shp_color=None,
465
621
  shp_width=None,
622
+ subset=None,
466
623
  ):
467
624
  """Launch the TiffViewer app"""
468
625
  app = QApplication(sys.argv)
@@ -475,6 +632,7 @@ def run_viewer(
475
632
  shapefiles=shapefile,
476
633
  shp_color=shp_color,
477
634
  shp_width=shp_width,
635
+ subset=subset,
478
636
  )
479
637
  win.show()
480
638
  sys.exit(app.exec())
@@ -482,6 +640,7 @@ def run_viewer(
482
640
  import click
483
641
 
484
642
  @click.command()
643
+ @click.version_option("1.0.6", prog_name="viewtif")
485
644
  @click.argument("tif_path", required=False)
486
645
  @click.option("--band", default=1, show_default=True, type=int, help="Band number to display")
487
646
  @click.option("--scale", default=1.0, show_default=True, type=float, help="Scale factor for display")
@@ -490,9 +649,9 @@ import click
490
649
  @click.option("--shapefile", multiple=True, type=str, help="One or more shapefiles to overlay")
491
650
  @click.option("--shp-color", default="white", show_default=True, help="Overlay color (name or #RRGGBB).")
492
651
  @click.option("--shp-width", default=1.0, show_default=True, type=float, help="Overlay line width (screen pixels).")
493
- 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):
494
654
  """Lightweight GeoTIFF viewer."""
495
-
496
655
  # --- Warn early if shapefile requested but geopandas missing ---
497
656
  if shapefile and not HAVE_GEO:
498
657
  print(
@@ -510,9 +669,9 @@ def main(tif_path, band, scale, rgb, rgbfiles, shapefile, shp_color, shp_width):
510
669
  shapefile=shapefile,
511
670
  shp_color=shp_color,
512
671
  shp_width=shp_width,
672
+ subset=subset,
513
673
  )
514
674
 
515
-
516
675
  if __name__ == "__main__":
517
676
  main()
518
677
 
viewtif-0.1.4/.gitignore DELETED
@@ -1,4 +0,0 @@
1
- .venv/
2
- dist/
3
- __pycache__/
4
- *.pyc
viewtif-0.1.4/PKG-INFO DELETED
@@ -1,89 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: viewtif
3
- Version: 0.1.4
4
- Summary: Simple GeoTIFF viewer with optional shapefile overlay.
5
- Project-URL: Homepage, https://github.com/nkeikon/tifviewer
6
- Project-URL: Source, https://github.com/nkeikon/tifviewer
7
- Project-URL: Issues, https://github.com/nkeikon/tifviewer/issues
8
- Author: Keiko Nomura
9
- License: MIT
10
- Requires-Python: >=3.9
11
- Requires-Dist: click>=8.1
12
- Requires-Dist: matplotlib>=3.7
13
- Requires-Dist: numpy>=1.23
14
- Requires-Dist: pyside6>=6.5
15
- Requires-Dist: rasterio>=1.3
16
- Provides-Extra: geo
17
- Requires-Dist: geopandas>=0.13; extra == 'geo'
18
- Requires-Dist: shapely>=2.0; extra == 'geo'
19
- Description-Content-Type: text/markdown
20
-
21
- # viewtif
22
-
23
- A lightweight GeoTIFF viewer for quick visualization directly from the command line.
24
-
25
- You can visualize single-band GeoTIFFs, RGB composites, and shapefile overlays in a simple Qt-based window.
26
-
27
- ---
28
-
29
- ## Installation
30
-
31
- ```bash
32
- pip install viewtif
33
- ```
34
-
35
- If you want to enable shapefile overlays, install with optional dependencies:
36
- ```bash
37
- pip install "viewtif[geo]"
38
- ```
39
- Note for macOS(zsh) users:
40
- Make sure to include the quotes, or zsh will interpret it as a pattern.
41
-
42
- ## Quick Start
43
- ```bash
44
- # 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
50
-
51
- # View an RGB composite
52
- viewtif --rgbfiles \
53
- examples/sample_data/HLS_B4.tif \
54
- examples/sample_data/HLS_B3.tif \
55
- examples/sample_data/HLS_B2.tif
56
-
57
- ```
58
-
59
- ## Controls
60
- | Key | Action |
61
- | -------------------- | --------------------------------------- |
62
- | `+` / `-` | Zoom in / out |
63
- | Arrow keys or `WASD` | Pan |
64
- | `C` / `V` | Increase / decrease contrast |
65
- | `G` / `H` | Increase / decrease gamma |
66
- | `M` | Toggle colormap (`viridis` ↔ `magma`) |
67
- | `[` / `]` | Previous / next band (single-band only) |
68
- | `R` | Reset view |
69
-
70
- ## Features
71
- - Command-line driven GeoTIFF viewer
72
- - Supports single-band or RGB composite display.
73
- - Optional shapefile overlay for geographic context.
74
- - Adjustable contrast, gamma, and colormap.
75
- - Fast preview using rasterio and PySide6.
76
-
77
- ## Example Data
78
- - ECOSTRESS_LST.tif
79
- - Zip_Codes.shp and associated files
80
- - HLS_B4.tif, HLS_B3.tif, HLS_B2.tif (RGB sample)
81
-
82
- ## Credit & License
83
- `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
-
85
- # Citation
86
- Longenecker, Jake; Lee, Christine; Hulley, Glynn; Cawse-Nicholson, Kerry; Purkis, Sam; Gleason, Art; Otis, Dan; Galdamez, Illeana; 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
-
88
- # License
89
- This project is released under the MIT License.
viewtif-0.1.4/README.md DELETED
@@ -1,69 +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_B4.tif \
34
- examples/sample_data/HLS_B3.tif \
35
- examples/sample_data/HLS_B2.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_B4.tif, HLS_B3.tif, HLS_B2.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, Illeana; 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.
File without changes