4 written by J.Scott on 2017/10/03 (joel.scott@nasa.gov)
10 from collections
import OrderedDict
11 from PRISM_module
import rd_prism_hdr, rd_L1B_bil
16 from netCDF4
import Dataset
17 from datetime
import datetime, timedelta
25 Defines and parses command line arguments
27 ocdataroot = os.getenv(
'OCDATAROOT')
28 parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter,
29 description=
' Converts PRISM data from the CORAL project to netCDF proxy data in the format of PACE-OCI ',
31 parser.add_argument(
'-v',
'--version', action=
'version', version=
'%(prog)s ' + __version__)
32 parser.add_argument(
'-i',
'--ifile', nargs=1, required=
True, type=str,
33 help=
' PRISM L1 prm*img.hdr file (required) ')
34 parser.add_argument(
'-o',
'--ofile', nargs=1, required=
True, type=str,
35 help=
' Output netCDF filename (required) ')
36 parser.add_argument(
'--cdlfile', nargs=1, type=str,
37 default=os.path.join(ocdataroot,
'prism',
'OCIP_Level-1C_Data_Structure.cdl'),
38 help=
' L1C format spec CDL file name ')
39 parser.add_argument(
'-d',
'--debug', action=
'store_true', default=
False)
40 parsedArgs=parser.parse_args(args)
47 Initiates a logger instance
49 lgrName =
'prismbil2oci_%s_T_%s' % (datetime.date(datetime.now()), datetime.time(datetime.now()))
50 lgr = logging.getLogger(lgrName)
54 fmt =
'%(asctime)s - %(name)s - %(levelname)s -\
55 [%(module)s..%(funcName)s..%(lineno)d] -\
57 formatter = logging.Formatter(fmt)
58 fh = logging.FileHandler(
'%s.log' % lgrName)
59 fh.setLevel(logging.DEBUG)
60 fh.setFormatter(formatter)
64 formatter = logging.Formatter(fmt)
65 ch = logging.StreamHandler()
66 ch.setLevel(logging.INFO)
67 ch.setFormatter(formatter)
70 lgr.debug(
'Logger initialized')
77 A subroutine to read PRISM L1 data
79 L1_hdr = OrderedDict()
80 L1_data = OrderedDict()
84 ls_hdr.append(fname_in)
86 if os.path.exists(fname_in.rsplit(
'_',1)[0] +
'_loc_ort.hdr'):
87 ls_hdr.append(fname_in.rsplit(
'_',1)[0] +
'_loc_ort.hdr')
89 ls_hdr.append(fname_in.rsplit(
'_',1)[0] +
'_loc.hdr')
91 if os.path.exists(fname_in.rsplit(
'_',1)[0] +
'_obs_ort.hdr'):
92 ls_hdr.append(fname_in.rsplit(
'_',1)[0] +
'_obs_ort.hdr')
94 ls_hdr.append(fname_in.rsplit(
'_',1)[0] +
'_obs.hdr')
97 ls_bil = [fname.rsplit(
'.')[0]
for fname
in ls_hdr]
99 for fname_hdr,fname_bil
in zip(ls_hdr,ls_bil):
100 if not os.path.exists(fname_hdr):
101 logger.warning(
'Missing PRISM header file: {:}'.format(fname_hdr))
103 if not os.path.exists(fname_bil):
104 logger.warning(
'Missing PRISM BIL file: {:}'.format(fname_bil))
106 logger.info(
'Reading HDR file ' + fname_hdr)
107 [hdr_type, dict_hdr] = rd_prism_hdr(fname_hdr)
110 L1_hdr[hdr_type] = OrderedDict()
112 L1_hdr[hdr_type][key] = dict_hdr[key]
114 logger.info(
'Reading BIL file ' + fname_bil)
115 dict_data = rd_L1B_bil(fname_bil, dict_hdr)
116 L1_data.update(dict_data)
118 return [L1_hdr, L1_data]
124 a subroutine to subsample via weighted averaging
125 spectral data about a desired band center
127 Inputs: band_data -- 3-dim numpy array of shape-order (number_of_bands(_to_average), number_of_scans, pixels_per_scan)
128 band_weights -- list or 1-dim numpy array of length (number_of_bands(_to_average))
130 Outputs: data -- 2-dim numpy array of shape-order (number_of_scans, pixels_per_scan)
131 width -- floating point value of sub-sampled FWHM value
133 width = np.sum(band_weights)
135 data_numerator = np.empty_like(band_data)
136 for i,weight
in zip(
range(data_numerator.shape[0]), band_weights):
137 data_numerator[i,:,:] = np.multiply(band_data[i,:,:], weight)
139 data = np.divide(np.sum(data_numerator, axis=0), width)
147 A subroutine to resample PRISM data to OCI targets to create OCIP data
152 bands_ocip =
list(bands_prism[0:191])
153 widths_ocip =
list(widths_prism[0:191])
157 band_array = np.ndarray((len(lis_940bands),1,1), dtype=
'f8')
158 band_array[:,0,0] = [bands_prism[i]
for i
in lis_940bands]
159 [data,width] =
subsample_spectral(band_array, [widths_prism[i]
for i
in lis_940bands])
160 bands_ocip.append(data[0,0])
161 widths_ocip.append(width)
165 band_array = np.ndarray((len(lis_1038bands),1,1), dtype=
'f8')
166 band_array[:,0,0] = [bands_prism[i]
for i
in lis_1038bands]
167 [data,width] =
subsample_spectral(band_array, [widths_prism[i]
for i
in lis_1038bands])
168 bands_ocip.append(data[0,0])
169 widths_ocip.append(width)
172 lt_ocip = np.ndarray((len(bands_ocip),lt_prism.shape[1],lt_prism.shape[2]), dtype=
"f8")
173 lt_ocip[0:len(index_ocip),:,:] = lt_prism[index_ocip,:,:]
175 [data,width] =
subsample_spectral(lt_prism[lis_940bands,:,:], [widths_prism[i]
for i
in lis_940bands])
176 lt_ocip[-2,:,:] = data[:,:]
178 [data,width] =
subsample_spectral(lt_prism[lis_1038bands,:,:], [widths_prism[i]
for i
in lis_1038bands])
179 lt_ocip[-1,:,:] = data[:,:]
181 return [bands_ocip, widths_ocip, lt_ocip]
187 A subroutine to read a OCIP L1C CDL file and fill the proper dimensions
189 with open(fname_in,
'r')
as fid:
190 lines = fid.readlines()
193 lines = [re.sub(
'NUMSSCANS',
str(num_scans), line)
for line
in lines]
194 lines = [re.sub(
'NUMSPIXELS',
str(num_pix), line)
for line
in lines]
195 lines = [re.sub(
'NUMSBANDS',
str(num_bands), line)
for line
in lines]
203 Coordinates calls to PRISM L1 BIL to L1C netCDF conversion process
208 mainLogger.info(
"prismbil2oci.py %s" % __version__)
209 ifile =
''.join(pArgs.ifile)
210 ofile =
''.join(pArgs.ofile)
211 cfile =
''.join(pArgs.cdlfile)
218 [
float(d)
for d
in dsL1_hdr[
'rdn_img'][
'fwhm'][:len(dsL1_hdr[
'rdn_img'][
'wavelength'])]],
222 cdl_lines =
read_OCIP_L1C_CDL(cfile, dsL1_data[
'utc time'].shape[0], dsL1_data[
'utc time'].shape[1], len(bands_ocip))
226 nc_fname = ofile.rsplit(
'/',1)[-1]
230 mainLogger.info(
'Creating netCDF4 file {:}'.format(ofile))
231 pid = subprocess.run(
'ncgen -b -v3 -o ' + ofile, input=
''.join(cdl_lines), encoding=
'ascii',
232 shell=
True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
234 logger.warning(
'Error in ncgen call: {:}'.format(pid.stderr))
236 nc_fid = Dataset(ofile,
'a')
240 mainLogger.info(
'Filling global attributes')
241 nc_fid.product_name = nc_fname
242 nc_fid.prism_version = dsL1_hdr[
'rdn_img'][
'prism_version']
243 dt_now = datetime.now()
244 nc_fid.date_created = dt_now.strftime(
"%Y-%m-%dT%H:%M:%SZ")
245 dt_start = dsL1_hdr[
'rdn_img'][
'start datetime']
246 nc_fid.flight_line = dt_start.strftime(
"prm%Y%m%dt%H%M%S")
247 nc_fid.time_coverage_start = dt_start.strftime(
"%Y-%m-%dT%H:%M:%SZ")
248 dt_end = dt_start + timedelta(microseconds=np.nanmax(dsL1_data[
'utc time']))
249 nc_fid.time_coverage_end = dt_end.strftime(
"%Y-%m-%dT%H:%M:%SZ")
250 nc_fid.northernmost_latitude =
float(np.nanmax(dsL1_data[
'latitude (wgs-84)']))
251 nc_fid.southernmost_latitude =
float(np.nanmin(dsL1_data[
'latitude (wgs-84)']))
252 nc_fid.easternmost_longitude =
float(np.nanmax(dsL1_data[
'longitude (wgs-84)']))
253 nc_fid.westernmost_longitude =
float(np.nanmin(dsL1_data[
'longitude (wgs-84)']))
254 nc_fid.geospatial_lat_max =
float(np.nanmax(dsL1_data[
'latitude (wgs-84)']))
255 nc_fid.geospatial_lat_min =
float(np.nanmin(dsL1_data[
'latitude (wgs-84)']))
256 nc_fid.geospatial_lon_max =
float(np.nanmax(dsL1_data[
'longitude (wgs-84)']))
257 nc_fid.geospatial_lon_min =
float(np.nanmin(dsL1_data[
'longitude (wgs-84)']))
258 nc_fid.day_night_flag =
"Day"
259 nc_fid.earth_sun_distance_correction =
float(np.nanmean(dsL1_data[
'earth-sun distance (au)']))
262 mainLogger.info(
'Filling and writing sensor_band_parameters')
263 nc_fid.groups[
'sensor_band_parameters'].variables[
'wavelength'][:] = bands_ocip
264 nc_fid.groups[
'sensor_band_parameters'].variables[
'fwhm'][:] = widths_ocip
267 mainLogger.info(
'Filling and writing scan_line_attributes')
268 dsL1_data[
'scan_start_time'][np.isnan(dsL1_data[
'scan_start_time'])] = nc_fid.groups[
'scan_line_attributes'].variables[
'scan_start_time']._FillValue
269 nc_fid.groups[
'scan_line_attributes'].variables[
'scan_start_time'][:] = dsL1_data[
'scan_start_time']
271 dsL1_data[
'scan_end_time'][np.isnan(dsL1_data[
'scan_end_time'])] = nc_fid.groups[
'scan_line_attributes'].variables[
'scan_end_time']._FillValue
272 nc_fid.groups[
'scan_line_attributes'].variables[
'scan_end_time'][:] = dsL1_data[
'scan_end_time']
275 mainLogger.info(
'Filling and writing navigation_data')
276 dsL1_data[
'longitude (wgs-84)'][np.isnan(dsL1_data[
'longitude (wgs-84)'])] = nc_fid.groups[
'navigation_data'].variables[
'longitude']._FillValue
277 nc_fid.groups[
'navigation_data'].variables[
'longitude'][:,:] = dsL1_data[
'longitude (wgs-84)']
279 dsL1_data[
'latitude (wgs-84)'][np.isnan(dsL1_data[
'latitude (wgs-84)'])] = nc_fid.groups[
'navigation_data'].variables[
'latitude']._FillValue
280 nc_fid.groups[
'navigation_data'].variables[
'latitude'][:,:] = dsL1_data[
'latitude (wgs-84)']
282 dsL1_data[
'elevation (m)'][np.isnan(dsL1_data[
'elevation (m)'])] = nc_fid.groups[
'navigation_data'].variables[
'height']._FillValue
283 nc_fid.groups[
'navigation_data'].variables[
'height'][:,:] = dsL1_data[
'elevation (m)']
285 dsL1_data[
'path length (m)'][np.isnan(dsL1_data[
'path length (m)'])] = nc_fid.groups[
'navigation_data'].variables[
'range']._FillValue
286 nc_fid.groups[
'navigation_data'].variables[
'range'][:,:] = dsL1_data[
'path length (m)']
288 dsL1_data[
'to-sensor zenith (0 to 90 degrees from zenith)'][np.isnan(dsL1_data[
'to-sensor zenith (0 to 90 degrees from zenith)'])] = nc_fid.groups[
'navigation_data'].variables[
'sensor_zenith']._FillValue
289 nc_fid.groups[
'navigation_data'].variables[
'sensor_zenith'][:,:] = dsL1_data[
'to-sensor zenith (0 to 90 degrees from zenith)']
291 dsL1_data[
'to-sensor azimuth (0 to 360 degrees cw from n)'][np.isnan(dsL1_data[
'to-sensor azimuth (0 to 360 degrees cw from n)'])] = nc_fid.groups[
'navigation_data'].variables[
'sensor_azimuth']._FillValue
292 nc_fid.groups[
'navigation_data'].variables[
'sensor_azimuth'][:,:] = dsL1_data[
'to-sensor azimuth (0 to 360 degrees cw from n)']
294 dsL1_data[
'to-sun zenith (0 to 90 degrees from zenith)'][np.isnan(dsL1_data[
'to-sun zenith (0 to 90 degrees from zenith)'])] = nc_fid.groups[
'navigation_data'].variables[
'solar_zenith']._FillValue
295 nc_fid.groups[
'navigation_data'].variables[
'solar_zenith'][:,:] = dsL1_data[
'to-sun zenith (0 to 90 degrees from zenith)']
297 dsL1_data[
'to-sun azimuth (0 to 360 degrees cw from n)'][np.isnan(dsL1_data[
'to-sun azimuth (0 to 360 degrees cw from n)'])] = nc_fid.groups[
'navigation_data'].variables[
'solar_azimuth']._FillValue
298 nc_fid.groups[
'navigation_data'].variables[
'solar_azimuth'][:,:] = dsL1_data[
'to-sun azimuth (0 to 360 degrees cw from n)']
301 mainLogger.info(
'Filling and writing observation_data')
302 lt_ocip[np.isnan(lt_ocip)] = nc_fid.groups[
'observation_data'].variables[
'Lt']._FillValue
303 nc_fid.groups[
'observation_data'].variables[
'Lt'][:,:,:] = lt_ocip
307 mainLogger.info(
'Successfully created and filled: {:}'.format(ofile))
311 if __name__ ==
"__main__":