xopr.cf_units
1# TODO: This is entirely AI generated and unchecked. 2# I just wanted something to quickly demonstrate what units might looks like. 3# Should be carefully reviewed. 4 5import numpy as np 6 7 8def apply_cf_compliant_attrs(ds): 9 """ 10 Apply CF-compliant units and comments to radar echogram dataset variables. 11 12 Parameters 13 ---------- 14 ds : xarray.Dataset 15 The input radar echogram dataset. 16 17 Returns 18 ------- 19 xarray.Dataset 20 Dataset with CF-compliant attributes applied. 21 """ 22 23 # Create a copy to avoid modifying the original dataset 24 ds_cf = ds.copy() 25 26 # Define CF-compliant attributes for coordinates 27 coordinate_attrs = { 28 'slow_time': { 29 #'units': 'seconds since 1970-01-01T00:00:00Z', 30 'standard_name': 'time', 31 'long_name': 'slow time', 32 'comment': 'Time coordinate for radar pulse transmission along flight track' 33 }, 34 'twtt': { 35 'units': 's', 36 'standard_name': 'time', 37 'long_name': 'two-way travel time', 38 'comment': 'Two-way travel time from radar to target and back' 39 } 40 } 41 42 # Define CF-compliant attributes for data variables 43 data_var_attrs = { 44 'Bottom': { 45 'units': 's', 46 'long_name': 'bottom surface two-way travel time', 47 'comment': 'Two-way travel time to detected bottom surface. NaN where bottom not detected.', 48 '_FillValue': np.nan 49 }, 50 'Data': { 51 'units': '1', # TODO: Appropriate units for radar data -- can we calibrate to watts? 52 'long_name': 'radar echo power', 53 'comment': 'Radar echo power in linear scale', 54 'coordinates': 'slow_time twtt' 55 }, 56 'Elevation': { 57 'units': 'm', 58 'standard_name': 'height_above_reference_ellipsoid', 59 'long_name': 'platform elevation above WGS84 ellipsoid', 60 'comment': 'GPS-derived elevation of radar platform above WGS84 reference ellipsoid' 61 }, 62 'Heading': { 63 'units': 'radians', 64 'standard_name': 'platform_orientation', 65 'long_name': 'platform heading angle', 66 'comment': 'Platform heading angle in radians from north, clockwise positive', 67 'valid_min': -np.pi, 68 'valid_max': np.pi, 69 'valid_range': [-np.pi, np.pi], 70 }, 71 'Latitude': { 72 'units': 'degrees_north', 73 'standard_name': 'latitude', 74 'long_name': 'platform latitude', 75 'comment': 'GPS-derived latitude of radar platform in WGS84 coordinate system', 76 'valid_min': -90.0, 77 'valid_max': 90.0, 78 'valid_range': [-90.0, 90.0] 79 }, 80 'Longitude': { 81 'units': 'degrees_east', 82 'standard_name': 'longitude', 83 'long_name': 'platform longitude', 84 'comment': 'GPS-derived longitude of radar platform in WGS84 coordinate system', 85 'valid_min': -180.0, 86 'valid_max': 180.0, 87 'valid_range': [-180.0, 180.0] 88 }, 89 'Pitch': { 90 'units': 'radians', 91 'standard_name': 'platform_pitch_angle', 92 'long_name': 'platform pitch angle', 93 'comment': 'Platform pitch angle in radians, positive nose up', 94 'valid_min': -np.pi/2, 95 'valid_max': np.pi/2, 96 'valid_range': [-np.pi/2, np.pi/2] 97 }, 98 'Roll': { 99 'units': 'radians', 100 'standard_name': 'platform_roll_angle', 101 'long_name': 'platform roll angle', 102 'comment': 'Platform roll angle in radians, positive right wing down', 103 'valid_min': -np.pi, 104 'valid_max': np.pi, 105 'valid_range': [-np.pi, np.pi] 106 }, 107 'Surface': { 108 'units': 's', 109 'long_name': 'surface two-way travel time', 110 'comment': 'Two-way travel time to detected surface. Zero indicates surface at platform level.', 111 'valid_min': 0.0 112 } 113 } 114 115 # Apply coordinate attributes 116 for coord_name, attrs in coordinate_attrs.items(): 117 if coord_name in ds_cf.coords: 118 ds_cf[coord_name].attrs.update(attrs) 119 120 # Apply data variable attributes 121 for var_name, attrs in data_var_attrs.items(): 122 if var_name in ds_cf.data_vars: 123 ds_cf[var_name].attrs.update(attrs) 124 125 # Add global attributes for CF compliance 126 global_attrs = { 127 'Conventions': 'CF-1.8', 128 'title': 'Radar Echogram Data', 129 'institution': 'Open Polar Radar (OPR)', 130 'source': 'Airborne/ground-based radar sounder', 131 'history': f'Converted to CF-compliant format on {np.datetime64("now").astype(str)}', 132 'references': 'https://gitlab.com/openpolarradar/opr', 133 'comment': 'Polar radar echogram data with CF-compliant metadata', 134 'geospatial_lat_min': float(ds_cf.Latitude.min()) if 'Latitude' in ds_cf else None, 135 'geospatial_lat_max': float(ds_cf.Latitude.max()) if 'Latitude' in ds_cf else None, 136 'geospatial_lon_min': float(ds_cf.Longitude.min()) if 'Longitude' in ds_cf else None, 137 'geospatial_lon_max': float(ds_cf.Longitude.max()) if 'Longitude' in ds_cf else None, 138 'time_coverage_start': str(ds_cf.slow_time.min().values) if 'slow_time' in ds_cf else None, 139 'time_coverage_end': str(ds_cf.slow_time.max().values) if 'slow_time' in ds_cf else None 140 } 141 142 # Remove None values from global attributes 143 global_attrs = {k: v for k, v in global_attrs.items() if v is not None} 144 145 # Update global attributes 146 ds_cf.attrs.update(global_attrs) 147 148 return ds_cf
def
apply_cf_compliant_attrs(ds):
9def apply_cf_compliant_attrs(ds): 10 """ 11 Apply CF-compliant units and comments to radar echogram dataset variables. 12 13 Parameters 14 ---------- 15 ds : xarray.Dataset 16 The input radar echogram dataset. 17 18 Returns 19 ------- 20 xarray.Dataset 21 Dataset with CF-compliant attributes applied. 22 """ 23 24 # Create a copy to avoid modifying the original dataset 25 ds_cf = ds.copy() 26 27 # Define CF-compliant attributes for coordinates 28 coordinate_attrs = { 29 'slow_time': { 30 #'units': 'seconds since 1970-01-01T00:00:00Z', 31 'standard_name': 'time', 32 'long_name': 'slow time', 33 'comment': 'Time coordinate for radar pulse transmission along flight track' 34 }, 35 'twtt': { 36 'units': 's', 37 'standard_name': 'time', 38 'long_name': 'two-way travel time', 39 'comment': 'Two-way travel time from radar to target and back' 40 } 41 } 42 43 # Define CF-compliant attributes for data variables 44 data_var_attrs = { 45 'Bottom': { 46 'units': 's', 47 'long_name': 'bottom surface two-way travel time', 48 'comment': 'Two-way travel time to detected bottom surface. NaN where bottom not detected.', 49 '_FillValue': np.nan 50 }, 51 'Data': { 52 'units': '1', # TODO: Appropriate units for radar data -- can we calibrate to watts? 53 'long_name': 'radar echo power', 54 'comment': 'Radar echo power in linear scale', 55 'coordinates': 'slow_time twtt' 56 }, 57 'Elevation': { 58 'units': 'm', 59 'standard_name': 'height_above_reference_ellipsoid', 60 'long_name': 'platform elevation above WGS84 ellipsoid', 61 'comment': 'GPS-derived elevation of radar platform above WGS84 reference ellipsoid' 62 }, 63 'Heading': { 64 'units': 'radians', 65 'standard_name': 'platform_orientation', 66 'long_name': 'platform heading angle', 67 'comment': 'Platform heading angle in radians from north, clockwise positive', 68 'valid_min': -np.pi, 69 'valid_max': np.pi, 70 'valid_range': [-np.pi, np.pi], 71 }, 72 'Latitude': { 73 'units': 'degrees_north', 74 'standard_name': 'latitude', 75 'long_name': 'platform latitude', 76 'comment': 'GPS-derived latitude of radar platform in WGS84 coordinate system', 77 'valid_min': -90.0, 78 'valid_max': 90.0, 79 'valid_range': [-90.0, 90.0] 80 }, 81 'Longitude': { 82 'units': 'degrees_east', 83 'standard_name': 'longitude', 84 'long_name': 'platform longitude', 85 'comment': 'GPS-derived longitude of radar platform in WGS84 coordinate system', 86 'valid_min': -180.0, 87 'valid_max': 180.0, 88 'valid_range': [-180.0, 180.0] 89 }, 90 'Pitch': { 91 'units': 'radians', 92 'standard_name': 'platform_pitch_angle', 93 'long_name': 'platform pitch angle', 94 'comment': 'Platform pitch angle in radians, positive nose up', 95 'valid_min': -np.pi/2, 96 'valid_max': np.pi/2, 97 'valid_range': [-np.pi/2, np.pi/2] 98 }, 99 'Roll': { 100 'units': 'radians', 101 'standard_name': 'platform_roll_angle', 102 'long_name': 'platform roll angle', 103 'comment': 'Platform roll angle in radians, positive right wing down', 104 'valid_min': -np.pi, 105 'valid_max': np.pi, 106 'valid_range': [-np.pi, np.pi] 107 }, 108 'Surface': { 109 'units': 's', 110 'long_name': 'surface two-way travel time', 111 'comment': 'Two-way travel time to detected surface. Zero indicates surface at platform level.', 112 'valid_min': 0.0 113 } 114 } 115 116 # Apply coordinate attributes 117 for coord_name, attrs in coordinate_attrs.items(): 118 if coord_name in ds_cf.coords: 119 ds_cf[coord_name].attrs.update(attrs) 120 121 # Apply data variable attributes 122 for var_name, attrs in data_var_attrs.items(): 123 if var_name in ds_cf.data_vars: 124 ds_cf[var_name].attrs.update(attrs) 125 126 # Add global attributes for CF compliance 127 global_attrs = { 128 'Conventions': 'CF-1.8', 129 'title': 'Radar Echogram Data', 130 'institution': 'Open Polar Radar (OPR)', 131 'source': 'Airborne/ground-based radar sounder', 132 'history': f'Converted to CF-compliant format on {np.datetime64("now").astype(str)}', 133 'references': 'https://gitlab.com/openpolarradar/opr', 134 'comment': 'Polar radar echogram data with CF-compliant metadata', 135 'geospatial_lat_min': float(ds_cf.Latitude.min()) if 'Latitude' in ds_cf else None, 136 'geospatial_lat_max': float(ds_cf.Latitude.max()) if 'Latitude' in ds_cf else None, 137 'geospatial_lon_min': float(ds_cf.Longitude.min()) if 'Longitude' in ds_cf else None, 138 'geospatial_lon_max': float(ds_cf.Longitude.max()) if 'Longitude' in ds_cf else None, 139 'time_coverage_start': str(ds_cf.slow_time.min().values) if 'slow_time' in ds_cf else None, 140 'time_coverage_end': str(ds_cf.slow_time.max().values) if 'slow_time' in ds_cf else None 141 } 142 143 # Remove None values from global attributes 144 global_attrs = {k: v for k, v in global_attrs.items() if v is not None} 145 146 # Update global attributes 147 ds_cf.attrs.update(global_attrs) 148 149 return ds_cf
Apply CF-compliant units and comments to radar echogram dataset variables.
Parameters
- ds (xarray.Dataset): The input radar echogram dataset.
Returns
- xarray.Dataset: Dataset with CF-compliant attributes applied.