Example #1
0
def l2w_required(inputfile, required, data={}, att={}):
    from acolite.shared import nc_data, nc_datasets, nc_atts
    datasets = nc_datasets(inputfile)

    if all([rd in datasets for rd in required]):
        for rd in required:
            if rd not in data:
                data[rd], att[rd] = nc_data(inputfile, rd, attributes=True)
                #att[rd] = nc_atts(inputfile, rd)
        return (True)
    else:
        return (False)
Example #2
0
def planetscope_ac(
        bundle,
        output,
        limit=None,
        gas_transmittance=True,
        uoz_default=0.3,
        uwv_default=1.5,
        wvlut='201710C',
        ## use ancillary data for gas transmittances rather than defaults
        ancillary_data=True,
        ## do sky glint correction
        sky_correction=True,
        sky_correction_option='all',
        lut_pressure=True,
        pressure=None,
        model_selection='min_tau',
        rdark_list_selection='intercept',
        luts=['PONDER-LUT-201704-MOD1', 'PONDER-LUT-201704-MOD2'],
        extra_ac_parameters=True,
        ignore_sr_image=True,
        extend_limit=False,
        keep_l1r_ncdf=False,
        map_rgb=True,
        map_rgb_rhos=True,
        rgb_autorange=False,
        rgb_percentiles=[10, 90],
        rgb_range_min=[0.0, 0.0, 0.0],
        rgb_range_max=[0.15, 0.15, 0.15],
        nc_compression=True):

    import os
    from numpy import nanmax, nanmin, nanpercentile

    import dateutil
    from osgeo import gdal, osr

    from acolite.shared import nc_gatts, nc_datasets, nc_data, rtoa_to_rhos
    from acolite.output import nc_write, write_rgb
    from acolite.plotting import plot_dark_spectrum
    from acolite import planetscope, ac

    import acolite as aco
    import matplotlib as mpl

    data_type = None
    sub = None
    sr_image_file = None

    if bundle[-3:] == '.nc':
        try:
            import dateutil.parser
            metadata = nc_gatts(bundle)
            ## fix some metadata not supported in NetCDF
            metadata['TIME'] = dateutil.parser.parse(metadata["isotime"])
            for mk in metadata:
                if ('BAND' in mk):
                    metadata[mk] = metadata[mk].split(',')
            data_type = "NetCDF"
        except:
            data_type = None

    zipped = False
    if data_type == None:
        data_type = 'PlanetScope'
        if bundle[-4:] == '.zip':
            zipped = True
            import zipfile, shutil
            bundle_orig = '{}'.format(bundle)
            bundle, ext = os.path.splitext(bundle_orig)
            zip_ref = zipfile.ZipFile(bundle_orig, 'r')
            zip_ref.extractall(bundle)
            zip_ref.close()

        files = planetscope.bundle_test(bundle)

        metafile = None
        image_file = None

        if 'metadata' in files: metafile = files['metadata']['path']
        if 'analytic' in files: image_file = files['analytic']['path']
        if 'sr' in files: sr_image_file = files['sr']['path']

        if metafile is not None:
            metadata = planetscope.parse_metadata(metafile)
            metadata['image_file'] = image_file
        else:
            bn, ex = os.path.splitext(os.path.basename(image_file))
            sp = bn.split('_')
            fdate = sp[0]
            ftime = sp[1]
            sat = sp[3]
            time = dateutil.parser.parse(fdate + ftime)

            ## SuperDove!
            if sat[0] == '2':
                print('Processing SuperDove file!')
                metadata = {}

                metadata['lut_sensor'] = 'PlanetScope_SuperDove'
                metadata['platform'] = "PlanetScope"
                metadata['platform_id'] = sat
                #metadata['orbit'] = "DESCENDING"
                metadata['SATELLITE_ID'] = sat[0:2]
                metadata['SATELLITE_SENSOR'] = "PlanetScope_{}".format(sat)
                metadata['LUT_SENSOR'] = "PlanetScope_SuperDove"
                metadata['SENSOR'] = "PlanetScope"
                metadata['SATELLITE_PREFIX'] = "ps"
                metadata['ViewingIncidence'] = 0
                metadata['ViewingAzimuth'] = 0.
                metadata['ViewZenith'] = 0.
                metadata['isotime'] = time.isoformat()
                metadata['THV'] = 0.
                metadata['PHIV'] = 0.
                metadata['DOY'] = time.strftime('%j')
                metadata['SE_DISTANCE'] = aco.shared.distance_se(
                    metadata['DOY'])
                metadata['isodate'] = time.isoformat()
                metadata['image_file'] = image_file
                metadata['limit'] = limit
                metadata['TIME'] = time

                sd_bands = {
                    'Coastal-Blue': '444',
                    'Blue': '492',
                    'Green_i': '533',
                    'Green_ii': '566',
                    'Yellow': '612',
                    'Red': '666',
                    'Red-edge': '707',
                    'NIR': '866'
                }

                metadata['BANDS_ALL'] = [k for k in sd_bands.keys()]
                for ib, band in enumerate(sd_bands):
                    metadata['{}-wave'.format(band)] = float(
                        sd_bands[band]) / 1000.
                    metadata['{}-wave_name'.format(band)] = sd_bands[band]
                    metadata['{}-band_idx'.format(band)] = ib + 1
                    metadata['{}-f0'.format(band)] = 0
                    metadata['{}-to_radiance'.format(band)] = 0.01
                    #                    metadata['{}-to_reflectance'.format(band)] = 0.0001
                    metadata['{}-to_reflectance'.format(band)] = 1e-4

                ds = gdal.Open(metadata['image_file'])
                gt = ds.GetGeoTransform()
                metadata['dims'] = (ds.RasterXSize, ds.RasterYSize)
                metadata['resolution'] = (gt[1], gt[5])
                ds = None

                p, (xrange,
                    yrange) = aco.planetscope.geo.get_projection(metadata)
                clon, clat = p((xrange[1] + xrange[0]) / 2,
                               (yrange[1] + yrange[0]) / 2,
                               inverse=True)
                sun = aco.shared.sunposition(metadata['isotime'], clon, clat)
                metadata['THS'] = sun['zenith']
                metadata['PHIS'] = sun['azimuth']
                metadata['AZI'] = 0  # metadata['PHIS'] - metadata['PHIV']

        if limit is not None:
            ret = planetscope.geo.get_sub(metadata, limit)
            if type(ret) == int:
                print('Error computing subset.')
                return (1)
            sub, p, (xrange, yrange, grid_region) = ret

    sensor = metadata['LUT_SENSOR']

    print(metadata)

    if sensor == 'PlanetScope_0d':
        print('Sensor {} not yet implemented'.format(sensor))
        return ()

    if ('lat' not in locals()) or ('lat' not in locals()):
        #print('Computing lat lon')
        if (data_type == "NetCDF") & ('limit' in metadata) & (limit is None):
            datasets = nc_datasets(bundle)
            print(datasets)
            if ('lon' in datasets) & ('lat' in datasets):
                lon = nc_data(bundle, 'lon')
                lat = nc_data(bundle, 'lat')
            else:
                lon, lat = planetscope.get_ll(metadata,
                                              limit=metadata['limit'],
                                              extend_limit=True)
        else:
            lon, lat = planetscope.get_ll(metadata,
                                          limit=limit,
                                          extend_limit=extend_limit)

    ## get NCEP & TOAST ancillary data
    if ancillary_data:
        ## use image/region mid-point
        pc_lon = lon[int(lon.shape[0] / 2), int(lon.shape[1] / 2)]
        pc_lat = lat[int(lat.shape[0] / 2), int(lat.shape[1] / 2)]
        pc_date = metadata['TIME'].strftime('%Y-%m-%d')
        pc_time = metadata['TIME'].hour + metadata[
            'TIME'].minute / 60. + metadata['TIME'].second / 3600.
        pc_anc = ac.ancillary.ancillary_get(pc_date,
                                            pc_lon,
                                            pc_lat,
                                            ftime=pc_time,
                                            kind='nearest')

        ## get pressure from ancillary data if not determined by user or by DEM
        if (pressure == None) & (lut_pressure):
            if 'press' not in pc_anc:
                print('No ancillary pressure found: using default.')
                pressure = None
            else:
                pressure = pc_anc['press']['interp']

    if pressure is None:
        pressure = 1013.25

    ## get gas transmittances
    if gas_transmittance:
        uoz = uoz_default
        uwv = uwv_default

        ## use ancillary data if provided
        if ancillary_data:
            if 'ozone' in pc_anc: uoz = pc_anc['ozone']['interp']
            else:
                print(
                    'No ancillary ozone found: using default {}.'.format(uoz))
            if 'p_water' in pc_anc: uwv = pc_anc['p_water']['interp'] / 10.
            else:
                print(
                    'No ancillary ozone found: using default {}.'.format(uwv))
        ## compute transmittances
        tt_oz = ac.o3_transmittance(sensor, metadata, uoz=uoz)
        tt_wv = ac.wvlut_interp(min(79.999, metadata['THS']),
                                metadata['THV'],
                                uwv=uwv,
                                sensor=sensor,
                                config=wvlut)
        tt_gas = {btag: tt_oz[btag] * tt_wv[btag] for btag in tt_oz.keys()}

    ## Sky reflectance correction
    if sky_correction:
        rsky = ac.toa_rsky(metadata, pressure=pressure)

    if not os.path.exists(output): os.makedirs(output)

    if data_type == "NetCDF":
        obase = metadata['obase']
        nc_l1r_new = False
        nc_file_l1r = '{}'.format(bundle)
    else:
        ## add PS satellite id
        if metadata['SENSOR'] == 'PlanetScope':
            obase = '{}_{}_{}'.format(
                metadata['SENSOR'],
                metadata['TIME'].strftime('%Y_%m_%d_%H_%M_%S'),
                'PS{}'.format(metadata['SATELLITE_SENSOR'].split('_')[1]))
        else:
            obase = '{}_{}_{}'.format(
                metadata['SENSOR'],
                metadata['TIME'].strftime('%Y_%m_%d_%H_%M_%S'),
                'RE{}'.format(metadata['SATELLITE_SENSOR'].split('-')[1]))
        nc_l1r_new = True
        nc_file_l1r = '{}/{}_L1R.nc'.format(output, obase)

    nc_file_l2r = '{}/{}_L2R.nc'.format(output, obase)
    bands = metadata['BANDS_ALL']

    if extend_limit:
        offset = grid_region['off']
        global_dims = (grid_region['dims'][1], grid_region['dims'][0])
    else:
        offset = None
        global_dims = None

    ## read RTOA and get rdark
    rdark = {}
    rhod = {}

    for bi, band in enumerate(bands):
        ds_att = planetscope.get_band_att(metadata, band)
        parname_t = 'rhot_{}'.format(ds_att['wave_name'])
        parname_s = 'rhos_{}'.format(ds_att['wave_name'])

        print(parname_t)

        if data_type == "NetCDF":
            band_data = nc_data(bundle, parname_t)
        else:
            band_data = planetscope.get_rtoa(image_file,
                                             bi + 1,
                                             band,
                                             metadata,
                                             sub=sub)
            ## write to L1R NetCDF
            nc_write(nc_file_l1r,
                     parname_t,
                     band_data,
                     dataset_attributes=ds_att,
                     new=nc_l1r_new,
                     attributes=metadata,
                     global_dims=global_dims,
                     offset=offset,
                     nc_compression=nc_compression)
            nc_l1r_new = False

        ## apply gas correction
        if gas_transmittance:
            band_data /= tt_gas[band]
            ds_att['tt_gas'] = tt_gas[band]

        ## apply sky correction
        if sky_correction:
            if sky_correction_option == 'all':
                band_data -= rsky[band]
                ds_att['rsky'] = rsky[band]

        ## get rdark
        rdark[band] = nanpercentile(band_data, (0.1))

        rhod[band] = {
            'rhod': rdark[band],
            'wave': ds_att['wave'] * 1000.,
            'tt_gas': tt_gas[band],
            'raa': metadata['AZI'],
            'vza': metadata['THV'],
            'sza': metadata['THS']
        }
        band_data = None
    print(rhod)

    ## select model
    lutd = aco.aerlut.import_luts(base_luts=luts)
    res = aco.ac.select_model2(rhod,
                               sensor,
                               pressure=pressure,
                               lutd=lutd,
                               rhod_tgas_cutoff=0.90,
                               rhod_model_selection='min_tau')

    attributes = metadata

    ## a/c parameters
    if extra_ac_parameters:
        pars = res['lut_meta']['par']
    else:
        pars = ['romix', 'dtott', 'utott', 'astot', 'rorayl']

    ## get sensor RSR
    rsr_file = '{}/RSR/{}.txt'.format(aco.config['pp_data_dir'], sensor)
    rsr, rsr_bands = aco.shared.rsr_read(file=rsr_file)

    raa = metadata['AZI']
    vza = metadata['THV']
    sza = metadata['THS']
    waves_mu = res['lut_meta']['wave']

    band_pars = {b: {} for b in rhod}
    for ip, par in enumerate(pars):
        ip = [
            i for i, value in enumerate(res['lut_meta']['par']) if value == par
        ]
        if len(ip) == 1: ip = ip[0]
        else: continue
        ret = res['rgi']((pressure, ip, waves_mu, raa, vza, sza, res['taua']))

        for b in rhod:
            band_pars[b][par] = aco.shared.rsr_convolute(
                ret, waves_mu, rsr[b]['response'], rsr[b]['wave'])

    for b in res['rhod_sel']:
        attributes['{}-rdark'.format(b)] = res['rhod_sel'][b]
    for b in band_pars:
        attributes['{}-rpath'.format(b)] = band_pars[b]['romix']
    for b in band_pars:
        for p in band_pars[b]:
            attributes['{}-{}'.format(b, p)] = band_pars[b][p]

    sel_model_lut_meta = res['lut_meta']
    dark_idx = str(bands[res['sel_idx']])
    tau550 = res['taua']
    sel_rmsd = res['rmsd']

    if 'aermod' in sel_model_lut_meta.keys():
        sel_mod_num = sel_model_lut_meta['aermod']
        if type(sel_mod_num) is list: sel_mod_num = sel_mod_num[0]
        sel_model_lut_meta['aermod'] = str(sel_mod_num)
        if sel_model_lut_meta['aermod'] == "1": model_char = 'C'
        if sel_model_lut_meta['aermod'] == "2": model_char = 'M'
        if sel_model_lut_meta['aermod'] == "3": model_char = 'U'
    else:
        model_char = '4C'
        model_char = '4C: {}/{}/{}/{}'.format(sel_model_lut_meta['mod1'],
                                              sel_model_lut_meta['mod2'],
                                              sel_model_lut_meta['mod3'],
                                              sel_model_lut_meta['mod4'])

    pixel_idx = -1
    attributes['dsf_pixel_idx'] = pixel_idx
    attributes['dsf_percentile'] = 0.1
    attributes['dsf_model_selection'] = model_selection

    attributes['ac_model'] = sel_model_lut_meta['base']  #[0]
    attributes['ac_model_char'] = model_char
    if type(dark_idx) == str:
        attributes['ac_band'] = dark_idx
    else:
        attributes['ac_band'] = ','.join(dark_idx)

    attributes['ac_aot550'] = tau550
    attributes['ac_rmsd'] = sel_rmsd
    print('model:{} band:{} aot={:.3f}'.format(attributes['ac_model_char'],
                                               attributes['ac_band'],
                                               attributes['ac_aot550']))

    ## plot dark spectrum
    ds_plot = '{}/{}_{}.{}'.format(output, obase, 'dark_spectrum', 'png')
    band_names = bands
    data_type = 'NetCDF'
    waves = [metadata['{}-{}'.format(b, 'wave_name')] for b in bands]
    dsf_spectrum_option = 'fixed'

    ratm_s = {b: band_pars[b]['romix'] for b in band_pars}
    rorayl_s = {b: band_pars[b]['rorayl'] for b in band_pars}
    plot_dark_spectrum(metadata,
                       ds_plot,
                       waves,
                       ratm_s,
                       rorayl_s,
                       rdark,
                       dark_idx,
                       tau550,
                       sel_model_lut_meta,
                       xlim=(430, 900))

    ## compute RHOS
    nc_l2r_new = True

    for bi, band in enumerate(bands):
        ds_att = planetscope.get_band_att(metadata, band)
        parname_t = 'rhot_{}'.format(ds_att['wave_name'])
        parname_s = 'rhos_{}'.format(ds_att['wave_name'])

        ## read from L1R NetCDF
        band_data = nc_data(nc_file_l1r, parname_t)

        nc_write(nc_file_l2r,
                 'lon',
                 lon,
                 new=nc_l2r_new,
                 attributes=attributes,
                 nc_compression=nc_compression)
        nc_l2r_new = False

        nc_write(nc_file_l2r,
                 'lat',
                 lat,
                 new=nc_l2r_new,
                 attributes=attributes,
                 nc_compression=nc_compression)
        nc_l2r_new = False

        ## write rhot
        nc_write(nc_file_l2r,
                 parname_t,
                 band_data,
                 dataset_attributes=ds_att,
                 new=nc_l2r_new,
                 attributes=attributes,
                 nc_compression=nc_compression)
        nc_l2r_new = False

        ## apply gas correction
        if gas_transmittance:
            band_data /= tt_gas[band]
            ds_att['tt_gas'] = tt_gas[band]

        ## apply sky correction
        if sky_correction:
            if sky_correction_option == 'all':
                band_data -= rsky[band]
                ds_att['rsky'] = rsky[band]

        for k in band_pars[band]:
            ds_att[k] = band_pars[band][k]

        ## compute rhos
        band_data = rtoa_to_rhos(band_data,
                                 ds_att['romix'],
                                 ds_att['utott'],
                                 ds_att['dtott'],
                                 ds_att['astot'],
                                 tt_gas=1.)
        nc_write(nc_file_l2r,
                 parname_s,
                 band_data,
                 dataset_attributes=ds_att,
                 new=nc_l2r_new,
                 attributes=attributes,
                 nc_compression=nc_compression)
        nc_l2r_new = False

    ## make RGB
    if metadata['LUT_SENSOR'] == "PlanetScope_SuperDove":
        wave_red = metadata['Red-wave_name']
        wave_green = metadata['Green_ii-wave_name']
        wave_blue = metadata['Blue-wave_name']
    else:
        wave_red = metadata['Red-wave_name']
        wave_green = metadata['Green-wave_name']
        wave_blue = metadata['Blue-wave_name']

    ## map rgb images
    ## keep image 3d matrix for further plotting (if needed)
    rgb_image = None
    if map_rgb:
        for par in ['rhot', 'rhos']:
            rgb_dir = '{}'.format(output)
            if not os.path.exists(rgb_dir): os.makedirs(rgb_dir)
            if (par == 'rhos') & (map_rgb_rhos == False): continue

            ## read data from NCDF file
            data_r = nc_data(nc_file_l2r, '{}_{}'.format(par, wave_red))
            data_g = nc_data(nc_file_l2r, '{}_{}'.format(par, wave_green))
            data_b = nc_data(nc_file_l2r, '{}_{}'.format(par, wave_blue))

            rgb_file = '{}/{}_{}.{}'.format(rgb_dir, obase,
                                            'RGB_{}'.format(par.upper()),
                                            'png')
            rgb_image = write_rgb(rgb_file,
                                  data_r,
                                  data_g,
                                  data_b,
                                  rgb_autorange=rgb_autorange,
                                  rgb_percentiles=rgb_percentiles,
                                  rgb_range_min=rgb_range_min,
                                  rgb_range_max=rgb_range_max,
                                  return_image=True)
            rgb_image = None
            data_r = None
            data_g = None
            data_b = None

    ## get PlanetScope surface reflectance
    if (sr_image_file is not None) & (not ignore_sr_image):
        nc_file_sr = '{}/{}_SR.nc'.format(output, obase)

        nc_sr_new = True
        attributes['auto_grouping'] = 'rhot:rhorc:rhos:rhow:sr'

        for bi, band in enumerate(bands):
            ds_att = planetscope.get_band_att(metadata, band)
            parname_sr = 'sr_{}'.format(ds_att['wave_name'])
            parname_s = 'rhos_{}'.format(ds_att['wave_name'])

            band_data = planetscope.get_rsur(sr_image_file, bi + 1, sub=sub)

            nc_write(nc_file_sr,
                     parname_sr,
                     band_data,
                     dataset_attributes=ds_att,
                     new=nc_sr_new,
                     attributes=metadata,
                     global_dims=global_dims,
                     offset=offset,
                     nc_compression=nc_compression)
            nc_sr_new = False

            ## find out overlap with L1 product to save in the same file
            if True:
                data_rhos = nc_data(nc_file_l2r, parname_s)
                nc_write(nc_file_sr,
                         parname_s,
                         data_rhos,
                         dataset_attributes=ds_att,
                         new=nc_sr_new,
                         attributes=metadata,
                         nc_compression=nc_compression)
                nc_sr_new = False

    if False:
        ## read data from NCDF file
        data_r = nc_data(nc_file_l2r, '{}_{}'.format('rhos', wave_red))

    ## remove the extracted bundle
    if zipped:
        shutil.rmtree(bundle)
        bundle = '{}'.format(bundle_orig)

    if (not keep_l1r_ncdf) & (not data_type == "NetCDF"):
        shutil.rmtree(nc_file_l1r)

    lutd = None
    mpl.pyplot.close('all')

    return (nc_file_l2r)
