NASA Logo
Ocean Color Science Software

ocssw V2022
l1bextract_oci.py
Go to the documentation of this file.
1 #! /usr/bin/env python3
2 
3 # Extractor for L1B_OCI files
4 
5 import argparse
6 import netCDF4
7 import pathlib
8 from os.path import basename
9 import numpy as np
10 import sys
11 import time
12 import datetime
13 from shutil import copyfile as cp
14 from seadasutils.pixlin_utils import pixlin
15 from seadasutils.setupenv import env
16 #from seadasutils.MetaUtils import readMetadata
17 from seadasutils.netcdf_utils import ncsubset_vars
18 #import seadasutils.ProcUtils as ProcUtils
19 
20 versionStr = "1.1 (2024-03-15)"
21 
22 class extract:
23 
24  def __init__(self, ifile, ofile=None,
25  north=None, south=None, west=None, east=None,
26  spixl=None, epixl=None, sline=None, eline=None,
27  verbose=False):
28  # inputs
29  self.ifile = pathlib.Path(ifile)
30  self.ofile = pathlib.Path(ofile)
31  self.north = north
32  self.south = south
33  self.west = west
34  self.east = east
35  self.spixl = spixl
36  self.epixl = epixl
37  self.sline = sline
38  self.eline = eline
39  self.verbose = verbose
40 
41  # defaults
42  self.runtime = None
43  self.attrs = None
44 
45  # unused, but needed by setupenv.py
46  self.dirs = {}
47  self.ancdir = None
48  self.curdir = False
49  self.sensor = None
50  env(self) # run setupenv
51 
52  def runextract(self, subset):
53 
54  srcfile = self.ifile
55  if srcfile.exists():
56  dstfile = self.ofile
57  if self.verbose:
58  print('Extracting', srcfile)
59 
60  ncsubset_vars(srcfile, dstfile, subset, timestamp=self.runtime)
61 
62  # Read infile as netCDF dataset
63  infile = netCDF4.Dataset(srcfile, 'r')
64  # Read and write outfile as netCDF4 dataset
65  outfile = netCDF4.Dataset(dstfile, 'r+')
66 
67  #_____________________________________________________________________________________________________________________________________
68  # |
69  # Modify time_coverage_start/end of output file: |
70  #_________________________________________________________________________________|
71 
72  # Read infile as netCDF dataset
73  infile = netCDF4.Dataset(srcfile, 'r')
74  # Read and write outfile as netCDF4 dataset
75  outfile = netCDF4.Dataset(dstfile, 'r+')
76 
77  # Number of seconds at infile time_coverage_start/end
78  infile_start_sec: float = infile.groups['scan_line_attributes'].variables['time'][0]
79  infile_end_sec = infile.groups['scan_line_attributes'].variables['time'][infile.dimensions['number_of_scans'].size - 1]
80  # Number of seconds at outfile time_coverage_start/end
81  outfile_start_sec = outfile.groups['scan_line_attributes'].variables['time'][0]
82  outfile_end_sec = outfile.groups['scan_line_attributes'].variables['time'][outfile.dimensions['number_of_scans'].size - 1]
83 
84  # Take infile time_coverage_start/end
85  infile_start_time = infile.time_coverage_start
86  infile_end_time = infile.time_coverage_end
87 
88  # Extract year, month, day, hours, minutes, seconds from infile time coverage start/end
89  start_form = datetime.datetime.strptime(infile_start_time[0:20] + '000', '%Y-%m-%dT%H:%M:%S.%f')
90  end_form = datetime.datetime.strptime(infile_end_time[0:20] + '000', '%Y-%m-%dT%H:%M:%S.%f')
91  # Extract year,...,seconds from epoch
92  epoch = datetime.datetime.strptime('1970 01 01 00 00 00.000','%Y %m %d %H %M %S.%f')
93  # Determine difference in time from infile time_coverage_start to epoch
94  diff_start = start_form - epoch
95  # Determine difference in time from infile time_coverage_end to epoch
96  diff_end = end_form - epoch
97 
98  # Calculate the number of seconds contained in the time difference in previous step
99  diff_sec_start = diff_start.total_seconds()
100  # Calculate the number of seconds contained in the time difference in previous step
101  diff_sec_end = diff_end.total_seconds()
102 
103  # Seconds between infile/outfile time_coverage_start/end
104  diff_infile_outfile_start = outfile_start_sec - infile_start_sec
105  diff_infile_outfile_end = outfile_end_sec - infile_end_sec
106 
107  # Add the input/output file time_coverage_start/end difference to the infile time_coverage_start/end # of seconds
108  outfile_tot_start_sec = diff_sec_start + diff_infile_outfile_start
109  outfile_tot_end_sec = diff_sec_end + diff_infile_outfile_end
110 
111  # Create time structure for the outfile time_coverage_start/end
112  outfile_start_time_since = time.gmtime(outfile_tot_start_sec)
113  outfile_end_time_since = time.gmtime(outfile_tot_end_sec)
114 
115  # Extract year, month, day, hours, minutes, seconds from outfile start/end time structs
116  ostart_y = outfile_start_time_since.tm_year
117  ostart_mon = "{0:0=2d}".format(outfile_start_time_since.tm_mon)
118  ostart_d = "{0:0=2d}".format(outfile_start_time_since.tm_mday)
119  ostart_h = "{0:0=2d}".format(outfile_start_time_since.tm_hour)
120  ostart_min = "{0:0=2d}".format(outfile_start_time_since.tm_min)
121  ostart_s = "{0:0=2d}".format(outfile_start_time_since.tm_sec)
122 
123  oend_y = outfile_end_time_since.tm_year
124  oend_mon = "{0:0=2d}".format(outfile_end_time_since.tm_mon)
125  oend_d = "{0:0=2d}".format(outfile_end_time_since.tm_mday)
126  oend_h = "{0:0=2d}".format(outfile_end_time_since.tm_hour)
127  oend_min = "{0:0=2d}".format(outfile_end_time_since.tm_min)
128  oend_s = "{0:0=2d}".format(outfile_end_time_since.tm_sec)
129 
130  # Change outfile time_coverage_start/end
131  outfile.time_coverage_start = str(ostart_y) + '-' + str(ostart_mon) + '-' + str(ostart_d) + 'T' + str(ostart_h) + ':' + str(ostart_min) + ':' + str(ostart_s)
132  outfile.time_coverage_end = str(oend_y) + '-' + str(oend_mon) + '-' + str(oend_d) + 'T' + str(oend_h) + ':' + str(oend_min) + ':' + str(oend_s)
133 
134  #_____________________________________________________________________________________________________________________________________
135  # |
136  # Gring Calculation: |
137  #_________________________________________________________________________________|
138 
139  outfile.set_auto_mask(False)
140  number_of_scans = outfile.dimensions['number_of_scans'].size - 1
141  SWIR_pixels = outfile.dimensions['SWIR_pixels'].size - 1
142 
143  latitude = outfile.groups['geolocation_data'].variables['latitude']
144  longitude = outfile.groups['geolocation_data'].variables['longitude']
145 
146  lon_min = longitude[0, 0]
147  lon_max = longitude[number_of_scans, SWIR_pixels]
148  lat_min = latitude[0, 0]
149  lat_max = latitude[number_of_scans, SWIR_pixels]
150 
151  # Degrees to separate latitude points, here it is 20
152  lat_add = float((lat_max - lat_min) / 20)
153 
154  # Place one point in the middle of longitude
155  lat_l = []
156  lat_r = []
157  lon_l = []
158  lon_r = []
159 
160  # add latitude right values
161  if lat_add > 0 and int(number_of_scans/lat_add) != 0:
162  for i in range(0, number_of_scans - 1, int(number_of_scans/lat_add)):
163  lat_r.append(i)
164  # add longitude left/right values
165  lon_l.append(0)
166  lon_r.append(SWIR_pixels)
167  else:
168  for i in range(0, number_of_scans - 1, 1):
169  lat_r.append(i)
170  # add longitude left/right values
171  lon_l.append(0)
172  lon_r.append(SWIR_pixels)
173 
174  # add latitude left values
175  lat_l = list(reversed(lat_r))
176 
177  # add longitude up values
178  lon_u = [SWIR_pixels, (SWIR_pixels/2), 0]
179  # add latitude up values
180  lat_u = [number_of_scans, number_of_scans, number_of_scans]
181  # add longitude down values
182  lon_d = list(reversed(lon_u))
183  # add longitude up values
184  lat_d = [0, 0, 0]
185 
186  lat_values_u = []
187  lon_values_u = []
188  lat_values_d = []
189  lon_values_d = []
190  lat_values_l = []
191  lon_values_l = []
192  lat_values_r = []
193  lon_values_r = []
194 
195  num = 0
196  for i in range(len(lat_u)):
197  lat_values_u.append(float(lat_u[i]))
198  lon_values_u.append(float(lon_u[i]))
199  num += 1
200  for i in range(len(lat_l)):
201  lat_values_l.append(float(lat_l[i]))
202  lon_values_l.append(float(lon_l[i]))
203  num += 1
204  for i in range(len(lat_d)):
205  lat_values_d.append(float(lat_d[i]))
206  lon_values_d.append(float(lon_d[i]))
207  num += 1
208  for i in range(len(lat_r)):
209  lat_values_r.append(float(lat_r[i]))
210  lon_values_r.append(float(lon_r[i]))
211  num += 1
212 
213  p_seq = []
214 
215  for i in range(num):
216  p_seq.append(np.dtype('int32').type(i + 1))
217 
218  args_lat = (lat_values_u, lat_values_l, lat_values_d, lat_values_r)
219  args_lon = (lon_values_u, lon_values_l, lon_values_d, lon_values_r)
220  lat_values = np.concatenate(args_lat)
221  lon_values = np.concatenate(args_lon)
222 
223  g_lat = []
224  g_lon = []
225 
226  for i in range(0,len(lat_values)):
227  g_lat.append(latitude[int(lat_values[i])][int(lon_values[i])])
228  g_lon.append(longitude[int(lat_values[i])][int(lon_values[i])])
229 
230  outfile.setncattr('GRingPointLatitude', g_lat)
231  outfile.setncattr('GRingPointLongitude', g_lon)
232  outfile.setncattr('GRingPointSequenceNo', p_seq)
233 
234  #_____________________________________________________________________________________________________________________________________
235  # |
236  # Geolocation lat/lon min/max update: |
237  #_________________________________________________________________________________|
238  def minmax(arr):
239  return arr.min(), arr.max()
240 
241  lat_min, lat_max = latitude[0, 0], latitude[number_of_scans, SWIR_pixels]
242  lon_min, lon_max = longitude[0, 0], longitude[number_of_scans, SWIR_pixels]
243 
244  outfile.setncattr('geospatial_lat_min', lat_min)
245  outfile.setncattr('geospatial_lat_max', lat_max)
246  outfile.setncattr('geospatial_lon_min', lon_min)
247  outfile.setncattr('geospatial_lon_max', lon_max)
248 
249  return 0
250 
251  def getpixlin(self):
252  if self.verbose:
253  print("north={} south={} west={} east={}".
254  format(self.north, self.south, self.west, self.east))
255 
256  # run lonlat2pixline
257  pl = pixlin(geofile=self.ifile,
258  north=self.north, south=self.south,
259  west=self.west, east=self.east,
260  verbose=self.verbose)
261  pl.lonlat2pixline(zero=False) # using 1-based indices
262  self.spixl, self.epixl, self.sline, self.eline = \
263  (pl.spixl, pl.epixl, pl.sline, pl.eline)
264  return pl.status
265 
266  def run(self):
267  # convert to zero-based index
268  self.spixl, self.epixl, self.sline, self.eline = \
269  (v-1 for v in (self.spixl, self.epixl, self.sline, self.eline))
270 
271  # extract file
272  subset = {'SWIR_pixels':[self.spixl, self.epixl],
273  'ccd_pixels': [self.spixl, self.epixl],
274  'number_of_scans': [self.sline, self.eline]}
275  self.runextract(subset)
276 
277  #return status
278  return 0
279 
280 def chk_pixl(args, infile):
281  if args.epixl == -1:
282  args.epixl = infile.dimensions['SWIR_pixels'].size
283  if args.eline == -1:
284  args.eline = infile.dimensions['number_of_scans'].size
285  return args.spixl, args.epixl, args.sline, args.eline
286 
287 if __name__ == "__main__":
288  print("l1bextract_oci", versionStr)
289  status = 0
290 
291  # parse command line
292  parser = argparse.ArgumentParser(
293  description='Extract specified area from OCI Level 1B files.',
294  epilog='Specify either geographic limits or pixel/line ranges, not both.')
295  parser.add_argument('-v', '--verbose', help='print status messages',
296  action='store_true')
297  parser.add_argument('ifile',
298  help='Level 1B input file')
299  parser.add_argument('ofile', nargs='?',
300  help='output file')
301 
302  group1 = parser.add_argument_group('geographic limits')
303  group1.add_argument('-n', '--north', type=float, help='northernmost latitude')
304  group1.add_argument('-s', '--south', type=float, help='southernmost latitude')
305  group1.add_argument('-w', '--west', type=float, help='westernmost longitude')
306  group1.add_argument('-e', '--east', type=float, help='easternmost longitude')
307 
308  group2 = parser.add_argument_group('pixel/line ranges (1-based)')
309  group2.add_argument('--spixl', type=int, help='start pixel', default = 1)
310  group2.add_argument('--epixl', type=int, help='end pixel', default = -1)
311 
312  group2.add_argument('--sline', type=int, help='start line', default = 1)
313  group2.add_argument('--eline', type=int, help='end line', default = -1)
314 
315  if len(sys.argv) == 1:
316  parser.print_help()
317  sys.exit(1)
318  args = parser.parse_args()
319 
320  pixel_bounds_specified = not (args.eline == -1 and args.sline == 1 and args.epixl == -1 and args.spixl == 1)
321 
322  # Read infile as netCDF dataset
323  infile = netCDF4.Dataset(args.ifile, 'r')
324 
325  args.spixl, args.epixl, args.sline, args.in_eline = chk_pixl(args, infile)
326 
327  # initialize
328  this = extract(ifile=args.ifile,
329  ofile=args.ofile,
330  north=args.north,
331  south=args.south,
332  west=args.west,
333  east=args.east,
334  spixl=args.spixl,
335  epixl=args.epixl,
336  sline=args.sline,
337  eline=args.eline,
338  verbose=args.verbose)
339 
340  # input value checks
341  goodlatlons = None not in (this.north, this.south, this.west, this.east)
342  if (goodlatlons and pixel_bounds_specified):
343  print("ERROR: Specify either geographic limits or pixel/line ranges, not both.")
344  sys.exit(1)
345  elif (goodlatlons and not pixel_bounds_specified):
346  status = this.getpixlin()
347  if status not in (0, 110):
348  print("No extract; lonlat2pixline status =", status)
349  exit(status)
350  status = this.run()
351  elif (pixel_bounds_specified and not goodlatlons):
352  status = this.run()
353  else:
354  print("No extract; subset not specified")
355  status = 1
356 
357  exit(status)
def runextract(self, subset)
def chk_pixl(args, infile)
def __init__(self, ifile, ofile=None, north=None, south=None, west=None, east=None, spixl=None, epixl=None, sline=None, eline=None, verbose=False)
list(APPEND LIBS ${NETCDF_LIBRARIES}) find_package(GSL REQUIRED) include_directories($
Definition: CMakeLists.txt:8
void print(std::ostream &stream, const char *format)
Definition: PrintDebug.hpp:38
def env(self)
Definition: setupenv.py:7
Definition: aerosol.c:136
def ncsubset_vars(srcfile, dstfile, subset, verbose=False, **kwargs)