-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathawips2_gtm_edr.py
551 lines (457 loc) · 21.2 KB
/
awips2_gtm_edr.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
awips2_gtm_edr
~~~~~~~~~~~~~~
Transcode HDF5 format Ground-Track Mercator (GTM) Imagery EDR files to NetCDF4 compatible with AWIPS2.
Note on .nc.wmo files:
These are NetCDF4 files which have been gzipped, then prepended with a 21-character header e.g. "TIBP99 KNES 000000\r\r\n"
To unpack them back to NetCDF files at the command line,
dd ibs=1 iseek=21 if=VIIRS_I4_IMG_EDR_TIPB04_KNES_npp_s201307141706188_e201307141707531_c201307311502170.nc.gz.wmo |gunzip >awips2.nc
ncdump -h awips2.nc
To do a quick visualization using ipython, try this:
ipython --pylab
from netCDF4 import Dataset
nc = Dataset('awips2.nc', 'r+')
bt = nc.variables['BrightnessTemperature@VIIRS-I4-IMG-EDR'] # variable name will vary by band; see ncdump output
bt.missing_value = 65535 # this is a hack to work around multiple-missing-value issue, note it modifies the actual file
imshow(bt[:,:])
From code written by DJHoese, Apr2013
:copyright: 2013 by University of Wisconsin Regents, see AUTHORS for more details
:license: GPLv3, see LICENSE for more details
:author: RKGarcia
"""
__author__ = 'rayg'
__docformat__ = 'reStructuredText'
import os
import sys
import re
import logging
import datetime
import h5py
from netCDF4 import Dataset
from collections import namedtuple
import numpy as np
from subprocess import PIPE, Popen
LOG = logging.getLogger(__name__)
# This is modified from adl_geo_ref:RE_NPP due to things like VI1BO instead of SVI01; kind and band are combined
RE_NPP_EDR = re.compile('(?P<kindband>[A-Z0-9]+)_(?P<sat>[A-Za-z0-9]+)_d(?P<date>\d+)_t(?P<start_time>\d+)_e(?P<end_time>\d+)_b(?P<orbit>\d+)_c(?P<created_time>\d+)_(?P<site>[a-zA-Z0-9]+)_(?P<domain>[a-zA-Z0-9]+)\.h5')
def h5path(elf, path, groups=None):
LOG.debug('path search for %s' % repr(path))
if not path:
return elf
if isinstance(path, str):
return h5path(elf, path.split('/'), groups)
LOG.debug('compiling %r' % path)
assert(isinstance(path[0], str))
rx = re.compile(path[0])
for k, v in elf.iteritems():
m = rx.match(k)
LOG.debug('checking %s' % k)
if not m:
continue
if groups is not None:
LOG.debug('updating information to include %s' % repr(m.groupdict()))
groups.update(m.groupdict())
return h5path(v, path[1:], groups) if len(path) > 1 else v
LOG.warning('no match for %s' % path[0])
return None
EDR_PATH = r'All_Data/(?P<collection>VIIRS-.*-EDR)_All/(?P<kind>(?:BrightnessTemperature)|(?:Reflectance)|(?:Albedo))'
# where to find Latitude and Longitude
GEO_PATH = r'All_Data/(?P<geo_collection>VIIRS-.*-EDR-GEO)_All'
TIME_PATH = r'Data_Products/VIIRS-.*-EDR-GEO/VIIRS-.*-EDR-GEO_Aggr'
GRING_PATH = r'Data_Products/VIIRS-.*-EDR-GEO/VIIRS-.*-EDR-GEO_Gran_0'
latlon = namedtuple('g_ring', 'lat lon')
# Time helpers
class UTC(datetime.tzinfo):
"""Time zone class for UTC
"""
ZERO = datetime.timedelta(0)
def utcoffset(self, dt):
return self.ZERO
def tzname(self, dt):
return "UTC"
def dst(self, dt):
return self.ZERO
def utc_now():
return datetime.datetime.utcnow().replace(tzinfo=UTC())
def nppdatetime(d, t, e=None):
"""
given d,t,e strings from filename, return datetime object or objects
"""
assert(len(d) == 8)
t = t.split('.')[0]
assert(len(t) in (6,7))
start = datetime.datetime.strptime(d + 'T' + t[:6], '%Y%m%dT%H%M%S')
start = start.replace(tzinfo=UTC())
if len(t) == 7:
start = start.replace(microsecond=int(t[6]) * 100000)
if not e:
return start
end = nppdatetime(d,e)
if end < start:
end += datetime.timedelta(days=1)
return start, end
class Granule(object):
"""
Tool for accessing necessary components of EDR + GEO HDF5 files in accordance to CDFCB
"""
kind = None
collection = None
edr_path = None
edr = None
geo_path = None
geo = None
def __init__(self, edr_path, geo_path=None):
self.edr_path = edr_path
self.edr = h5py.File(edr_path, 'r')
if geo_path is None:
geo_ref = self.edr.attrs.get('N_GEO_Ref', None)
if geo_ref:
geo_ref = str(geo_ref[0,0])
LOG.debug("N_GEO_Ref is {0:s}".format(geo_ref))
_, geo_filename = os.path.split(geo_ref)
geo_dir, _ = os.path.split(edr_path)
geo_path = os.path.join(geo_dir, geo_filename)
LOG.debug("using {0:s} as geo path via N_GEO_Ref".format(geo_path))
else:
LOG.info('no N_GEO_Ref; assuming integrated geolocation')
if geo_path:
self.geo_path = geo_path
self.geo = h5py.File(geo_path, 'r')
else:
self.geo_path = edr_path
self.geo = self.edr
self._info = {}
h5v = h5path(self.edr, EDR_PATH, self._info)
self._data = h5v[:]
self._geo_var = h5path(self.geo, GEO_PATH, self._info)
@property
def factors(self):
"""
Find the RadianceFactors or BrightnessFactors hdf5 variable, depending on self.kind
:return: numpy array
"""
# this gets a little tricky
# search for variable ending with Factors but starting with first 3 letters of kind
pat = r'All_Data/(?P<collection>VIIRS-.*-EDR)_All/{0:s}.*Factors'.format(self.kind[:3])
return h5path(self.edr, pat)[:]
@property
def data(self):
return self._data
@property
def collection(self):
return self._info['collection']
@property
def geo_collection(self):
return self._info['geo_collection']
@property
def kind(self):
return self._info['kind']
@property
def along_track_pixels(self):
return self._data.shape[0]
@property
def cross_track_pixels(self):
return self._data.shape[1]
@property
def factor_count(self):
return self.factors.shape[0]
@property
def lat_lon(self):
base = self._geo_var
return latlon(base['Latitude'][:], base['Longitude'][:])
@property
def lat_lon_envelope(self):
# Make data manipulations
# FIXME: missing value testing? short granule testing?
lat_data, lon_data = self.lat_lon
mid_idx = lat_data.shape[-1] / 2
lat_envelope = lat_data[:, (0, mid_idx, -1)]
lon_envelope = lon_data[:, (0, mid_idx, -1)]
return latlon(lat_envelope, lon_envelope)
@property
def start_end(self):
base = h5path(self.geo, TIME_PATH)
start_date = base.attrs["AggregateBeginningDate"][0, 0]
start_time = base.attrs["AggregateBeginningTime"][0, 0]
end_date = base.attrs["AggregateEndingDate"][0, 0]
end_time = base.attrs["AggregateEndingTime"][0, 0]
return nppdatetime(start_date, start_time), nppdatetime(end_date, end_time)
@property
def gring_lat_lon(self):
# FIXME this doesn't deal with aggregates having more than one granule, consistency could be improved on h5 paths
base = h5path(self.geo, GRING_PATH)
return latlon(base.attrs["G-Ring_Latitude"][:].astype(np.float64), base.attrs["G-Ring_Longitude"][:].astype(np.float64))
class AWIPS2_NetCDF4(object):
"""
Tool for creating AWIPS2 NetCDF files
"""
_nc = None
row_dim_name, col_dim_name = None, None
fac_dim_name, env_dim_name = None, None
def create_dimensions(self, along, cross, factors):
# Create Dimensions
_nc = self._nc
self.row_dim_name = "AlongTrack-%d" % along
self.fac_dim_name = "Granule-%d" % factors # FIXME???
self.col_dim_name = "CrossTrack-%d" % cross
self.env_dim_name = "CrossTrack-3" # FIXME??
LOG.debug('along-track %d' % along)
LOG.debug('cross-track %d' % cross)
LOG.debug('factors %d' % factors)
_nc.createDimension(self.row_dim_name, along)
_nc.createDimension(self.fac_dim_name, factors)
_nc.createDimension(self.col_dim_name, cross)
_nc.createDimension(self.env_dim_name, 3)
def create_time_attrs(self, sdt, edt):
# Create Global Attributes
self._nc.time_coverage_start = sdt.strftime("%Y-%m-%dT%H:%M:%SZ")
self._nc.time_coverage_end = edt.strftime("%Y-%m-%dT%H:%M:%SZ")
self._nc.date_created = utc_now().strftime("%Y-%m-%dT%H:%M:%SZ")
def create_g_ring_attrs(self, g_ring_lat, g_ring_lon):
g_ring_lat = np.append(g_ring_lat, g_ring_lat[0])
g_ring_lon = np.append(g_ring_lon, g_ring_lon[0])
self._nc.setncattr("g_ring_latitude", g_ring_lat)
self._nc.setncattr("g_ring_longitude", g_ring_lon)
def create_image_vars(self, var_stem, collection, data, factors):
# Create and write Variables
# Image data
LOG.debug('data shape is {0:s}'.format(repr(data.shape)))
var_name = "{0:s}@{1:s}".format(var_stem, collection)
LOG.info('writing variable {0:s}...'.format(var_name))
bt_var = self._nc.createVariable(var_name, 'u2',
dimensions=(self.row_dim_name, self.col_dim_name))
bt_var[:, :] = data
bt_var.setncattr("missing_value", [65535, 65534, 65533, 65532, 65531, 65530, 65529, 65528])
# Scaling Factors
# BrightnessTemperature => BrightnessFactors
# BrightnessTemperatureOrReflectance => BrightnessTemperatureOrReflectanceFactors
# good job guys...
prefix = 'Brightness' if var_stem == 'BrightnessTemperature' else var_stem
LOG.debug('{0:s} is prefix'.format(prefix))
bt_factors_var = self._nc.createVariable(
"{0:s}Factors@{1:s}".format(prefix, collection),
'f4',
dimensions=(self.fac_dim_name,)
)
# Factors should be 2 but this aggregate has more
bt_factors_var[:] = factors
def create_geo_vars(self, collection, lat_envelope, lon_envelope):
# remove -M##, -I# in geo collection
# alpha = [x for x in collection.split('-') if not x[-1].isdigit()]
# collection = '-'.join(alpha)
# create navigation variables
lat_var = self._nc.createVariable("Latitude@{0:s}".format(collection), 'f4',
dimensions=(self.row_dim_name, self.env_dim_name))
lat_var[:, :] = lat_envelope
lat_var.setncattr("missing_value", [-999.9, -999.8, -999.5, -999.4, -999.3])
lon_var = self._nc.createVariable("Longitude@{0:s}".format(collection), 'f4',
dimensions=(self.row_dim_name, self.env_dim_name))
lon_var[:, :] = lon_envelope
lon_var.setncattr("missing_value", [-999.9, -999.8, -999.5, -999.4, -999.3])
def __init__(self, filename):
self._nc = Dataset(filename, 'w')
def close(self):
self._nc.sync()
self._nc.close()
self._nc = None
CHANNEL_INFO_LUT = {
'VI1BO': ('01', 'Reflectance', 'VIIRS_I1_IMG_EDR'),
'VI2BO': ('02', 'Reflectance', 'VIIRS_I2_IMG_EDR'),
'VI3BO': ('03', 'Reflectance', 'VIIRS_I3_IMG_EDR'),
'VI4BO': ('04', 'BrightnessTemperature', 'VIIRS_I4_IMG_EDR'),
'VI5BO': ('05', 'BrightnessTemperature', 'VIIRS_I5_IMG_EDR'),
'VM01O': ('11', 'BrightnessTemperatureOrReflectance', 'VIIRS_M1_EDR'),
'VM02O': ('14', 'BrightnessTemperatureOrReflectance', 'VIIRS_M4_EDR'),
'VM03O': ('19', 'BrightnessTemperatureOrReflectance', 'VIIRS_M9_EDR'),
'VM04O': ('24', 'BrightnessTemperatureOrReflectance', 'VIIRS_M14_EDR'),
'VM05O': ('25', 'BrightnessTemperatureOrReflectance', 'VIIRS_M15_EDR'),
'VM06O': ('26', 'BrightnessTemperatureOrReflectance', 'VIIRS_M16_EDR'),
'VNCCO': ('10', 'Albedo', 'VIIRS_NCC_EDR'),
}
def _ncdatefmt(dt):
return dt.strftime('%Y%m%d%H%M%S') + ('%1d' % (dt.microsecond / 100000))
nc_info = namedtuple('nc_info', 'filename_stem tipb_id station ddhhmm variable_stem')
def _nc_info_from_edr_path(edr_path, station=None, region=None):
"convert granule and source information into a netcdf filename and supporting header information"
dn, fn = os.path.split(edr_path)
m = RE_NPP_EDR.match(fn)
if not m:
raise ValueError('{0:s} is not a valid CDFCB-compliant NPP pathname'.format(edr_path))
g = m.groupdict()
sat, d, t, e, c, site, kind_band = map(lambda x: g[x], ('sat', 'date', 'start_time', 'end_time', 'created_time', 'site', 'kindband'))
tipb_id, nc_var_stem, stem = CHANNEL_INFO_LUT.get(kind_band, (None, None, None))
if not tipb_id:
raise ValueError('{0:s} is not a known EDR type'.format(kind_band))
tipb_id = region + tipb_id
sdt, edt = nppdatetime(d,t,e)
ncs = _ncdatefmt(sdt)
nce = _ncdatefmt(edt)
creation = c[:15] # truncate
ddhhmm = sdt.strftime('%d%H%M')
return nc_info(
'{stem:s}_{tipb_id:s}_{station:s}_{sat:s}_s{ncs:s}_e{nce:s}_c{creation:s}'.format(**locals()),
tipb_id,
station or site.upper(),
ddhhmm,
nc_var_stem)
WMO_SUFFIX = '.gz.wmo'
def wmo_wrap(nc_path, wmo_header="TIPB99 KNES 000000", wmo_path=None):
"""
create AWIPS2 wrapper, which involves a text header followed by a gzipped netcdf file.
Yeah, about that...
"""
wmo_header = wmo_header + '\r\r\n'
wmo_path = nc_path + WMO_SUFFIX if wmo_path is None else wmo_path
LOG.debug('writing {0:s} with header {1!r:s}'.format(wmo_path, wmo_header))
gzfp = open(wmo_path, 'wb')
gzfp.write(wmo_header)
gzfp.flush()
ncfp = open(nc_path, 'rb')
gz = Popen(['gzip'], stdin=ncfp, stdout=gzfp)
_,_ = gz.communicate()
def transform(edr_path, output_dir=None, geo_path=None, region=None, station=None, wmo_path=None, cleanup=True):
LOG.info('opening files')
gran = Granule(edr_path, geo_path)
ncstem, tipb_id, station, ddhhmm, varname = _nc_info_from_edr_path(edr_path, station=station, region=region)
ncfn = ncstem + '.nc'
if wmo_path is None:
wmo_path = ncfn + WMO_SUFFIX
if output_dir is not None:
wmo_path = os.path.join(output_dir, os.path.split(wmo_path)[-1])
start, end = gran.start_end
LOG.debug('start, end = {0:s}, {1:s}'.format(start, end))
LOG.info('creating output file {0:s}'.format(ncfn))
nc = AWIPS2_NetCDF4(ncfn)
LOG.debug('adding dimensions')
nc.create_dimensions(gran.along_track_pixels, gran.cross_track_pixels, gran.factor_count)
LOG.debug('adding time attributes')
nc.create_time_attrs(start, end)
LOG.debug('accessing G-Ring navigation')
gr = gran.gring_lat_lon
LOG.debug("writing G-Ring attributes")
nc.create_g_ring_attrs(g_ring_lat=gr.lat, g_ring_lon=gr.lon)
LOG.debug('transferring image data')
nc.create_image_vars(varname, gran.collection, gran.data, gran.factors)
LOG.debug('transferring lat-lon envelope')
env = gran.lat_lon_envelope
nc.create_geo_vars(gran.geo_collection, lat_envelope=env.lat, lon_envelope=env.lon)
LOG.debug('writing NetCDF4 file')
nc.close()
LOG.debug('wrapping NetCDF4 as WMO')
wmo_header = '{0:s} {1:s} {2:s}'.format(tipb_id, station, ddhhmm)
wmo_wrap(ncfn, wmo_header=wmo_header, wmo_path=wmo_path)
if cleanup:
LOG.debug('cleaning out intermediate file {0:s}'.format(ncfn))
os.unlink(ncfn)
LOG.info('done!')
return wmo_path
def main():
import optparse
usage = """
%prog [options] VIIRS-imagery-file.h5 {VIIRS-geo-file.h5} {output-nc-file}
"""
parser = optparse.OptionParser(usage)
#parser.add_option('-t', '--test', dest="self_test",
# action="store_true", default=False, help="run self-tests")
parser.add_option('-v', '--verbose', dest='verbosity', action="count", default=0,
help='each occurrence increases verbosity 1 level through ERROR-WARNING-INFO-DEBUG')
parser.add_option('-d', '--debug', dest='debug', action="count", default=0,
help='enable debug mode where clean-up does not occur (results in .nc file creation)')
parser.add_option('-o', '--output', dest='output', default=None,
help='destination directory to store output to')
parser.add_option('-s', '--station', dest='station', default='CSPP', type='str',
help='Station of origin, which is placed in headers, e.g. KNES')
parser.add_option('-r', '--region', dest='region', default='TIPB', type='str',
help='AWIPS2 WMO region, which is placed in headers, e.g. TIPB for Alaska')
# parser.add_option('-I', '--include-path', dest="includes",
# action="append", help="include path to append to GCCXML call")
(options, args) = parser.parse_args()
# make options a globally accessible structure, e.g. OPTS.
global OPTS
OPTS = options
#if options.self_test:a
# # import doctest
# # doctest.testmod()
# sys.exit(2)
levels = [logging.ERROR, logging.WARN, logging.INFO, logging.DEBUG]
logging.basicConfig(level=levels[min(3,options.verbosity)])
if not args:
parser.error( 'incorrect arguments, try -h or --help.' )
return 9
for arg in args:
if os.path.isfile(arg):
transform(arg, station=options.station, output_dir=options.output, region=options.region, cleanup=not options.debug)
elif os.path.isdir(arg):
from glob import glob
pat = os.path.join(arg, 'V???O*h5') # VI?BO, VM??O, VNCCO
for edr_path in glob(pat):
LOG.info('processing {0:s}'.format(edr_path))
transform(edr_path, station=options.station, region=options.region, output_dir=options.output, cleanup=not options.debug)
else:
LOG.warning('really not sure what to do with {0!r:s} - ignoring'.format(arg))
return 0
if __name__=='__main__':
sys.exit(main())
DJHoese = """
# Read the input files
img_file = h5py.File("./VI4BO_npp_d20130116_t0944041_e0945402_b06328_c20130305060723097136_noaa_ops.h5")
geo_file = h5py.File("./GIGTO_npp_d20130116_t0944041_e0945402_b06328_c20130305060441825409_noaa_ops.h5")
img_data = img_file["All_Data"]["VIIRS-I4-IMG-EDR_All"]["BrightnessTemperature"][:,:]
img_factors = img_file["All_Data"]["VIIRS-I4-IMG-EDR_All"]["BrightnessFactors"][:]
lat_data = geo_file["All_Data"]["VIIRS-IMG-GTM-EDR-GEO_All"]["Latitude"][:,:]
lon_data = geo_file["All_Data"]["VIIRS-IMG-GTM-EDR-GEO_All"]["Longitude"][:,:]
start_date = geo_file["Data_Products"]["VIIRS-IMG-GTM-EDR-GEO"]["VIIRS-IMG-GTM-EDR-GEO_Aggr"].attrs["AggregateBeginningDate"][0,0]
start_time = geo_file["Data_Products"]["VIIRS-IMG-GTM-EDR-GEO"]["VIIRS-IMG-GTM-EDR-GEO_Aggr"].attrs["AggregateBeginningTime"][0,0]
end_date = geo_file["Data_Products"]["VIIRS-IMG-GTM-EDR-GEO"]["VIIRS-IMG-GTM-EDR-GEO_Aggr"].attrs["AggregateEndingDate"][0,0]
end_time = geo_file["Data_Products"]["VIIRS-IMG-GTM-EDR-GEO"]["VIIRS-IMG-GTM-EDR-GEO_Aggr"].attrs["AggregateEndingTime"][0,0]
g_ring_lat = geo_file["Data_Products"]["VIIRS-IMG-GTM-EDR-GEO"]["VIIRS-IMG-GTM-EDR-GEO_Gran_0"].attrs["G-Ring_Latitude"][:].astype(numpy.float64)
g_ring_lon = geo_file["Data_Products"]["VIIRS-IMG-GTM-EDR-GEO"]["VIIRS-IMG-GTM-EDR-GEO_Gran_0"].attrs["G-Ring_Longitude"][:].astype(numpy.float64)
# Make data manipulations
mid_idx = lat_data.shape[-1]/2
lat_envelope = lat_data[:,(0,mid_idx,-1)]
lon_envelope = lon_data[:,(0,mid_idx,-1)]
# Write the output file
_nc = Dataset("VIIRS_I4_IMG_EDR_TIPB99_KNES_npp_s201301160944041_e201301160945402_c20130305060441825409.nc", mode="w")
nc_file = Dataset("viirs_img_edr_20121001.nc", mode="w")
# Create Dimensions
row_dim_name = "AlongTrack-%d" % img_data.shape[0]
fac_dim_name = "Granule-%d" % img_factors.shape[0]
col_dim_name = "CrossTrack-%d" % img_data.shape[1]
env_dim_name = "CrossTrack-%d" % 3
_nc.createDimension(row_dim_name, img_data.shape[0])
_nc.createDimension(fac_dim_name, img_factors.shape[0])
_nc.createDimension(col_dim_name, img_data.shape[1])
_nc.createDimension(env_dim_name, 3)
# Create Global Attributes
_nc.time_coverage_start = datetime.datetime.strptime(start_date + start_time.split(".")[0], "%Y%m%d%H%M%S").strftime("%Y-%m-%dT%H:%M:%SZ")
_nc.time_coverage_end = datetime.datetime.strptime(end_date + end_time.split(".")[0], "%Y%m%d%H%M%S").strftime("%Y-%m-%dT%H:%M:%SZ")
_nc.date_created = utc_now().strftime("%Y-%m-%dT%H:%M:%SZ")
print g_ring_lat.dtype
print g_ring_lat
print g_ring_lon
g_ring_lat = np.append(g_ring_lat, g_ring_lat[0])
g_ring_lon = np.append(g_ring_lon, g_ring_lon[0])
_nc.setncattr("g_ring_latitude", g_ring_lat)
_nc.setncattr("g_ring_longitude", g_ring_lon)
# Create and write Variables
# Image data
bt_var = _nc.createVariable("BrightnessTemperature@VIIRS-I4-IMG-EDR", 'u2', dimensions=(row_dim_name,col_dim_name))
bt_var[:,:] = img_data
bt_var.setncattr("missing_value", "65535 65534 65533 65532 65531 65530 65529 65528")
# XXX: Need missing_valuename?
# Scaling Factors
bt_factors_var = _nc.createVariable("BrightnessFactors@VIIRS-I4-IMG-EDR", 'f4', dimensions=(fac_dim_name,))
# Factors should be 2 but this aggregate has more
bt_factors_var[:] = img_factors
# Navigation
lat_var = _nc.createVariable("Latitude@VIIRS-IMG-GTM-EDR-GEO", 'f4', dimensions=(row_dim_name,env_dim_name))
lat_var[:,:] = lat_envelope
lat_var.setncattr("missing_value", "-999.9 -999.8 -999.5 -999.4 -999.3")
lon_var = _nc.createVariable("Longitude@VIIRS-IMG-GTM-EDR-GEO", 'f4', dimensions=(row_dim_name,env_dim_name))
lon_var[:,:] = lon_envelope
lon_var.setncattr("missing_value", "-999.9 -999.8 -999.5 -999.4 -999.3")
_nc.sync()
_nc.close()
"""