viewinline 0.3.1__tar.gz → 0.3.2__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: viewinline
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: Quick look geospatial viewer for the terminal, with inline image previews
5
5
  Project-URL: Homepage, https://github.com/nkeikon/viewinline
6
6
  Project-URL: Repository, https://github.com/nkeikon/viewinline
@@ -217,12 +217,12 @@ General:
217
217
  Raster:
218
218
  --band BAND Band number to display (single raster), or slice number for NetCDF. (default: 1)
219
219
  --bands BANDS Display multiple bands as a grid. Accepts ranges (30-40), lists (3,4,5), or mixed (1,5,10-15).
220
+ --rgb R G B Three band numbers for RGB display (e.g., --rgb 4 3 2). Overrides default 1 2 3.
221
+ --rgbfiles R G B Three single-band rasters for RGB composite. Can also provide as positional arguments.
220
222
  --timestep INTEGER Alias for --band when working with NetCDF files.
221
223
  --subset INTEGER Variable index for NetCDF/HDF files (e.g., --subset 1).
222
224
  --reduce DIM_NAME For 3D NetCDF variables, specify which dimension to use as the band/slider axis. Auto-detected if omitted.
223
225
  --colormap Apply colormap to single-band rasters. Flag without the color scheme → 'terrain'.
224
- --rgb R G B Three band numbers for RGB display (e.g., --rgb 4 3 2). Overrides default 1 2 3.
225
- --rgbfiles R G B Three single-band rasters for RGB composite. Can also provide as positional arguments.
226
226
  --vmin VMIN Minimum pixel value for raster display scaling.
227
227
  --vmax VMAX Maximum pixel value for raster display scaling.
228
228
  --nodata NODATA Override nodata value for rasters if dataset metadata is missing or incorrect.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "viewinline"
7
- version = "0.3.1"
7
+ version = "0.3.2"
8
8
  description = "Quick look geospatial viewer for the terminal, with inline image previews"
9
9
  readme = "README.md"
10
10
  license = { text = "Apache-2.0" }
@@ -9,14 +9,11 @@ Supports:
9
9
 
10
10
  Display:
11
11
  Sends iTerm2-style inline image escape sequences. Works in terminals that support
12
- the iTerm2 inline image protocol (iTerm2, WezTerm, Konsole, etc.). In other
13
- terminals, the escape codes are ignored.
12
+ the iTerm2 inline image protocol (iTerm2, WezTerm, Konsole, etc.). For others,
13
+ please see line 48-58.
14
14
 
15
15
  Particularly useful on HPC systems and remote servers accessed via SSH — images
16
16
  render on your local terminal without X11 forwarding, VNC, or file downloads.
