示例#1
0
文件: netcdf.py 项目: cberys/libcchdo
def define_attributes(nc_file, expocode, sect_id, data_type, stnnbr, castno,
                      bottom_depth):
    nc_file.EXPOCODE = expocode
    nc_file.Conventions = 'COARDS/WOCE'
    nc_file.WOCE_VERSION = '3.0'
    nc_file.WOCE_ID = sect_id
    nc_file.DATA_TYPE = data_type
    nc_file.STATION_NUMBER = stnnbr
    nc_file.CAST_NUMBER = castno
    nc_file.BOTTOM_DEPTH_METERS = bottom_depth
    nc_file.Creation_Time = fns.strftime_iso(datetime.datetime.utcnow())
示例#2
0
def write(self, handle):
    """How to write a CTD NetCDF file."""
    UNKNOWN = 'UNKNOWN'
    UNSPECIFIED_UNITS = 'unspecified'
    STRLEN = 40

    # Prepare file handles and datetime information
    tmp = NamedTemporaryFile()
    strdate = str(self.globals['DATE'])
    strtime = str(self.globals['TIME'])
    isowocedate = datetime.strptime(strdate + strtime, '%Y%m%d%H%M')
    nc_file = nc.Dataset(tmp.name, 'w', format='NETCDF3_CLASSIC')

    # Define dimensions variables
    makeDim = nc_file.createDimension
    makeDim('time', 1)
    makeDim('depth', len(self))
    makeDim('latitude', 1)
    makeDim('longitude', 1)
    makeDim('string_dimension', STRLEN)

    # Define dataset attributes
    nc_file.EXPOCODE = self.globals['EXPOCODE']
    nc_file.Conventions = 'COARDS-DEV/WOCE'
    nc_file.WOCE_VERSION = '3.0'
    nc_file.WOCE_ID = self.globals['SECT_ID'] if 'SECT_ID' in self.globals \
            else UNKNOWN
    nc_file.DATA_TYPE = 'WOCE CTD'
    nc_file.STATION_NUMBER = self.globals['STNNBR'] or UNKNOWN
    nc_file.CAST_NUMBER = self.globals['CASTNO'] or UNKNOWN
    nc_file.BOTTOM_DEPTH_METERS = nc.simplest_str(float(self.globals['DEPTH']))
    nc_file.Creation_Time = strftime_iso(datetime.utcnow())
    nc_file.ORIGINAL_HEADER = self.globals['header']
    nc_file.WOCE_CTD_FLAG_DESCRIPTION = woce.CTD_FLAG_DESCRIPTION

    def MISSING_COORD_VAR (param):
        return ("expected global coordinate variable %s "
                "but not found (XXX)") % param

    # Coordinate variables
    if 'TIME' not in self.globals:
        raise AttributeError(MISSING_COORD_VAR('TIME'))
    var_time = nc_file.createVariable('time', 'i', ('time', ))
    var_time.long_name = 'time'
    var_time.units = 'minutes since %s' % strftime_iso(nc.EPOCH)
    var_time.data_min = nc.minutes_since_epoch(isowocedate)
    var_time.data_max = var_time.data_min
    var_time.C_format = '%10d'
    var_time[:] = var_time.data_min

    if 'LATITUDE' not in self.globals:
        raise AttributeError(MISSING_COORD_VAR('LATITUDE'))
    var_latitude = nc_file.createVariable('latitude', 'f', ('latitude',))
    var_latitude.long_name = 'latitude'
    var_latitude.units = 'degrees_N'
    var_latitude.data_min = float(self.globals['LATITUDE'])
    var_latitude.data_max = var_latitude.data_min
    var_latitude.C_format = '%9.4f'
    var_latitude[:] = var_latitude.data_min

    if 'LONGITUDE' not in self.globals:
        raise AttributeError(MISSING_COORD_VAR('LONGITUDE'))
    var_longitude = nc_file.createVariable('longitude', 'f', ('longitude',))
    var_longitude.long_name = 'longitude'
    var_longitude.units = 'degrees_E'
    var_longitude.data_min = float(self.globals['LONGITUDE'])
    var_longitude.data_max = var_longitude.data_min
    var_longitude.C_format = '%9.4f'
    var_longitude[:] = var_longitude.data_min

    woce_datetime = woce.strftime_woce_date_time(isowocedate)

    if 'DATE' not in self.globals:
        raise AttributeError(MISSING_COORD_VAR('DATE'))
    var_woce_date = nc_file.createVariable('woce_date', 'i', ('time',))
    var_woce_date.long_name = 'WOCE date'
    var_woce_date.units = 'yyyymmdd UTC'
    var_woce_date.data_min = int(woce_datetime[0] or -9)
    var_woce_date.data_max = var_woce_date.data_min
    var_woce_date.C_format = '%8d'
    var_woce_date[:] = var_woce_date.data_min

    var_woce_time = nc_file.createVariable('woce_time', 'i2', ('time',))
    var_woce_time.long_name = 'WOCE time'
    var_woce_time.units = 'hhmm UTC'
    var_woce_time.data_min = int(woce_datetime[1] or -9)
    var_woce_time.data_max = var_woce_time.data_min
    var_woce_time.C_format = '%4d'
    var_woce_time[:] = var_woce_time.data_min

    var_station = nc_file.createVariable('station', 'c', ('string_dimension', ))
    var_station.long_name = 'STATION'
    var_station.units = UNSPECIFIED_UNITS
    var_station.C_format = '%s'
    var_station[:] = nc.simplest_str(self.globals['STNNBR']).ljust(len(var_station))
    
    var_cast = nc_file.createVariable('cast', 'c', ('string_dimension', ))
    var_cast.long_name = 'CAST'
    var_cast.units = UNSPECIFIED_UNITS
    var_cast.C_format = '%s'
    var_cast[:] = nc.simplest_str(self.globals['CASTNO']).ljust(len(var_cast))

    # Create data variables and fill them
    for param, column in self.columns.iteritems():
        parameter = column.parameter
        parameter_name = parameter.mnemonic_woce()
        if parameter_name in nc.STATIC_PARAMETERS_PER_CAST:
            continue
        var = nc_file.createVariable(
                  parameter.full_name.encode('ascii', 'replace'), 'f8',
                  ('time', 'depth', 'latitude', 'longitude', ))
        var.long_name = parameter.full_name.encode('ascii', 'replace')
        var.units = parameter.units.name.encode('ascii', 'replace') if \
                        parameter.units else UNSPECIFIED_UNITS
        compact_column = filter(None, column)
        if compact_column:
            var.data_min = min(compact_column)
            var.data_max = max(compact_column)
        else:
            var.data_min = float('-inf')
            var.data_max = float('inf')
        var.C_format = parameter.format.encode('ascii', 'replace')
        print parameter_name, len(var), len(column.values)
        var[:] = column.values

        if column.is_flagged_woce():
            vfw = nc_file.createVariable(parameter.name + nc.QC_SUFFIX, 'i2',
                    ('time', 'depth', 'latitude', 'longitude', ))
            vfw.long_name = parameter.name + nc.QC_SUFFIX
            vfw[:] = column.flags_woce

    # Transfer finished NetCDF file to provided handle
    nc_file.close()
    handle.write(tmp.read())
    tmp.close()
