3 SeaDAS library for commonly used functions within other python scripts
11 from tarfile
import BLOCKSIZE
13 from datetime
import datetime, timedelta, date
16 from requests.adapters
import HTTPAdapter
17 from pathlib
import Path
38 DEFAULT_CHUNK_SIZE = 131072
50 logging.basicConfig(level=logging.DEBUG)
52 obpgSession = requests.Session()
53 obpgSession.mount(
'https://', HTTPAdapter(max_retries=ntries))
56 print(
"OBPG session started")
59 print(
"reusing existing OBPG session")
66 ctype = req.headers.get(
'Content-Type')
67 if ctype
and ctype.startswith(
'text/html'):
68 if "<title>Earthdata Login</title>" in req.text:
74 def httpdl(server, request, localpath='.', outputfilename=None, ntries=5,
75 uncompress=False, timeout=30., verbose=0, force_download=False,
76 chunk_size=DEFAULT_CHUNK_SIZE):
79 urlStr =
'https://' + server + request
82 localpath = Path(localpath)
88 if not force_download:
90 ofile = localpath / outputfilename
93 rpath = Path(request.rstrip())
94 if 'requested_files' in request:
95 rpath = Path(request.rstrip().split(
'?')[0])
96 ofile = localpath / rpath.name
97 if re.search(
r'(?<=\?)(\w+)', ofile.name):
98 ofile = Path(ofile.name.split(
'?')[0])
103 headers = {
"If-Modified-Since":modified_since.strftime(
"%a, %d %b %Y %H:%M:%S GMT")}
105 with obpgSession.get(urlStr, stream=
True, timeout=timeout, headers=headers)
as req:
107 if req.status_code != 200:
108 status = req.status_code
112 if not Path.exists(localpath):
114 Path.mkdir(localpath, mode=0o2775, parents=
True)
116 if not outputfilename:
117 cd = req.headers.get(
'Content-Disposition')
119 outputfilename = re.findall(
"filename=(.+)", cd)[0]
121 outputfilename = urlStr.split(
'/')[-1]
123 ofile = localpath / outputfilename
127 if 'last-modified' in req.headers:
128 remote_lmt = req.headers[
'last-modified']
129 remote_ftime = datetime.strptime(remote_lmt,
"%a, %d %b %Y %H:%M:%S GMT").replace(tzinfo=
None)
130 if modified_since
and not force_download:
131 if (remote_ftime - modified_since).total_seconds() < 0:
134 print(
"Skipping download of %s" % outputfilename)
137 total_length = req.headers.get(
'content-length')
138 length_downloaded = 0
139 total_length =
int(total_length)
141 print(
"Downloading %s (%8.2f MBs)" % (outputfilename,total_length /1024/1024))
143 with open(ofile,
'wb')
as fd:
145 for chunk
in req.iter_content(chunk_size=chunk_size):
147 length_downloaded += len(chunk)
150 percent_done =
int(50 * length_downloaded / total_length)
151 sys.stdout.write(
"\r[%s%s]" % (
'=' * percent_done,
' ' * (50-percent_done)))
155 if ofile.suffix
in {
'.Z',
'.gz',
'.bz2'}:
157 print(
"\nUncompressing {}".format(ofile))
160 status = compressStatus
181 compProg = {
".gz":
"gunzip -f ",
".Z":
"gunzip -f ",
".bz2":
"bunzip2 -f "}
182 exten = Path(compressed_file).suffix
183 unzip = compProg[exten]
184 cmd = [unzip,
str(compressed_file.resolve())]
185 p = subprocess.Popen(cmd, shell=
False)
186 status = os.waitpid(p.pid, 0)[1]
188 print(
"Warning! Unable to decompress %s" % compressed_file)
197 localFile = Path(localFile)
198 if not Path.is_file(localFile):
199 while localFile.suffix
in {
'.Z',
'.gz',
'.bz2'}:
200 localFile = localFile.with_suffix(
'')
202 if Path.is_file(localFile):
203 ftime = datetime.fromtimestamp(localFile.stat().st_mtime)
209 Parses file list from oceandata.sci.gsfc.nasa.gov through html source
210 intended for update_luts.py, by may have other uses
212 oldfile = Path.resolve(filename)
215 parse = re.compile(
r"(?<=(\"|\')>)\S+(\.(hdf|h5|dat|txt))")
216 if not Path.exists(oldfile):
217 print(
'Error: ' + oldfile +
' does not exist')
220 of = open(oldfile,
'r')
224 newlist.append(parse.search(line).
group(0))
234 Convert between datetime object and/or standard string formats
237 datetime_i datetime object or formatted string
238 in_datetype input format code;
239 must be present if datetime_i is a string
240 out_datetype output format code; if absent, return datetime object
242 datetype may be one of:
243 'j': Julian YYYYDDDHHMMSS
244 'g': Gregorian YYYYMMDDHHMMSS
245 't': TAI YYYY-MM-DDTHH:MM:SS.uuuuuuZ
246 'h': HDF-EOS YYYY-MM-DD HH:MM:SS.uuuuuu
254 't':
"%Y-%m-%dT%H:%M:%S.%fZ",
255 'h':
"%Y-%m-%d %H:%M:%S.%f",
257 if in_datetype
is None:
260 dateobj = datetime.strptime(datetime_i, date_time_format[in_datetype])
262 if out_datetype
is None:
265 return dateobj.strftime(date_time_format[out_datetype])
270 Offset datetime_i by dsec seconds.
273 delta = timedelta(seconds=dsec)
274 return date_convert(dateobj + delta, out_datetype=datetype)
278 Return difference in seconds.
282 return (t1-t0).total_seconds()
285 """Round to nearest "resolution" minutes, preserving format.
290 String representation of datetime, in "datetype" format
292 Format of datetime, as strftime or date_convert() code
293 resolution : integer, optional
294 Number of minutes to round to (default=5)
295 rounding : integer, optional
296 Rounding "direction", where
298 0 = round to nearest (default)
304 new_minute = (dateobj.minute // resolution) * resolution
306 new_minute = (dateobj.minute // resolution + 1) * resolution
308 new_minute = ((dateobj.minute + resolution/2.0) // resolution) * resolution
311 dateobj -= timedelta(minutes=dateobj.minute,
312 seconds=dateobj.second,
313 microseconds=dateobj.microsecond)
314 dateobj += timedelta(minutes=new_minute)
321 Delete a file from the system
322 A simple wrapper for Path.unlink
324 file_to_delete = Path(file_to_delete)
325 if Path.exists(file_to_delete):
326 Path.unlink(file_to_delete)
334 returns days since file creation
337 today = date.today().toordinal()
339 utc_create = time.localtime(p.stat().st_ctime)
341 return today - date(utc_create.tm_year, utc_create.tm_mon, utc_create.tm_mday).toordinal()
346 returns days since last file modification
349 today = date.today().toordinal()
351 utc_mtime = time.localtime(p.stat().st_mtime)
353 return today - date(utc_mtime.tm_year, utc_mtime.tm_mon, utc_mtime.tm_mday).toordinal()
358 Print a file to the standard output.
360 with open(file_to_print)
as f:
364 hasher = hashlib.sha1()
365 with open(filepath,
'rb')
as afile:
366 buf = afile.read(BLOCKSIZE)
369 buf = afile.read(BLOCKSIZE)
371 if hasher.hexdigest() == checksum:
378 Determine the satellite sensor from the file metadata
379 if unable to determine the sensor, return 'X'
382 senlst = {
'Sea-viewing Wide Field-of-view Sensor (SeaWiFS)':
'seawifs',
383 'SeaWiFS':
'seawifs',
384 'Coastal Zone Color Scanner (CZCS)':
'czcs',
385 'Ocean Color and Temperature Scanner (OCTS)':
'octs',
386 'Ocean Scanning Multi-Spectral Imager (OSMI)':
'osmi',
387 'Ocean Color Monitor OCM-2':
'ocm2',
388 'Second-generation Global Imager (SGLI)':
'sgli',
389 'GOCI':
'goci',
'Hawkeye':
'hawkeye',
'hico':
'hico',
390 'OCIS':
'ocis',
'MERIS':
'meris',
'MOS':
'mos',
'TM':
'tm',
391 'Aquarius':
'aquarius',
'VIIRS':
'viirs'}
398 if 'ASSOCIATEDPLATFORMSHORTNAME' in fileattr:
399 print(fileattr[
'ASSOCIATEDPLATFORMSHORTNAME'])
400 return fileattr[
'ASSOCIATEDPLATFORMSHORTNAME']
401 elif 'Instrument_Short_Name' in fileattr:
402 print(senlst[
str(fileattr[
'Instrument_Short_Name'])])
403 return senlst[
str(fileattr[
'Instrument_Short_Name'])]
404 elif 'Sensor' in fileattr:
405 print(senlst[(fileattr[
'Sensor']).strip()])
406 return senlst[(fileattr[
'Sensor']).strip()]
407 elif 'Sensor name' in fileattr:
408 print(senlst[(fileattr[
'Sensor name']).strip()])
409 return senlst[(fileattr[
'Sensor name']).strip()]
410 elif 'SENSOR_ID' in fileattr
and re.search(
'(OLI|ETM)', fileattr[
'SENSOR_ID']):
411 if 'SPACECRAFT_ID' in fileattr
and re.search(
'LANDSAT_9', fileattr[
'SPACECRAFT_ID']):
415 print(fileattr[
'SENSOR_ID'].strip())
416 return fileattr[
'SENSOR_ID'].strip()
417 elif 'PRODUCT' in fileattr
and re.search(
'MER', fileattr[
'PRODUCT']):
418 print(fileattr[
'PRODUCT'])
420 elif 'instrument' in fileattr:
421 print(fileattr[
'instrument'])
422 if re.search(
'(OLCI|MSI)', fileattr[
'instrument']):
423 if 'platform' in fileattr:
424 return fileattr[
'platform']
426 return senlst[(fileattr[
'instrument'])].strip()