17
-
18
- No detection, no fallbacks. If images are not shown, it means that the terminal
19
- is not compatible.
20
17
  """
21
18
 
22
19
  import sys, os, base64, shutil, argparse
@@ -28,19 +25,13 @@ from matplotlib import colormaps
28
25
  import matplotlib as mpl
29
26
  import subprocess
30
27
 
31
- try:
32
- import netCDF4
33
- HAS_NETCDF4 = True
34
- except ImportError:
35
- HAS_NETCDF4 = False
36
-
37
28
  import warnings
38
29
 
39
30
  warnings.filterwarnings("ignore", message="More than one layer found", category=UserWarning)
40
31
  warnings.filterwarnings("ignore", message="Dataset has no geotransform", category=UserWarning)
41
32
  warnings.filterwarnings("ignore", message="invalid scale_factor or add_offset attribute", category=UserWarning)
42
33
 
43
- __version__ = "0.3.1"
34
+ __version__ = "0.3.2"
44
35
 
45
36
  AVAILABLE_COLORMAPS = [
46
37
  "viridis", "inferno", "magma", "plasma",
@@ -266,6 +257,23 @@ def parse_bands(s: str) -> list[int]:
266
257
  print(f"[WARN] Could not parse band: {part}")
267
258
  return sorted(set(bands))
268
259
 
260
+ def parse_rgb(values: list[str]) -> list[int]:
261
+ """Parse --rgb: accepts '4 3 2' or '4,3,2'."""
262
+ if len(values) == 1:
263
+ # comma-separated: '4,3,2'
264
+ parts = values[0].split(",")
265
+ else:
266
+ # space-separated: '4' '3' '2'
267
+ parts = values
268
+ try:
269
+ result = [int(p.strip()) for p in parts]
270
+ if len(result) != 3:
271
+ raise ValueError
272
+ return result
273
+ except ValueError:
274
+ print("[WARN] --rgb requires exactly 3 band numbers. e.g. --rgb 4 3 2 or --rgb 4,3,2")
275
+ return None
276
+
269
277
  # ---------------------------------------------------------------------
270
278
  # CSV handling
271
279
  # ---------------------------------------------------------------------
@@ -304,11 +312,18 @@ def preview_df(df, max_rows: int = 10, query_mode: bool = False, filename: str =
304
312
  # -------------------------------------------------------------
305
313
  if n_rows <= max_rows:
306
314
  rows_to_show = df
315
+ elif query_mode:
316
+ ans = input(f"Filtered results: {n_rows} rows. Show first {max_rows} or all? [first/all]: ").strip().lower()
317
+ if ans == "all":
318
+ rows_to_show = df
319
+ else:
320
+ rows_to_show = df.head(max_rows)
307
321
  else:
308
- ans = input(f"Preview first {max_rows} rows? [y/N]: ").strip().lower()
309
- if ans not in ("y", "yes"):
310
- return
311
- rows_to_show = df.head(max_rows)
322
+ ans = input(f"Large file: {n_rows} rows. Show first {max_rows} or all? [first/all]: ").strip().lower()
323
+ if ans == "all":
324
+ rows_to_show = df
325
+ else:
326
+ rows_to_show = df.head(max_rows)
312
327
 
313
328
  # -------------------------------------------------------------
314
329
  # Build pretty table
@@ -610,8 +625,15 @@ def normalize_to_uint8(band: np.ndarray, vmin=None, vmax=None, nodata=None) -> n
610
625
  valid_vals = band[valid]
611
626
 
612
627
  # --- Manual scaling ---
613
- if vmin is not None and vmax is not None:
614
- mn, mx = vmin, vmax
628
+ if vmin is not None or vmax is not None:
629
+ # Use percentile for whichever end is not specified
630
+ if valid_vals.size < 1_000_000:
631
+ sample = valid_vals
632
+ else:
633
+ sample = np.random.choice(valid_vals, 1_000_000, replace=False)
634
+ p2, p98 = np.percentile(sample, (2, 98))
635
+ mn = vmin if vmin is not None else p2
636
+ mx = vmax if vmax is not None else p98
615
637
  print(f"[VIEW] Using manual scaling: {mn} to {mx}")
616
638
 
617
639
  # --- Percentile fallback ---
@@ -664,7 +686,9 @@ def render_netcdf_via_netcdf4(path, args):
664
686
  """Read a NetCDF file via netCDF4 (bypassing GDAL). Handles hierarchical
665
687
  groups and hyperspectral cubes where GDAL aborts or interprets axes wrong.
