OB.DAAC Logo
NASA Logo
Ocean Color Science Software

ocssw V2022
l1cextract.py
Go to the documentation of this file.
1 #! /usr/bin/env python3
2 
3 # Extractor for L1C 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 calendar
13 import datetime
14 import re
15 from shutil import copyfile as cp
16 import xml.etree.ElementTree as ET
17 from seadasutils.pixlin_utils import pixlin
18 from seadasutils.setupenv import env
19 #from seadasutils.MetaUtils import readMetadata
20 from seadasutils.netcdf_utils import nccopy, nccopy_grp, ncsubset_vars
21 #import seadasutils.ProcUtils as ProcUtils
22 
23 class extract:
24 
25  def __init__(self, idir, odir=None,
26  north=None, south=None, west=None, east=None,
27  spixl=None, epixl=None, sline=None, eline=None,
28  verbose=False):
29  # inputs
30  self.idir = pathlib.Path(idir)
31  self.odir = pathlib.Path(odir)
32  self.north = north
33  self.south = south
34  self.west = west
35  self.east = east
36  self.spixl = spixl
37  self.epixl = epixl
38  self.sline = sline
39  self.eline = eline
40  self.verbose = verbose
41 
42  # defaults
43  self.runtime = None
44  self.attrs = None
45 
46  # unused, but needed by setupenv.py
47  self.dirs = {}
48  self.ancdir = None
49  self.curdir = False
50  self.sensor = None
51  env(self) # run setupenv
52 
53  def runextract(self, subset):
54 
55  srcfile = self.idir
56  if srcfile.exists():
57  dstfile = self.odir
58  if self.verbose:
59  print('Extracting', srcfile)
60 
61  ncsubset_vars(srcfile, dstfile, subset, timestamp=self.runtime)
62 
63  # Read infile as netCDF dataset
64  infile = netCDF4.Dataset(srcfile, 'r')
65  # Read and write outfile as netCDF4 dataset
66  outfile = netCDF4.Dataset(dstfile, 'r+')
67 
68  # Update nadir_bin
69  nadir_bin = np.dtype('int32').type(infile.nadir_bin)
70  if (nadir_bin > self.epixl):
71  outfile.nadir_bin = np.dtype('int32').type(-1)
72  else:
73  outfile.nadir_bin = np.dtype('int32').type(nadir_bin - (self.spixl + 1))
74 
75  #_____________________________________________________________________________________________________________________________________
76  # |
77  # Add extract_pixel/line_start/stop: |
78  #_________________________________________________________________________________|
79 
80  if 'extract_pixel_start' in infile.ncattrs():
81  outfile.extract_pixel_start = np.dtype('int32').type(infile.extract_pixel_start + self.spixl + 1)
82  outfile.extract_pixel_stop = np.dtype('int32').type(infile.extract_pixel_stop + self.epixl + 1)
83  outfile.extract_line_start = np.dtype('int32').type(infile.extract_line_start + self.sline + 1)
84  outfile.extract_line_stop = np.dtype('int32').type(infile.extract_line_stop + self.eline + 1)
85  else:
86  outfile.extract_pixel_start = np.dtype('int32').type(self.spixl + 1)
87  outfile.extract_pixel_stop = np.dtype('int32').type(self.epixl + 1)
88  outfile.extract_line_start = np.dtype('int32').type(self.sline + 1)
89  outfile.extract_line_stop = np.dtype('int32').type(self.eline + 1)
90 
91  #_____________________________________________________________________________________________________________________________________
92  # |
93  # Modify time_coverage_start/end of output file: |
94  #_________________________________________________________________________________|
95 
96  # Read infile as netCDF dataset
97  infile = netCDF4.Dataset(srcfile, 'r')
98  # Read and write outfile as netCDF4 dataset
99  outfile = netCDF4.Dataset(dstfile, 'r+')
100 
101  # Account for different file formats
102  try:
103  if 'nadir_view_time' in infile.groups['bin_attributes'].variables:
104  # Number of seconds at infile time_coverage_start/end
105  infile_start_sec = infile.groups['bin_attributes'].variables['nadir_view_time'][0]
106  infile_end_sec = infile.groups['bin_attributes'].variables['nadir_view_time'][infile.dimensions['bins_along_track'].size - 1]
107  # Number of seconds at outfile time_coverage_start/end
108  outfile_start_sec = outfile.groups['bin_attributes'].variables['nadir_view_time'][0]
109  outfile_end_sec = outfile.groups['bin_attributes'].variables['nadir_view_time'][outfile.dimensions['bins_along_track'].size - 1]
110 
111  # Take infile time_coverage_start/end
112  infile_start_time = infile.time_coverage_start
113  infile_end_time = infile.time_coverage_end
114 
115  # Extract year, month, day, hours, minutes, seconds from infile time coverage start/end
116  start_form = datetime.datetime.strptime(infile_start_time[0:20], '%Y-%m-%dT%H:%M:%SZ')
117  end_form = datetime.datetime.strptime(infile_end_time[0:20], '%Y-%m-%dT%H:%M:%SZ')
118  # Extract year,...,seconds from epoch
119  epoch = datetime.datetime.strptime('1970 01 01 00 00 00','%Y %m %d %H %M %S')
120 
121  except:
122  if 'time_nadir' in infile.groups['geolocation_data'].variables:
123  #infile.groups['geolocation_data']['time_nadir'].valid_max = 86400.00
124  #infile.groups['geolocation_data']['time_nadir'].valid_min = 0.00
125 
126  # Number of seconds at infile time_coverage_start/end
127  infile_start_sec: float = infile.groups['geolocation_data'].variables['time_nadir'][0]
128  infile_end_sec = infile.groups['geolocation_data'].variables['time_nadir'][infile.dimensions['bins_along_track'].size - 1]
129  # Number of seconds at outfile time_coverage_start/end
130  outfile_start_sec = outfile.groups['geolocation_data'].variables['time_nadir'][0]
131  outfile_end_sec = outfile.groups['geolocation_data'].variables['time_nadir'][outfile.dimensions['bins_along_track'].size - 1]
132 
133  # Take infile time_coverage_start/end
134  infile_start_time = infile.time_coverage_start
135  infile_end_time = infile.time_coverage_end
136 
137  # Extract year, month, day, hours, minutes, seconds from infile time coverage start/end
138  start_form = datetime.datetime.strptime(infile_start_time[0:20], '%Y-%m-%dT%H:%M:%S')
139  end_form = datetime.datetime.strptime(infile_end_time[0:20], '%Y-%m-%dT%H:%M:%S')
140  # Extract year,...,seconds from epoch
141  epoch = datetime.datetime.strptime('1970 01 01 00 00 00','%Y %m %d %H %M %S')
142 
143  # Determine difference in time from infile time_coverage_start to epoch
144  diff_start = start_form - epoch
145  # Determine difference in time from infile time_coverage_end to epoch
146  diff_end = end_form - epoch
147 
148  # Calculate the number of seconds contained in the time difference in previous step
149  diff_sec_start = diff_start.total_seconds()
150  # Calculate the number of seconds contained in the time difference in previous step
151  diff_sec_end = diff_end.total_seconds()
152 
153  # Seconds between infile/outfile time_coverage_start/end
154  diff_infile_outfile_start = outfile_start_sec - infile_start_sec
155  diff_infile_outfile_end = outfile_end_sec - infile_end_sec
156 
157  # Add the input/output file time_coverage_start/end difference to the infile time_coverage_start/end # of seconds
158  outfile_tot_start_sec = diff_sec_start + diff_infile_outfile_start
159  outfile_tot_end_sec = diff_sec_end + diff_infile_outfile_end
160 
161  # Create time structure for the outfile time_coverage_start/end
162  outfile_start_time_since = time.gmtime(outfile_tot_start_sec)
163  outfile_end_time_since = time.gmtime(outfile_tot_end_sec)
164 
165  # Extract year, month, day, hours, minutes, seconds from outfile start/end time structs
166  ostart_y = outfile_start_time_since.tm_year
167  ostart_mon = "{0:0=2d}".format(outfile_start_time_since.tm_mon)
168  ostart_d = "{0:0=2d}".format(outfile_start_time_since.tm_mday)
169  ostart_h = "{0:0=2d}".format(outfile_start_time_since.tm_hour)
170  ostart_min = "{0:0=2d}".format(outfile_start_time_since.tm_min)
171  ostart_s = "{0:0=2d}".format(outfile_start_time_since.tm_sec)
172 
173  oend_y = outfile_end_time_since.tm_year
174  oend_mon = "{0:0=2d}".format(outfile_end_time_since.tm_mon)
175  oend_d = "{0:0=2d}".format(outfile_end_time_since.tm_mday)
176  oend_h = "{0:0=2d}".format(outfile_end_time_since.tm_hour)
177  oend_min = "{0:0=2d}".format(outfile_end_time_since.tm_min)
178  oend_s = "{0:0=2d}".format(outfile_end_time_since.tm_sec)
179 
180  # Change outfile time_coverage_start/end
181  outfile.time_coverage_start = str(ostart_y) + '-' + str(ostart_mon) + '-' + str(ostart_d) + 'T' + str(ostart_h) + ':' + str(ostart_min) + ':' + str(ostart_s) + 'Z'
182  outfile.time_coverage_end = str(oend_y) + '-' + str(oend_mon) + '-' + str(oend_d) + 'T' + str(oend_h) + ':' + str(oend_min) + ':' + str(oend_s) + 'Z'
183 
184  #_____________________________________________________________________________________________________________________________________
185  # |
186  # Gring Calculation: |
187  #_________________________________________________________________________________|
188 
189  lat = outfile.dimensions['bins_along_track'].size - 1
190  lon = outfile.dimensions['bins_across_track'].size - 1
191 
192  latitude = outfile.groups['geolocation_data'].variables['latitude']
193  longitude = outfile.groups['geolocation_data'].variables['longitude']
194 
195  lon_min = longitude[0, 0]
196  lon_max = longitude[lat, lon]
197  lat_min = latitude[0, 0]
198  lat_max = latitude[lat , lon]
199 
200  # Degrees to separate latitude points, here it is 20
201  lat_add = np.dtype('int32').type((lat_max - lat_min) / 20)
202  # Place one point in the middle of longitude
203  lon_add = (lon_max - lon_min) / 2
204 
205  lat_l = []
206  lat_r = []
207  lon_l = []
208  lon_r = []
209 
210  # add latitude right values
211  for i in range(0, lat - 1, np.dtype('int32').type(lat/lat_add)):
212  lat_r.append(i)
213  # add longitude left/right values
214  lon_l.append(0)
215  lon_r.append(lon)
216 
217  # add latitude left values
218  lat_l = list(reversed(lat_r))
219 
220  lon_values = []
221  lat_values = []
222 
223  # add longitude up values
224  lon_u = [lon, lon/2, 0]
225  # add latitude up values
226  lat_u = [lat, lat, lat]
227  # add longitude down values
228  lon_d = list(reversed(lon_u))
229  # add longitude up values
230  lat_d = [0, 0, 0]
231 
232  num = 0;
233  for i in range(len(lat_u)):
234  lat_values.append(np.dtype('float32').type(lat_u[i]))
235  lon_values.append(lon_u[i])
236  num += 1
237  for i in range(len(lat_l)):
238  lat_values.append(lat_l[i])
239  lon_values.append(lon_l[i])
240  num += 1
241  for i in range(len(lat_d)):
242  lat_values.append(lat_d[i])
243  lon_values.append(lon_d[i])
244  num += 1
245  for i in range(len(lat_r)):
246  lat_values.append(lat_r[i])
247  lon_values.append(lon_r[i])
248  num += 1
249 
250  p_seq = []
251 
252  for i in range(num):
253  p_seq.append(np.dtype('int32').type(i + 1))
254 
255  outfile.groups['geolocation_data'].setncattr('gringpointlatitude', lat_values)
256  outfile.groups['geolocation_data'].setncattr('gringpointlongitude', lon_values)
257  outfile.groups['geolocation_data'].setncattr('gringpointsequence', p_seq)
258 
259  return 0
260 
261  def getpixlin(self):
262  if self.verbose:
263  print("north={} south={} west={} east={}".
264  format(self.north, self.south, self.west, self.east))
265 
266  # run lonlat2pixline
267  pl = pixlin(file=self.idir,
268  north=self.north, south=self.south,
269  west=self.west, east=self.east,
270  verbose=self.verbose)
271  pl.lonlat2pixline(zero=False) # using 1-based indices
272  self.spixl, self.epixl, self.sline, self.eline = \
273  (pl.spixl, pl.epixl, pl.sline, pl.eline)
274  return pl.status
275 
276  def run(self):
277  # convert to zero-based index
278  self.spixl, self.epixl, self.sline, self.eline = \
279  (v-1 for v in (self.spixl, self.epixl, self.sline, self.eline))
280 
281  # extract file
282  subset = {'bins_along_track':[self.spixl, self.epixl],
283  'bins_across_track': [self.sline, self.eline]}
284  self.runextract(subset)
285 
286  #return status
287  return 0
288 
289 if __name__ == "__main__":
290 
291  # parse command line
292  parser = argparse.ArgumentParser(
293  description='Extract specified area from OLCI Level 1C 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('idir',
298  help='Level 1C input file path')
299  parser.add_argument('odir', nargs='?',
300  help='output file path')
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')
310  group2.add_argument('--epixl', type=int, help='end pixel')
311  group2.add_argument('--sline', type=int, help='start line')
312  group2.add_argument('--eline', type=int, help='end line')
313 
314  if len(sys.argv) == 1:
315  parser.print_help()
316  sys.exit(1)
317  args = parser.parse_args()
318 
319  # initialize
320  this = extract(idir=args.idir,
321  odir=args.odir,
322  north=args.north,
323  south=args.south,
324  west=args.west,
325  east=args.east,
326  spixl=args.spixl,
327  epixl=args.epixl,
328  sline=args.sline,
329  eline=args.eline,
330  verbose=args.verbose)
331 
332 
333  # input value checks
334  goodlatlons = None not in (this.north, this.south, this.west, this.east)
335  goodindices = None not in (this.spixl, this.epixl, this.sline, this.eline)
336  if (goodlatlons and goodindices):
337  print("ERROR: Specify either geographic limits or pixel/line ranges, not both.")
338  sys.exit(1)
339  elif goodlatlons:
340  status = this.getpixlin()
341  if status not in (0, 110):
342  print("No extract; lonlat2pixline status =", status)
343  exit(status)
344  elif goodindices:
345  pass
346  else:
347  print("ERROR: Specify all values for either geographic limits or pixel/line ranges.")
348  sys.exit(1)
349 
350  # run
351  status = this.run()
352 
353  exit(status)
def getpixlin(self)
Definition: l1cextract.py:261
list(APPEND LIBS ${PGSTK_LIBRARIES}) add_executable(atteph_info_modis atteph_info_modis.c) target_link_libraries(atteph_info_modis $
Definition: CMakeLists.txt:7
def __init__(self, idir, odir=None, north=None, south=None, west=None, east=None, spixl=None, epixl=None, sline=None, eline=None, verbose=False)
Definition: l1cextract.py:25
def runextract(self, subset)
Definition: l1cextract.py:53
def env(self)
Definition: setupenv.py:7
const char * str
Definition: l1c_msi.cpp:35
def ncsubset_vars(srcfile, dstfile, subset, verbose=False, **kwargs)