示例#3
0
 def test_strftime_iso(self):
     self.assertEqual('2010-05-04T13:42:39Z',
                      fns.strftime_iso(datetime(2010, 5, 4, 13, 42, 39)))
示例#4
0
def create_oceansites_nc(df, filename, data_type, version=None):
    from libcchdo.formats import netcdf as nc
    info = {
        'date_start': df.globals['_DATETIME'],
        'lat': df.globals['LATITUDE'],
        'lon': df.globals['LONGITUDE'],
        'depths': df.globals['DEPTH'],
        'data_length': len(df),
    }

    version = _sanitize_os_version(version)
    nc_file = nc.Dataset(filename, 'w', format='NETCDF3_CLASSIC')
    nc_file.data_type = 'OceanSITES time-series {data_type} data'.format(
        data_type=data_type)
    nc_file.format_version = version
    # 2011-11-28 Jing Zhou says platform code may be left blank as NA myshen
    nc_file.wmo_platform_code = ''
    if version == '1.2':
        nc_file.platform_code = ''
    now = datetime.utcnow()
    nc_file.date_update = strftime_iso(now)
    nc_file.source = 'Shipborne observation'
    nc_file.history = ''.join([
        info['date_start'].isoformat(), "Z data collected\n",
        now.isoformat(), "Z date file translated/written"
    ])
    nc_file.data_mode = 'D'
    nc_file.quality_control_indicator = '1'
    nc_file.quality_index = 'B'
    if version == '1.1':
        nc_file.conventions = 'OceanSITES Manual 1.1, CF-1.1'
    elif version == '1.2':
        nc_file.Conventions = 'CF-1.4, OceanSITES 1.1'
    nc_file.netcdf_version = '3.x'
    nc_file.naming_authority = 'OceanSITES'
    nc_file.cdm_data_type = 'Station'

    nc_file.geospatial_lat_min = str(info['lat'])
    nc_file.geospatial_lat_max = str(info['lat'])
    nc_file.geospatial_lon_min = str(info['lon'])
    nc_file.geospatial_lon_max = str(info['lon'])
    nc_file.geospatial_vertical_min = 0
    nc_file.geospatial_vertical_max = str(info['depths'])
    nc_file.geospatial_vertical_positive = 'down'

    nc_file.author = 'Shen:Diggs (Scripps)'
    nc_file.data_assembly_center = data_assembly_center(version)
    nc_file.distribution_statement = OS_TEXT['DISTRIBUTION_STATEMENT']
    nc_file.citation = OS_TEXT['CITATION']
    nc_file.update_interval = 'void'
    nc_file.qc_manual = qc_manual(version)
    nc_file.time_coverage_start = strftime_iso(info['date_start'])
    nc_file.time_coverage_end = strftime_iso(info['date_start'])

    nc_file.createDimension('TIME')
    try:
        nc_file.createDimension('DEPTH', info['data_length'])
    except RuntimeError:
        raise AttributeError("There is no data to be written.")
    nc_file.createDimension('LATITUDE', 1)
    nc_file.createDimension('LONGITUDE', 1)
    nc_file.createDimension('POSITION', 1)

    # OceanSITES coordinate variables
    var_time = nc_file.createVariable('TIME',
                                      'd', ('TIME', ),
                                      fill_value=999999.0)
    var_time.long_name = 'time'
    var_time.standard_name = 'time'
    var_time.units = 'days since 1950-01-01T00:00:00Z'
    var_time.valid_min = 0.0
    var_time.valid_max = 90000.0
    var_time.QC_indicator = 7  # Matthias Lankhorst
    var_time.QC_procedure = 5  # Matthias Lankhorst
    # 1/24 assuming a typical cast lasts one hour Matthias Lankhorst
    var_time.uncertainty = 0.0417
    var_time.axis = 'T'
    var_time[:] = [fractional_days_since(info['date_start'])]

    var_latitude = nc_file.createVariable('LATITUDE',
                                          'f', ('LATITUDE', ),
                                          fill_value=99999.0)
    var_latitude.long_name = 'Latitude of each location'
    var_latitude.standard_name = 'latitude'
    var_latitude.units = 'degrees_north'
    var_latitude.valid_min = -90.0
    var_latitude.valid_max = 90.0
    var_latitude.QC_indicator = 7  # Matthias Lankhorst
    var_latitude.QC_procedure = 5  # Matthias Lankhorst
    var_latitude.uncertainty = 0.0045  # Matthias Lankhorst
    var_latitude.axis = 'Y'
    if version == '1.1':
        pass
    elif version == '1.2':
        var_latitude.reference = 'WGS84'
        var_latitude.coordinate_reference_frame = 'urn:ogc:crs:EPSG::4326'
    var_latitude[:] = [info['lat']]

    var_longitude = nc_file.createVariable('LONGITUDE',
                                           'f', ('LONGITUDE', ),
                                           fill_value=99999.0)
    var_longitude.long_name = 'Longitude of each location'
    var_longitude.standard_name = 'longitude'
    var_longitude.units = 'degrees_east'
    var_longitude.valid_min = -180.0
    var_longitude.valid_max = 180.0
    var_longitude.QC_indicator = 7  # Matthias Lankhorst
    var_longitude.QC_procedure = 5  # Matthias Lankhorst
    # Matthias Lankhorst
    var_longitude.uncertainty = 0.0045 / cos(float(info['lat']))
    var_longitude.axis = 'X'
    if version == '1.1':
        pass
    elif version == '1.2':
        var_longitude.reference = 'WGS84'
        var_longitude.coordinate_reference_frame = 'urn:ogc:crs:EPSG::4326'
    var_longitude[:] = [info['lon']]

    var_depth = nc_file.createVariable('DEPTH',
                                       'f', ('DEPTH', ),
                                       fill_value=-99999.0)
    var_depth.long_name = 'Depth of each measurement'
    var_depth.standard_name = 'depth'
    var_depth.units = 'meters'
    var_depth.valid_min = 0.0
    var_depth.valid_max = 12000.0
    # Subject: OceanSITES: more on QC flags, uncertainty, depth
    # Interpolated from latitude and pressure.
    var_depth.QC_indicator = 8
    var_depth.QC_procedure = 2  # See above
    var_depth.uncertainty = 1.0  # A decibar
    if version == '1.1':
        var_depth.axis = 'down'  # oceanic
    elif version == '1.2':
        var_depth.positive = 'down'
        var_depth.axis = 'Z'
        var_depth.reference = 'sea_level'  # TODO is this right?
        var_depth.coordinate_reference_frame = 'urn:ogc:crs:EPSG::5113'
    return nc_file