666
688
  """
667
- if not HAS_NETCDF4:
689
+ try:
690
+ import netCDF4
691
+ except ImportError:
668
692
  print("[ERROR] netCDF4 not installed. Install with:")
669
693
  print(" pip install netCDF4")
670
694
  print(" or: pip install viewinline[netcdf]")
@@ -759,7 +783,7 @@ def render_netcdf_via_netcdf4(path, args):
759
783
  # BANDS GALLERY for NetCDF
760
784
  if getattr(args, "bands", None):
761
785
  band_list = parse_bands(args.bands)
762
- print(f"[DEBUG] band_count={band_count}, band_list={band_list}")
786
+ # print(f"[DEBUG] band_count={band_count}, band_list={band_list}")
763
787
  valid_bands = [b for b in band_list if 1 <= b <= band_count]
764
788
  if not valid_bands:
765
789
  print(f"[ERROR] No valid bands. Variable has {band_count} bands along '{var.dimensions[spectral_axis]}'.")
@@ -773,10 +797,48 @@ def render_netcdf_via_netcdf4(path, args):
773
797
  nc.close()
774
798
  colormap = args.colormap if args.colormap else "viridis"
775
799
  render_bands_gallery(np.stack(slices, axis=0), valid_bands, band_count,
776
- display_scale=getattr(args, "display", None),
777
- colormap=colormap)
800
+ grid=getattr(args, "gallery", None),
801
+ display_scale=getattr(args, "display", None),
802
+ colormap=colormap)
778
803
  return
779
804
 
805
+ # RGB COMPOSITE for NetCDF
806
+ if getattr(args, "rgb", None):
807
+ try:
808
+ # rgb_bands = args.rgb
809
+ rgb_bands = parse_rgb(args.rgb)
810
+ if rgb_bands is None:
811
+ return
812
+ if len(rgb_bands) != 3:
813
+ raise ValueError("exactly 3 bands required")
814
+ slices = []
815
+ for b in rgb_bands:
816
+ if b < 1 or b > band_count:
817
+ raise ValueError(f"band {b} out of range (1-{band_count})")
818
+ slicer = [slice(None)] * 3
819
+ slicer[spectral_axis] = b - 1
820
+ slices.append(np.asarray(var[tuple(slicer)], dtype=np.float64))
821
+ nc.close()
822
+ print(f"[INFO] Using RGB bands: {rgb_bands}")
823
+ img = np.stack([normalize_to_uint8(s, vmin=args.vmin, vmax=args.vmax, nodata=args.nodata)
824
+ for s in slices], axis=-1)
825
+ H, W = img.shape[:2]
826
+ if args.display:
827
+ new_w = max(1, int(W * args.display))
828
+ new_h = max(1, int(H * args.display))
829
+ img = np.array(Image.fromarray(img).resize((new_w, new_h), Image.BILINEAR))
830
+ else:
831
+ max_dim = 2000
832
+ if max(H, W) > max_dim:
833
+ scale = max_dim / max(H, W)
834
+ new_w, new_h = int(W * scale), int(H * scale)
835
+ img = np.array(Image.fromarray(img).resize((new_w, new_h), Image.BILINEAR))
836
+ show_image_auto(img, getattr(args, "display", None), is_vector=False)
837
+ return
838
+ except ValueError as e:
839
+ print(f"[WARN] Invalid --rgb: {e}. Falling back to band 1.")
840
+
841
+ # Single band slicer
780
842
  slicer = [slice(None)] * 3
781
843
  slicer[spectral_axis] = band_idx
782
844
  data = np.asarray(var[tuple(slicer)], dtype=np.float64)
@@ -970,14 +1032,13 @@ def render_raster(paths: list[str], args) -> None:
970
1032
  if band_count >= 3 and not paths[0].lower().endswith('.nc') and not user_specified_band and getattr(args, 'rgb', None):
971
1033
 
972
1034
  if getattr(args, "rgb", None):
973
- try:
974
- rgb_idx = [b - 1 for b in args.rgb]
975
- if len(rgb_idx) != 3:
976
- raise ValueError
977
- print(f"[INFO] Using RGB bands: {args.rgb}")
978
- except Exception:
1035
+ rgb_parsed = parse_rgb(args.rgb)
1036
+ if rgb_parsed is None:
979
1037
  print("[WARN] Invalid --rgb. Using default 1 2 3")
980
1038
  rgb_idx = [0, 1, 2]
1039
+ else:
1040
+ rgb_idx = [b - 1 for b in rgb_parsed]
1041
+ print(f"[INFO] Using RGB bands: {rgb_parsed}")
981
1042
  else:
982
1043
  rgb_idx = [0, 1, 2]
983
1044
 
@@ -1100,6 +1161,7 @@ def render_gallery(folder: str, grid: str = "4x4", display_scale=None, is_vector
1100
1161
 
1101
1162
  # Load thumbnails
1102
1163
  thumbs = []
1164
+ loaded_files = []
1103
1165
  thumb_size = (128, 128)
1104
1166
  for f in files:
1105
1167
  try:
@@ -1122,6 +1184,7 @@ def render_gallery(folder: str, grid: str = "4x4", display_scale=None, is_vector
1122
1184
  img = Image.open(f).convert("RGB")
1123
1185
  img.thumbnail(thumb_size)
1124
1186
  thumbs.append(img)
1187
+ loaded_files.append(f)
1125
1188
 
1126
1189
  except Exception as e:
1127
1190
  print(f"[SKIP] {os.path.basename(f)} — {e}")
@@ -1147,8 +1210,12 @@ def render_gallery(folder: str, grid: str = "4x4", display_scale=None, is_vector
1147
1210
  canvas.paste(img, (x, y))
1148
1211
 
1149
1212
  print(f"[INFO] Displaying {n} images ({cols}×{rows} grid)")
1150
- show_image_auto(np.array(canvas), display_scale, is_vector)
1151
1213
 
1214
+ for r in range(rows):
1215
+ row_files = loaded_files[r * cols:(r + 1) * cols]
1216
+ print(" ".join(f"{os.path.basename(f):<20}" for f in row_files))
1217
+
1218
+ show_image_auto(np.array(canvas), display_scale, is_vector)
1152
1219
  except Exception as e:
1153
1220
  print(f"[ERROR] Failed to render gallery: {e}")
1154
1221
 
@@ -1156,7 +1223,6 @@ def render_bands_gallery(data: np.ndarray, band_list: list[int], band_count: int
1156
1223
  grid: str = None, display_scale=None, colormap: str = "viridis") -> None:
1157
1224
  """Render multiple bands from a single raster as a grid of thumbnails."""
1158
1225
  import math
1159
- from PIL import ImageDraw
1160
1226
 
1161
1227
  # Validate bands
1162
1228
  valid_bands = [b for b in band_list if 1 <= b <= band_count]
@@ -1223,14 +1289,22 @@ def render_bands_gallery(data: np.ndarray, band_list: list[int], band_count: int
1223
1289
  x = margin + c * cell_w
1224
1290
  y = margin + r * cell_h
1225
1291
  canvas.paste(img, (x, y))
1226
- # Draw label on canvas background below the thumbnail
1227
- label = f"B{valid_bands[i]}"
1228
- lx = x + (thumb_w - len(label) * 6) // 2
1229
- ly = y + thumb_h + 2
1230
- draw.text((lx, ly), label, fill=(0, 0, 0), font=font)
1292
+ if _TERMINAL_SUPPORTS_IMAGES:
1293
+ # Draw label on canvas background below the thumbnail
1294
+ label = f"B{valid_bands[i]}"
1295
+ lx = x + (thumb_w - len(label) * 6) // 2
1296
+ ly = y + thumb_h + 2
1297
+ draw.text((lx, ly), label, fill=(0, 0, 0), font=font)
1231
1298
 
1232
1299
  print(f"[INFO] Displaying {n} bands ({cols}×{rows} grid, colormap: {colormap})")
1233
1300
  # print(f"[DEBUG] canvas size: {canvas_w}×{canvas_h}px")
1301
+
1302
+ if not _TERMINAL_SUPPORTS_IMAGES:
1303
+ # Print band labels as text grid
1304
+ for r in range(rows):
1305
+ row_bands = valid_bands[r * cols:(r + 1) * cols]
1306
+ print(" ".join(f"B{b:<4}" for b in row_bands))
1307
+
1234
1308
  show_image_auto(np.array(canvas), display_scale)
1235
1309
 
1236
1310
  # ---------------------------------------------------------------------
@@ -1592,153 +1666,141 @@ def main() -> None:
1592
1666
  formatter_class=SmartDefaults
1593
1667
  )
1594
1668
 
1595
- # File input
1669
+ # File input
1596
1670
  parser.add_argument(
1597
- "paths", nargs="*", # Zero or more (optional)
1671
+ "paths", nargs="*",
1598
1672
  help="Path to raster(s), vector, or CSV file. Provide 1 file or exactly 3 rasters for RGB (R G B)."
1599
1673
  )
1600
- # Display options
1601
- parser.add_argument(
1674
+
1675
+ # General options
1676
+ general = parser.add_argument_group("General")
1677
+ general.add_argument(
1602
1678
  "--display", type=float, default=None,
1603
1679
  help="Resize only the displayed image (0.5=smaller, 2=bigger). Default: auto-fit to terminal."
1604
1680
  )
1681
+ general.add_argument(
1682
+ "--gallery", nargs="?", const="4x4", metavar="GRID",
1683
+ help="Display all image files in a folder as thumbnails (e.g., --gallery 5x4). Incompatible files are skipped."
1684
+ )
1605
1685
 
1606
1686
  # Raster options
1607
- parser.add_argument(
1687
+ raster = parser.add_argument_group("Raster")
1688
+ raster.add_argument(
1608
1689
  "--band", type=int, default=None,
1609
- help="Band number to display (single raster case), or slice number for NetCDF."
1690
+ help="Band number to display (single raster), or slice number for NetCDF. (default: 1)"
1610
1691
  )
1611
- parser.add_argument(
1692
+ raster.add_argument(
1693
+ "--bands", type=str,
1694
+ help="Display multiple bands as a grid. Accepts ranges (30-40), lists (3,4,5), or mixed (1,3,10-15)."
1695
+ )
1696
+ raster.add_argument(
1612
1697
  "--timestep", type=int, default=None,
1613
- help="Alias for --band when working with NetCDF files (1-based index)."
1698
+ help="Alias for --band when working with NetCDF files."
1614
1699
  )
1615
- parser.add_argument(
1700
+ raster.add_argument(
1701
+ "--subset", type=int, default=None,
1702
+ help="Variable index for NetCDF/HDF files (e.g., --subset 1)."
1703
+ )
1704
+ raster.add_argument(
1705
+ "--reduce", dest="reduce_dim", type=str, default=None, metavar="DIM_NAME",
1706
+ help="For 3D NetCDF variables, specify which dimension to use as the band/slider axis. Auto-detected if omitted."
1707
+ )
1708
+ raster.add_argument(
1616
1709
  "--colormap", nargs="?", const="terrain",
1617
1710
  choices=AVAILABLE_COLORMAPS, default=None,
1618
- help="Apply colormap to single-band rasters or vector coloring. Flag without value → 'terrain'."
1711
+ help="Apply colormap to single-band rasters. Flag without value → 'terrain'."
1619
1712
  )
1620
- parser.add_argument(
1621
- "--rgb", nargs=3, type=int, metavar=('R', 'G', 'B'), default=None,
1622
- help="Three band numbers for RGB display (e.g., --rgb 4 3 2). Overrides default 1 2 3."
1713
+ raster.add_argument(
1714
+ "--rgb", nargs='+', type=str, metavar='BAND', default=None,
1715
+ help="Three band numbers for RGB display (e.g., --rgb 4 3 2 or --rgb 4,3,2). Overrides default 1 2 3."
1623
1716
  )
1624
- parser.add_argument(
1717
+ raster.add_argument(
1625
1718
  "--rgbfiles", nargs=3, type=str, metavar=('R', 'G', 'B'),
1626
- help="Three single-band rasters for RGB composite (e.g., --rgbfiles R.tif G.tif B.tif). Can also provide as positional arguments without the flag."
1719
+ help="Three single-band rasters for RGB composite. Can also provide as positional arguments."
1627
1720
  )
1628
- parser.add_argument(
1721
+ raster.add_argument(
1629
1722
  "--vmin", type=float, default=None,
1630
1723
  help="Minimum pixel value for raster display scaling."
1631
1724
  )
1632
- parser.add_argument(
1725
+ raster.add_argument(
1633
1726
  "--vmax", type=float, default=None,
1634
1727
  help="Maximum pixel value for raster display scaling."
1635
1728
  )
1636
- parser.add_argument(
1729
+ raster.add_argument(
1637
1730
  "--nodata", type=float, default=None,
1638
1731
  help="Override nodata value for rasters if dataset metadata is missing or incorrect."
1639
1732
  )
1640
- parser.add_argument(
1641
- "--gallery", nargs="?", const="4x4", metavar="GRID",
1642
- help="Display all PNG/JPG/TIF images in a folder as thumbnails (e.g., 5x5 grid)."
1643
- )
1644
- parser.add_argument(
1645
- "--subset", type=int, default=None,
1646
- help="Variable index for NetCDF files (e.g. --subset 1)."
1733
+
1734
+ # Vector options
1735
+ vector = parser.add_argument_group("Vector")
1736
+ vector.add_argument(
1737
+ "--color-by", type=str, default=None,
1738
+ help="Numeric column to color vector features by."
1647
1739
  )
1648
- parser.add_argument(
1649
- "--reduce", dest="reduce_dim", type=str, default=None,
1650
- metavar="DIM_NAME",
1651
- help="For 3D NetCDF variables, specify which dimension to use as the band axis (auto-detected if omitted)."
1740
+ vector.add_argument(
1741
+ "--width", type=float, default=0.7,
1742
+ help="Line width for vector boundaries."
1652
1743
  )
1653
- parser.add_argument(
1654
- "--bands",
1655
- type=str,
1656
- help="Display multiple bands as a grid. Accepts ranges (30-40), lists (3,4,5), or mixed (1,3,10-15)."
1744
+ vector.add_argument(
1745
+ "--edgecolor", type=str, default="#F6FF00",
1746
+ help="Edge color for vector outlines (hex or named color)."
1747
+ )
1748
+ vector.add_argument(
1749
+ "--layer", type=str, default=None,
1750
+ help="Layer name for GeoPackage/multi-layer files, or variable name for NetCDF files."
1751
+ )
1752
+ vector.add_argument(
1753
+ "--table", action="store_true",
1754
+ help="Display vector/parquet file as tabular data instead of rendering geometry."
1657
1755
  )
1658
1756
 
1659
- # CSV options
1660
- parser.add_argument(
1661
- "--hist",
1662
- nargs="?",
1663
- const=True,
1664
- help="Show histograms for all numeric columns or specify one column name."
1757
+ # Tabular options
1758
+ tabular = parser.add_argument_group("Tabular")
1759
+ tabular.add_argument(
1760
+ "--hist", nargs="?", const=True,
1761
+ help="Show histograms for all numeric columns or specify one column name."
1665
1762
  )
1666
- parser.add_argument(
1667
- "--describe",
1668
- nargs="?",
1669
- const=True,
1763
+ tabular.add_argument(
1764
+ "--describe", nargs="?", const=True,
1670
1765
  help="Show summary statistics for all numeric columns or specify one column name."
1671
1766
  )
1672
- parser.add_argument(
1673
- "--bins", type=int, default=20,
1674
- help="Number of bins for CSV histograms (used with --hist)."
1767
+ tabular.add_argument(
1768
+ "--bins", type=int, default=20,
1769
+ help="Number of bins for histograms (used with --hist)."
1675
1770
  )
1676
- parser.add_argument(
1677
- "--scatter", nargs=2, metavar=("X", "Y"),
1678
- help="Plot scatter of two numeric CSV columns (e.g. --scatter area_km2 year)."
1771
+ tabular.add_argument(
1772
+ "--scatter", nargs=2, metavar=("X", "Y"),
1773
+ help="Scatter plot of two numeric columns (e.g. --scatter area_km2 year)."
1679
1774
  )
1680
- parser.add_argument(
1681
- "--unique",
1682
- metavar="COLUMN",
1683
- help="Show unique values for a categorical column and exit"
1775
+ tabular.add_argument(
1776
+ "--unique", metavar="COLUMN",
1777
+ help="Show unique values for a categorical column."
1684
1778
  )
1685
- parser.add_argument(
1686
- "--where",
1687
- type=str,
1688
- default=None,
1689
- help="Filter rows using SQL WHERE clause (DuckDB required). Example: --where \"year > 2010\""
1779
+ tabular.add_argument(
1780
+ "--where", type=str, default=None,
1781
+ help="Filter rows using SQL WHERE clause (e.g. --where \"year > 2010\")."
1690
1782
  )
1691
- parser.add_argument(
1692
- "--sort",
1693
- type=str,
1694
- default=None,
1695
- help="Sort rows by values in the specified column, ascending by default (e.g. --sort population). Use --desc to reverse."
1783
+ tabular.add_argument(
1784
+ "--sort", type=str, default=None,
1785
+ help="Sort rows by column, ascending by default. Use --desc to reverse."
1696
1786
  )
1697
- parser.add_argument(
1698
- "--desc",
1699
- action="store_true",
1787
+ tabular.add_argument(
1788
+ "--desc", action="store_true",
1700
1789
  help="Sort in descending order."
1701
1790
  )
1702
- parser.add_argument(
1703
- "--limit",
1704
- type=int,
1705
- default=None,
1791
+ tabular.add_argument(
1792
+ "--limit", type=int, default=None,
1706
1793
  help="Limit number of rows shown (e.g. --limit 100)."
1707
1794
  )
1708
- parser.add_argument(
1709
- "--select",
1710
- nargs="+",
1711
- help="Select specific columns (space separated) (e.g. --select Country City)"
1795
+ tabular.add_argument(
1796
+ "--select", nargs="+",
1797
+ help="Select specific columns (e.g. --select Country City)."
1712
1798
  )
1713
- parser.add_argument(
1714
- "--sql",
1715
- type=str,
1716
- help="Execute full DuckDB SQL query against CSV (advanced mode)."
1799
+ tabular.add_argument(
1800
+ "--sql", type=str,
1801
+ help="Execute full DuckDB SQL query against CSV/parquet (advanced mode)."
1717
1802
  )
1718
1803
 
1719
- # Vector options
1720
- parser.add_argument(
1721
- "--color-by", type=str, default=None,
1722
- help="Numeric column to color vector features by (optional)."
1723
- )
1724
- parser.add_argument(
1725
- "--width", type=float, default=0.7,
1726
- help="Line width for vector boundaries"
1727
- )
1728
- parser.add_argument(
1729
- "--edgecolor", type=str, default="#F6FF00",
1730
- help="Edge color for vector outlines (hex or named color)."
1731
- )
1732
- parser.add_argument(
1733
- "--layer", type=str, default=None,
1734
- # help="Layer name for GeoPackage or multi-layer files."
1735
- help="Layer name for GeoPackage/multi-layer files, or variable name for NetCDF files."
1736
- )
1737
- parser.add_argument(
1738
- "--table", action="store_true",
1739
- help="Display vector/parquet file as tabular data instead of rendering geometry."
1740
- )
1741
-
1742
1804
  parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
1743
1805
 
1744
1806
  args = parser.parse_args()
File without changes
File without changes
@@ -184,12 +184,12 @@ General:
184
184
  Raster:
185
185
  --band BAND Band number to display (single raster), or slice number for NetCDF. (default: 1)
186
186
  --bands BANDS Display multiple bands as a grid. Accepts ranges (30-40), lists (3,4,5), or mixed (1,5,10-15).
187
+ --rgb R G B Three band numbers for RGB display (e.g., --rgb 4 3 2). Overrides default 1 2 3.
188
+ --rgbfiles R G B Three single-band rasters for RGB composite. Can also provide as positional arguments.
187
189
  --timestep INTEGER Alias for --band when working with NetCDF files.
188
190
  --subset INTEGER Variable index for NetCDF/HDF files (e.g., --subset 1).
189
191
  --reduce DIM_NAME For 3D NetCDF variables, specify which dimension to use as the band/slider axis. Auto-detected if omitted.
190
192
  --colormap Apply colormap to single-band rasters. Flag without the color scheme → 'terrain'.
191
- --rgb R G B Three band numbers for RGB display (e.g., --rgb 4 3 2). Overrides default 1 2 3.
192
- --rgbfiles R G B Three single-band rasters for RGB composite. Can also provide as positional arguments.
193
193
  --vmin VMIN Minimum pixel value for raster display scaling.
194
194
  --vmax VMAX Maximum pixel value for raster display scaling.
195
195
  --nodata NODATA Override nodata value for rasters if dataset metadata is missing or incorrect.