Example #3
0
def acolite_map(
        inputfile=None,
        output=None,
        parameters=None,
        dpi=300,
        ext='png',
        mapped=True,
        max_dim=1000,
        limit=None,
        auto_range=False,
        range_percentiles=(5, 95),
        dataset_rescale=False,
        map_title=True,
        map_colorbar=False,
        map_colorbar_orientation='vertical',  #'horizontal', 
        rgb_rhot=False,
        rgb_rhos=False,
        red_wl=660,
        green_wl=560,
        blue_wl=480,
        rgb_min=[0.0] * 3,
        rgb_max=[0.15] * 3,
        rgb_pan_sharpen=False,
        map_parameters_pan=True,
        map_fillcolor='White',
        map_scalepos='LR',
        map_scalebar=True,
        map_scalecolor='Black',
        map_scalecolor_rgb='White',
        map_scalelen=None,
        map_projection='tmerc',
        map_colorbar_edge=True,
        map_points=None,
        return_image=False,
        map_raster=False):

    import os, copy
    import datetime, time, dateutil.parser

    from acolite.shared import datascl, nc_data, nc_datasets, nc_gatts, qmap, closest_idx
    from acolite.acolite import pscale
    import acolite as ac

    from numpy import nanpercentile, log10, isnan, dstack
    from scipy.ndimage import zoom

    import matplotlib
    import matplotlib.pyplot as plt

    if not os.path.exists(inputfile):
        print('File {} not found.'.format(inputfile))
        return (False)

    ## run through maps
    maps = {
        'rhot': rgb_rhot,
        'rhos': rgb_rhos,
        'parameters': parameters != None
    }
    if all([maps[m] == False for m in maps]): return ()

    ## get parameter scaling
    psc = pscale()

    ## read netcdf info
    l2w_datasets = nc_datasets(inputfile)
    print(l2w_datasets)

    gatts = nc_gatts(inputfile)
    if 'MISSION_INDEX' in gatts:
        sat, sen = gatts['MISSION'], gatts['MISSION_INDEX']
        stime = dateutil.parser.parse(gatts['IMAGING_DATE'] + ' ' +
                                      gatts['IMAGING_TIME'])
        obase = '{}_{}_{}'.format(sat, sen,
                                  stime.strftime('%Y_%m_%d_%H_%M_%S'))
    else:
        sp = gatts['sensor'].split(
            '_') if 'sensor' in gatts else gatts['SATELLITE_SENSOR'].split('_')
        sat, sen = sp[0], sp[1]
        stime = dateutil.parser.parse(gatts['isodate'] if 'isodate' in
                                      gatts else gatts['ISODATE'])
        obase = gatts['output_name'] if 'output_name' in gatts else gatts[
            'obase']

    ## find pan sharpening dataset
    if rgb_pan_sharpen:
        if sat not in ['L7', 'L8']: rgb_pan_sharpen = False
        tmp = os.path.splitext(inputfile)
        l1_pan_ncdf = '{}L1R_pan{}'.format(tmp[0][0:-3], tmp[1])
        if os.path.exists(l1_pan_ncdf):
            pan_data = nc_data(l1_pan_ncdf, 'rhot_pan')
        else:
            print('L1 pan NetCDF file not found')
            rgb_pan_sharpen = False

    if output is not None:
        odir = output
    else:
        odir = gatts['output_dir']

    if not os.path.exists(odir): os.makedirs(odir)

    scf = 1.
    rescale = 1.0

    #if dataset_rescale or mapped:
    lon = nc_data(inputfile, 'lon')
    if mapped:
        lat = nc_data(inputfile, 'lat')
        if rgb_pan_sharpen:
            lon_pan = zoom(lon, zoom=2, order=1)
            lat_pan = zoom(lat, zoom=2, order=1)

    ## set up mapping info
    if True:
        from numpy import linspace, tile, ceil, isnan, nan
        mask_val = -9999.9999
        from scipy.ndimage.interpolation import map_coordinates

        ## rescale to save memory
        dims = lon.shape
        dsc = (dims[0] / max_dim, dims[1] / max_dim)
        scf /= max(dsc)

        if rgb_pan_sharpen: scf = 1.0

        if (scf < 1.) and dataset_rescale:
            sc_dims = (int(ceil(dims[0] * scf)), int(ceil(dims[1] * scf)))
            xdim = linspace(0, dims[1], sc_dims[1]).reshape(1, sc_dims[1])
            ydim = linspace(0, dims[0], sc_dims[0]).reshape(sc_dims[0], 1)
            xdim = tile(xdim, (sc_dims[0], 1))
            ydim = tile(ydim, (1, sc_dims[1]))

            resc = [ydim, xdim]
            xdim, ydim = None, None
            lon = map_coordinates(lon, resc, mode='nearest')
            lat = map_coordinates(lat, resc, mode='nearest')
        else:
            rescale = scf

    ## run through parameters
    for mi in maps:
        if not maps[mi]: continue

        if mi == 'parameters':
            if rgb_pan_sharpen:
                if map_parameters_pan & mapped:
                    lon = lon_pan * 1.0
                    lon_pan = None
                    lat = lat_pan * 1.0
                    lat_pan = None
                pan_data, lon_pan, lat_pan = None, None, None

            print('Mapping {}'.format(mi))
            if type(parameters) is not list: parameters = [parameters]
            for pid, par in enumerate(parameters):
                par = par.strip()
                pard = None

                ## check if this parameter exists
                if par not in l2w_datasets:
                    print('Parameter {} not in file {}.'.format(
                        par, inputfile))
                    continue

                print('Mapping {}'.format(par))
                ## read data
                data = nc_data(inputfile, par)
                if (rgb_pan_sharpen) & (map_parameters_pan):
                    data = zoom(data, zoom=2, order=1)

                ## rescale data
                if (scf != 1.0) and dataset_rescale:
                    data[isnan(data)] = mask_val
                    data = map_coordinates(data, resc, cval=mask_val)
                    data[data <= int(mask_val)] = nan
                    data[data <= 0] = nan

                data_range = nanpercentile(data, range_percentiles)

                ## get parameter mapping configuration
                if par in psc:
                    pard = copy.deepcopy(psc[par])
                else:
                    tmp = par.split('_')
                    par_generic = '_'.join((tmp[0:-1] + ['*']))
                    if par_generic in psc:
                        pard = copy.deepcopy(psc[par_generic])
                        try:  ## add wavelength to generic name
                            wave = int(tmp[len(tmp) - 1])
                            pard['name'] = '{} ({} nm)'.format(
                                pard['name'], wave)
                        except:
                            pass
                    else:
                        pard = {
                            'color table': 'default',
                            'min': data_range[0],
                            'max': data_range[1],
                            'log': False,
                            'name': par,
                            'unit': '',
                            'parameter': par
                        }

                if pard['color table'] == 'default':
                    pard['color table'] = 'viridis'
                ctfile = "{}/{}/{}.txt".format(ac.config['pp_data_dir'],
                                               'Shared/ColourTables',
                                               pard['color table'])

                if os.path.exists(ctfile):
                    from matplotlib.colors import ListedColormap
                    from numpy import loadtxt
                    pard['color table'] = ListedColormap(
                        loadtxt(ctfile) / 255.)

                if 'title' not in pard:
                    pard['title'] = '{} [{}]'.format(pard['name'],
                                                     pard['unit'])
                if auto_range:
                    pard['min'] = data_range[0]
                    pard['max'] = data_range[1]

                if isnan(pard['min']): pard['min'] = data_range[0]
                if isnan(pard['max']): pard['max'] = data_range[1]

                ## outputfile
                outputfile = '{}/{}_{}.png'.format(odir, obase, par)

                if map_title:
                    title = '{} {}/{} {}'.format(
                        pard['name'], sat, sen,
                        stime.strftime('%Y-%m-%d (%H:%M UTC)'))
                else:
                    title = None

                ## use qmap option
                if mapped:
                    range = (pard['min'], pard['max'])
                    if 'limit' in gatts:
                        limit = gatts['limit']

                    if ('xx' not in locals()):
                        xx, yy, m = qmap(data,
                                         lon,
                                         lat,
                                         outputfile=outputfile,
                                         title=title,
                                         rescale=rescale,
                                         colorbar=map_colorbar_orientation,
                                         colorbar_edge=map_colorbar_edge,
                                         cmap=pard['color table'],
                                         label=pard['title'],
                                         range=range,
                                         log=pard['log'],
                                         map_fillcolor=map_fillcolor,
                                         limit=limit,
                                         dpi=dpi,
                                         points=map_points,
                                         projection=map_projection,
                                         scalebar=map_scalebar,
                                         scalepos=map_scalepos,
                                         scalecolor=map_scalecolor,
                                         scalelen=map_scalelen)
                    else:
                        xx, yy, m = qmap(data,
                                         lon,
                                         lat,
                                         outputfile=outputfile,
                                         title=title,
                                         rescale=rescale,
                                         colorbar=map_colorbar_orientation,
                                         colorbar_edge=map_colorbar_edge,
                                         cmap=pard['color table'],
                                         label=pard['title'],
                                         range=range,
                                         log=pard['log'],
                                         map_fillcolor=map_fillcolor,
                                         limit=limit,
                                         dpi=dpi,
                                         points=map_points,
                                         projection=map_projection,
                                         scalebar=map_scalebar,
                                         scalepos=map_scalepos,
                                         scalecolor=map_scalecolor,
                                         scalelen=map_scalelen,
                                         xx=xx,
                                         yy=yy,
                                         m=m)

                else:
                    import matplotlib.cm as cm
                    from matplotlib.colors import ListedColormap
                    cmap = cm.get_cmap(pard['color table'])
                    cmap.set_bad(map_fillcolor)
                    cmap.set_under(map_fillcolor)

                    if not map_raster:
                        ## set up plot
                        fig = plt.figure()
                        canvas = matplotlib.backends.backend_agg.FigureCanvasAgg(
                            fig)
                        ax = fig.add_subplot(111)

                        print(pard['min'], pard['max'])

                        if pard['log']:
                            from matplotlib.colors import LogNorm
                            cax = ax.imshow(data,
                                            vmin=pard['min'],
                                            vmax=pard['max'],
                                            cmap=cmap,
                                            norm=LogNorm(vmin=pard['min'],
                                                         vmax=pard['max']))
                        else:
                            cax = ax.imshow(data,
                                            vmin=pard['min'],
                                            vmax=pard['max'],
                                            cmap=cmap)

                        if map_colorbar:
                            if map_colorbar_orientation == 'vertical':
                                cbar = fig.colorbar(cax,
                                                    orientation='vertical')
                                cbar.ax.set_ylabel(pard['title'])
                            else:
                                cbar = fig.colorbar(cax,
                                                    orientation='horizontal')
                                cbar.ax.set_xlabel(pard['title'])

                            if map_title: ax.set_title(title)
                            ax.axis('off')
                            canvas.print_figure(outputfile,
                                                dpi=dpi,
                                                bbox_inches='tight')
                        plt.close()
                    else:
                        from PIL import Image
                        ## rescale for mapping
                        if pard['log']:
                            from numpy import log10
                            datasc = datascl(log10(data),
                                             dmin=log10(pard['min']),
                                             dmax=log10(pard['max']))
                        else:
                            datasc = datascl(data,
                                             dmin=pard['min'],
                                             dmax=pard['max'])

                        d = cmap(datasc)
                        for wi in (0, 1, 2):
                            ## convert back to 8 bit channels (not ideal)
                            d_ = datascl(d[:, :, wi], dmin=0, dmax=1)
                            if wi == 0: im = d_
                            else: im = dstack((im, d_))

                        img = Image.fromarray(im)

                        ## output image
                        img.save(outputfile)

                print('Wrote {}'.format(outputfile))
        else:
            print('Mapping RGB {}'.format(mi))
            ## RGBs
            waves = [
                float(ds.split('_')[1]) for ds in l2w_datasets if ds[0:4] == mi
            ]
            if len(waves) == 0:
                print('No appropriate datasets found for RGB {} in {}'.format(
                    mi, inputfile))
                continue

            ## read datasets
            for wi, wl in enumerate([red_wl, green_wl, blue_wl]):
                idx, wave = closest_idx(waves, wl)
                cpar = '{}_{}'.format(mi, int(wave))
                ## read data
                data = nc_data(inputfile, cpar)

                if rgb_pan_sharpen:
                    data = zoom(data, zoom=2, order=1)
                    if wi == 0: vis_i = data * 1.0
                    else: vis_i += data
                    if wi == 2:
                        vis_i /= 3
                        pan_i = vis_i / pan_data
                        vis_i = None

                ## rescale data
                if (scf != 1.0) and dataset_rescale:
                    data[isnan(data)] = mask_val
                    data = map_coordinates(data, resc, cval=mask_val)
                    data[data <= int(mask_val)] = nan
                    data[data <= 0] = nan

                ## stack image
                if wi == 0:
                    image = data
                else:
                    image = dstack((image, data))

            ## rescale data between 0 and 1
            for wi in (2, 1, 0):
                if rgb_pan_sharpen: image[:, :, wi] /= pan_i
                image[:, :, wi] = datascl(
                    image[:, :, wi], dmin=rgb_min[wi], dmax=rgb_max[wi]) / 255.

            par = r'$\rho_{}$'.format(mi[3]) + ' RGB'
            if map_title:
                title = '{} {}/{} {}'.format(
                    par, sat, sen, stime.strftime('%Y-%m-%d (%H:%M UTC)'))
            else:
                title = None

            ## outputfile
            if rgb_pan_sharpen:
                outputfile = '{}/{}_rgb_{}_pan.png'.format(odir, obase, mi)
            else:
                outputfile = '{}/{}_rgb_{}.png'.format(odir, obase, mi)

            # use qmap option
            if mapped:
                if 'limit' in gatts:
                    limit = gatts['limit']

                if rgb_pan_sharpen:
                    ret = qmap(image,
                               lon_pan,
                               lat_pan,
                               outputfile=outputfile,
                               title=title,
                               rescale=rescale,
                               colorbar=map_colorbar_orientation,
                               colorbar_edge=map_colorbar_edge,
                               limit=limit,
                               dpi=dpi,
                               points=map_points,
                               projection=map_projection,
                               scalebar=map_scalebar,
                               scalepos=map_scalepos,
                               scalecolor=map_scalecolor_rgb,
                               scalelen=map_scalelen)
                    ret = None
                else:
                    if ('xx' not in locals()):
                        xx, yy, m = qmap(image,
                                         lon,
                                         lat,
                                         outputfile=outputfile,
                                         title=title,
                                         rescale=rescale,
                                         colorbar=map_colorbar_orientation,
                                         colorbar_edge=map_colorbar_edge,
                                         limit=limit,
                                         dpi=dpi,
                                         points=map_points,
                                         projection=map_projection,
                                         scalebar=map_scalebar,
                                         scalepos=map_scalepos,
                                         scalecolor=map_scalecolor_rgb,
                                         scalelen=map_scalelen)
                    else:
                        xx, yy, m = qmap(image,
                                         lon,
                                         lat,
                                         outputfile=outputfile,
                                         title=title,
                                         rescale=rescale,
                                         colorbar=map_colorbar_orientation,
                                         colorbar_edge=map_colorbar_edge,
                                         limit=limit,
                                         dpi=dpi,
                                         points=map_points,
                                         projection=map_projection,
                                         scalebar=map_scalebar,
                                         scalepos=map_scalepos,
                                         scalecolor=map_scalecolor_rgb,
                                         scalelen=map_scalelen,
                                         xx=xx,
                                         yy=yy,
                                         m=m)

            else:
                if not map_raster:
                    ## set up plot
                    fig = plt.figure()
                    canvas = matplotlib.backends.backend_agg.FigureCanvasAgg(
                        fig)
                    ax = fig.add_subplot(111)
                    ax.imshow(image)
                    image = None

                    if map_title: ax.set_title(title)
                    ax.axis('off')
                    canvas.print_figure(outputfile,
                                        dpi=dpi,
                                        bbox_inches='tight')
                    plt.close()
                else:
                    from PIL import Image
                    for wi in (0, 1, 2):
                        # convert again to 8 bit channels (not ideal)
                        data = datascl(image[:, :, wi], dmin=0, dmax=1)
                        if wi == 0:
                            im = data
                        else:
                            im = dstack((im, data))

                    img = Image.fromarray(im)
                    img.save(outputfile)

            print('Wrote {}'.format(outputfile))