示例#5
0
def _write_dfile(dfile, fileobj, cfg=DEFAULT_CFG, cvt=None):
    """Write microstructure COARDS compliant file."""
    log.debug(dfile.globals)
    with nc.nc_dataset_to_stream(fileobj, format='NETCDF3_CLASSIC') as nc_file:
        nc_file.Conventions = 'CF-1.6'
        nc_file.netcdf_version = '3'
        nc_file.history = ''.join([
            "data collected\n",
            datetime.utcnow().isoformat(), "Z date file translated/written"
        ])
        nc_file.source = _real_data_type(cfg)
        # TODO README file inclusion or reference? details quality, instruments
        # used, TS used pumped instruments etc
        # TODO will supply DOIs
        nc_file.references = 'references'

        nc_file.data_type = _real_data_type(cfg)

        nc_file.format_version = '0.1-beta'

        nc_file.date_update = fns.strftime_iso(datetime.utcnow())

        (minlng, minlat), (maxlng, maxlat) = _get_coordinate_bounds(dfile)
        nc_file.geospatial_lat_min = str(minlat)
        nc_file.geospatial_lat_max = str(maxlat)
        nc_file.geospatial_lon_min = str(minlng)
        nc_file.geospatial_lon_max = str(maxlng)
        nc_file.geospatial_vertical_min = 0
        nc_file.geospatial_vertical_max = int(dfile.globals['DEPTH'])
        nc_file.geospatial_vertical_positive = 'down'
        nc_file.author = cfg['originator']
        nc_file.pi_name = cfg['pi']
        # non-OceanSITES standard
        nc_file.data_originator = cfg['originator']
        nc_file.data_assembly_center = 'CCHDO'
        # TODO
        nc_file.distribution_statement = ('HRP distribution statement. TBD.')
        # TODO
        nc_file.citation = ('Citation statement. TBD.')
        nc_file.update_interval = 'void'
        nc_file.time_coverage_start = fns.strftime_iso(
            dfile.globals['_DATETIME'])
        nc_file.time_coverage_end = fns.strftime_iso(
            dfile.globals['_DATETIME'])

        nc_file.dive_number = int(dfile.globals['STNNBR'])

        nc_file.createDimension('TIME')
        nc_file.createDimension('BOTTOM_DEPTH', 1)
        try:
            nc_file.createDimension('DEPTH', len(dfile))
        except RuntimeError:
            raise AttributeError("There is no data to be written.")
        nc_file.createDimension('LATITUDE', 1)
        nc_file.createDimension('LONGITUDE', 1)

        var_time = nc_file.createVariable('TIME',
                                          'd', ('TIME', ),
                                          fill_value=999999.0)
        var_time.long_name = 'time'
        var_time.standard_name = 'time'
        var_time.units = 'days since 1950-01-01T00:00:00Z'
        var_time.valid_min = 0.0
        var_time.valid_max = 90000.0
        var_time.axis = 'T'

        var_latitude = nc_file.createVariable('LATITUDE',
                                              'f', ('LATITUDE', ),
                                              fill_value=99999.0)
        var_latitude.long_name = 'Latitude of each location'
        var_latitude.standard_name = 'latitude'
        var_latitude.units = 'degrees_north'
        var_latitude.valid_min = -90.0
        var_latitude.valid_max = 90.0
        var_latitude.axis = 'Y'

        var_longitude = nc_file.createVariable('LONGITUDE',
                                               'f', ('LONGITUDE', ),
                                               fill_value=99999.0)
        var_longitude.long_name = 'Longitude of each location'
        var_longitude.standard_name = 'longitude'
        var_longitude.units = 'degrees_east'
        var_longitude.valid_min = -180.0
        var_longitude.valid_max = 180.0
        var_longitude.axis = 'X'

        if dfile.globals.get('DEPTH'):
            var_bot_depth = nc_file.createVariable('BOT_DEPTH',
                                                   'f', ('BOTTOM_DEPTH', ),
                                                   fill_value=-99999.0)
            var_bot_depth.long_name = 'Sea floor depth below sea level'
            var_bot_depth.standard_name = 'sea_floor_depth_below_sea_level'
            var_bot_depth.units = 'meters'
            var_bot_depth.valid_min = 0.0
            var_bot_depth.valid_max = 12000.0
            var_bot_depth.axis = 'Z'
            var_bot_depth = float(dfile.globals['DEPTH'])

        var_depth = nc_file.createVariable('DEPTH',
                                           'f', ('DEPTH', ),
                                           fill_value=-99999.0)
        var_depth.long_name = 'Depth of each measurement'
        var_depth.standard_name = 'depth'
        var_depth.units = 'meters'
        var_depth.valid_min = 0.0
        var_depth.valid_max = 12000.0
        var_depth.axis = 'Z'

        # Write variables
        var_time[:] = [decimal_days_since(dfile.globals['_DATETIME'])]
        var_latitude[:] = [dfile.globals['LATITUDE']]
        var_longitude[:] = [dfile.globals['LONGITUDE']]

        var_depth[:] = _write_depths(var_depth, dfile, cvt)

        write_columns(dfile, nc_file, cvt)

        nc_file.title = '{0} ExpoCode={1} Dive={2}'.format(
            cfg['data_type'], dfile.globals['EXPOCODE'],
            dfile.globals['STNNBR'])
        nc_file.id = '{0} {1} {2}'.format(cfg['data_type'],
                                          dfile.globals['EXPOCODE'],
                                          dfile.globals['STNNBR'])

        nc.check_variable_ranges(nc_file)