Source code for gedih3.cli.gh3_read_schema

#!/usr/bin/env python

# Copyright (C) 2026, University of Maryland. All Rights Reserved.
# Authors: Tiago de Conto, Amelia Grace Holcomb
# For commercial licensing inquiries, contact UM Ventures at umdtechtransfer@umd.edu

"""
Inspect schema of parquet, feather, geopackage, HDF5 files, or H3 databases.

Read and display column names and data types from output datasets,
useful for understanding extracted data structure. Supports single
files, dataset directories, and H3 databases (auto-detects format).
When no path is given, reads from the default H3 database.
"""

import argparse
import os
import sys


[docs] def get_cmd_args(): """Parse command line arguments""" p = argparse.ArgumentParser( description="Inspect schema of parquet, feather, geopackage, HDF5 files, or H3 databases" ) p.add_argument( "path", type=str, nargs='?', default=None, help="path to file or directory to inspect [default: GH3_DEFAULT_H3_DIR]" ) p.add_argument( "-p", "--product", dest="product", type=str, default=None, help="filter columns by product suffix (e.g., L2A -> columns ending in _l2a)" ) p.add_argument( "--grep", dest="grep", type=str, default=None, help="filter columns by pattern (case-insensitive)" ) p.add_argument( "-g", "--group", dest="group", type=str, default=None, help="for HDF5 files, specific group/beam to inspect (e.g., 'BEAM0101')" ) p.add_argument( "--json", dest="json_output", action="store_true", help="output in JSON format" ) p.add_argument( "--csv", dest="csv_output", action="store_true", help="output in CSV format" ) p.add_argument( "--no-header", dest="no_header", action="store_true", help="suppress headers in output" ) from gedih3.cliutils import add_storage_args add_storage_args(p) return p.parse_args()
def _detect_file_type(path): """Detect display name for the file/directory type.""" from gedih3.utils import smart_exists, smart_isdir, smart_join path_lower = path.lower() if path_lower.endswith(('.h5', '.hdf5')): return "HDF5" if smart_isdir(path): from gedih3.config import BUILD_LOG_FILENAME build_log = smart_join(path, BUILD_LOG_FILENAME) if smart_exists(build_log): return "H3 Database" from gedih3.cliutils import detect_dataset_format fmt = detect_dataset_format(path) return {'parquet': 'Parquet', 'feather': 'Feather', 'gpkg': 'GeoPackage'}.get(fmt, fmt) ext = os.path.splitext(path)[1].lstrip('.').lower() return {'parquet': 'Parquet', 'feather': 'Feather', 'gpkg': 'GeoPackage'}.get(ext, ext) def _format_shape(rows, cols): """Format HDF5 dataset shape as a string.""" return f"({rows},)" if cols == 1 else f"({rows}, {cols})" def _print_table(schema_df, file_type, path, group=None, no_header=False): """Print schema as a formatted terminal table.""" is_hdf5 = 'path' in schema_df.columns if not no_header: print() print(f"{file_type} Schema: {path}") if group: print(f"Group: {group}") print("=" * 70) print() if is_hdf5: if not no_header: print(f"{'Dataset Path':<40} {'Type':<15} {'Shape':<15}") print("-" * 70) for _, row in schema_df.iterrows(): shape = _format_shape(row['rows'], row['cols']) print(f"{row['path']:<40} {str(row['dtype']):<15} {shape:<15}") else: if not no_header: print(f"{'Column':<40} {'Type':<30}") print("-" * 70) for _, row in schema_df.iterrows(): print(f"{row['column']:<40} {str(row['dtype']):<30}") if not no_header: print("-" * 70) print(f"Total: {len(schema_df)} {'datasets' if is_hdf5 else 'columns'}") print() def _print_json(schema_df): """Print schema as JSON.""" import json is_hdf5 = 'path' in schema_df.columns if is_hdf5: data = [{"name": row['path'], "dtype": str(row['dtype']), "shape": _format_shape(row['rows'], row['cols'])} for _, row in schema_df.iterrows()] else: data = [{"name": row['column'], "dtype": str(row['dtype'])} for _, row in schema_df.iterrows()] print(json.dumps(data, indent=2)) def _print_csv(schema_df): """Print schema as CSV.""" is_hdf5 = 'path' in schema_df.columns if is_hdf5: print("name,dtype,shape") for _, row in schema_df.iterrows(): print(f"{row['path']},{row['dtype']},{_format_shape(row['rows'], row['cols'])}") else: print("name,dtype") for _, row in schema_df.iterrows(): print(f"{row['column']},{row['dtype']}")
[docs] def main(): args = get_cmd_args() from gedih3.cliutils import setup_storage, resolve_path_args setup_storage(args) # Resolve default path from environment if args.path is None: from gedih3.config import GH3_DEFAULT_H3_DIR args.path = GH3_DEFAULT_H3_DIR resolve_path_args(args, ['path']) from gedih3.utils import smart_exists if not smart_exists(args.path): print(f"Error: Path not found: {args.path}", file=sys.stderr) sys.exit(1) try: from gedih3.utils import read_schema schema_df = read_schema(args.path, root=args.group) # Sort by name column for user-friendly output sort_col = 'path' if 'path' in schema_df.columns else 'column' schema_df = schema_df.sort_values(sort_col).reset_index(drop=True) file_type = _detect_file_type(args.path) except Exception as e: print(f"Error reading schema: {e}", file=sys.stderr) sys.exit(1) # Apply column filters (only for non-HDF5 schemas) if 'column' in schema_df.columns: if args.product: suffix = f"_{args.product.lower()}" mask = schema_df['column'].str.lower().str.endswith(suffix) schema_df = schema_df[mask] if args.grep: mask = schema_df['column'].str.contains(args.grep, case=False) schema_df = schema_df[mask] if args.json_output: _print_json(schema_df) elif args.csv_output: _print_csv(schema_df) else: _print_table(schema_df, file_type, args.path, args.group, args.no_header)
if __name__ == '__main__': main()