diff --git a/COTconvert.py b/COTconvert.py deleted file mode 100644 index 1d073c9f8d3249604ef25c545d5a2480e55ded28..0000000000000000000000000000000000000000 --- a/COTconvert.py +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env python - -### System libraries -import time, string, sys -from os import path - -import numpy as np -from bisect import bisect,bisect_right,bisect_left - -def binary_search_SP(cpsval,size,cpsArr): - status = 0 - if ((cpsval > cpsArr[0]) and (cpsval < cpsArr[-1])): - #-- This is for unit offset - #l_index = 0 - #u_index = size+1 - #-- This is for zero offset - l_index = -1 - u_index = size - while ((u_index - l_index) > 1): - search_index = (u_index + l_index)/2 - if (cpsval <= cpsArr[search_index]): - u_index = search_index - else : - l_index = search_index - - elif (cpsval <= cpsArr[0]): - print "cpsval <= minimum in LUT" - l_index = 0 - u_index = 1 - status = 1 - elif (cpsval >= cpsArr[-1]): - print "cpsval >= maximum in LUT" - l_index = size-2 - u_index = size-1 - status = 1 - else : - print "Some situation not captured, passing!!!" - pass - - return [u_index, l_index, status] - -def COTconvert(nCOT,COTlut,CPS,COTvis): - - cpsArr = COTlut[:,0] - visIrEfficRatio = COTlut[:,1] - - # If CPS exceeds largest value in LUT, assign to COTir - # the last COT value in LUT - - if ( CPS > cpsArr[-1]) : - s = visIrEfficRatio[-1] - else : - u_index,l_index,status = binary_search_SP(CPS, nCOT, cpsArr) - - print "Finished binary search..." - print "l_index = ",l_index - print "u_index = ",u_index - - m = (visIrEfficRatio[u_index]-visIrEfficRatio[l_index])/(cpsArr[u_index]-cpsArr[l_index]) - b = visIrEfficRatio[u_index]-m*cpsArr[u_index] - s = m*CPS+b - - print "cpsArr[l_index] = ",cpsArr[l_index] - print "cpsArr[u_index] = ",cpsArr[u_index] - print "visIrEfficRatio[l_index] = ",visIrEfficRatio[l_index] - print "visIrEfficRatio[u_index] = ",visIrEfficRatio[u_index] - print "s = ",s - - # Compute the IR COT - COTir = COTvis/s - return COTir - -def readCOTfile(file): - results = [] - file=str(file) - f = open(file,'r') - - lines = f.readlines()[1:] - f.close() - for l in lines: - fields = l.split() - CPS = float(fields[0]) - COT = float(fields[1]) - all = [CPS,COT] - results.append(all) - - results = np.array(results) - nCOT = np.shape(results)[0] - return results,nCOT - -def main(cot,cps): - file = '/data/geoffc/MODIS_NPOESS/trunk/LEOCAT/data/VIIRS/LUT/CTP/COTlut.db' - COTlut,nCOT, = readCOTfile(file) - COTir = COTconvert(nCOT,COTlut,cps,cot) - print "COTir = ",COTir - -if __name__=='__main__': - cot = float(sys.argv[1]) - cps = float(sys.argv[2]) - print "Input cloud optical thickness: ",cot - print "Input cloud particle size: ",cps,"microns" - - main(cot,cps) diff --git a/LandWaterMask.py b/LandWaterMask.py new file mode 100644 index 0000000000000000000000000000000000000000..f7cff86f97de15dcf53992ab19ca96b6a97523ef --- /dev/null +++ b/LandWaterMask.py @@ -0,0 +1,527 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +LandWaterMask.py + + * DESCRIPTION: Class to granulate the DEM Land Water Mask data product + +Created by Geoff Cureton on 2013-03-05. +Copyright (c) 2013 University of Wisconsin SSEC. All rights reserved. +""" + +file_Date = '$Date: 2014-09-08 17:01:37 -0500 (Mon, 08 Sep 2014) $' +file_Revision = '$Revision: 2224 $' +file_Author = '$Author: geoffc $' +file_HeadURL = '$HeadURL: https://svn.ssec.wisc.edu/repos/jpss_adl/trunk/scripts/edr/GridIP/LandWaterMask.py $' +file_Id = '$Id: LandWaterMask.py 2224 2014-09-08 22:01:37Z geoffc $' + +__author__ = 'G.P. Cureton <geoff.cureton@ssec.wisc.edu>' +__version__ = '$Id: LandWaterMask.py 2224 2014-09-08 22:01:37Z geoffc $' +__docformat__ = 'Epytext' + + + +import os, sys, logging, traceback +from os import path,uname,environ +import string +import re +import uuid +from glob import glob +from time import time +from datetime import datetime,timedelta + +from scipy import round_ + +import numpy as np +from numpy import ma +import copy +from bisect import bisect_left,bisect_right + +import ctypes +from numpy.ctypeslib import ndpointer + +import tables as pytables +from tables import exceptions as pyEx + +import ViirsData + +# skim and convert routines for reading .asc metadata fields of interest +import adl_blob2 as adl_blob +import adl_asc +from adl_asc import skim_dir, contiguous_granule_groups, granule_groups_contain, effective_anc_contains,eliminate_duplicates,_is_contiguous, RDR_REQUIRED_KEYS, POLARWANDER_REQUIRED_KEYS +from adl_common import ADL_HOME, CSPP_RT_HOME, CSPP_RT_ANC_HOME + +# every module should have a LOG object +try : + sourcename= file_Id.split(" ") + LOG = logging.getLogger(sourcename[1]) +except : + LOG = logging.getLogger('LandWaterMask') + +from Utils import getURID, getAscLine, getAscStructs, findDatelineCrossings, shipOutToFile +from Utils import index, find_lt, find_le, find_gt, find_ge +from Utils import plotArr + + +class LandWaterMask() : + + def __init__(self,inDir=None, sdrEndian=None, ancEndian=None): + self.collectionShortName = 'VIIRS-GridIP-VIIRS-Lwm-Mod-Gran' + self.xmlName = 'VIIRS_GRIDIP_VIIRS_LWM_MOD_GRAN.xml' + self.blobDatasetName = 'landWaterMask' + self.dataType = 'uint8' + self.sourceType = 'DEM' + self.sourceList = [''] + self.trimObj = ViirsData.ViirsTrimTable() + + if inDir is None : + self.inDir = path.abspath(path.curdir) + else : + self.inDir = inDir + + if sdrEndian is None : + self.sdrEndian = adl_blob.LITTLE_ENDIAN + else : + self.sdrEndian = sdrEndian + + if ancEndian is None : + self.ancEndian = adl_blob.LITTLE_ENDIAN + else : + self.ancEndian = ancEndian + + # Digital Elevation Model (DEM) land sea mask types + self.DEM_list = ['DEM_SHALLOW_OCEAN','DEM_LAND','DEM_COASTLINE', + 'DEM_SHALLOW_INLAND_WATER','DEM_EPHEMERAL_WATER', + 'DEM_DEEP_INLAND_WATER','DEM_MOD_CONT_OCEAN','DEM_DEEP_OCEAN'] + self.DEM_dict = { + 'DEM_SHALLOW_OCEAN' : 0, + 'DEM_LAND' : 1, + 'DEM_COASTLINE' : 2, + 'DEM_SHALLOW_INLAND_WATER' : 3, + 'DEM_EPHEMERAL_WATER' : 4, + 'DEM_DEEP_INLAND_WATER' : 5, + 'DEM_MOD_CONT_OCEAN' : 6, + 'DEM_DEEP_OCEAN' : 7 + } + + + def setGeolocationInfo(self,dicts): + ''' + Populate this class instance with the geolocation data for a single granule + ''' + # Set some environment variables and paths + ANC_SCRIPTS_PATH = path.join(CSPP_RT_HOME,'viirs') + ADL_ASC_TEMPLATES = path.join(ANC_SCRIPTS_PATH,'asc_templates') + + # Collect some data from the geolocation dictionary + self.geoDict = dicts + URID = dicts['URID'] + geo_Collection_ShortName = dicts['N_Collection_Short_Name'] + N_Granule_ID = dicts['N_Granule_ID'] + ObservedStartTimeObj = dicts['ObservedStartTime'] + geoFiles = glob('%s/%s*' % (self.inDir,URID)) + geoFiles.sort() + + LOG.debug("###########################") + LOG.debug(" Geolocation Information ") + LOG.debug("###########################") + LOG.debug("N_Granule_ID : %r" % (N_Granule_ID)) + LOG.debug("ObservedStartTime : %s" % (ObservedStartTimeObj.__str__())) + LOG.debug("N_Collection_Short_Name : %s" %(geo_Collection_ShortName)) + LOG.debug("URID : %r" % (URID)) + LOG.debug("geoFiles : %r" % (geoFiles)) + LOG.debug("###########################") + + # Do we have terrain corrected geolocation? + terrainCorrectedGeo = True if 'GEO-TC' in geo_Collection_ShortName else False + + # Do we have long or short style geolocation field names? + if (geo_Collection_ShortName=='VIIRS-MOD-GEO-TC' or geo_Collection_ShortName=='VIIRS-MOD-RGEO') : + longFormGeoNames = True + LOG.debug("We have long form geolocation names") + elif (geo_Collection_ShortName=='VIIRS-MOD-GEO' or geo_Collection_ShortName=='VIIRS-MOD-RGEO-TC') : + LOG.debug("We have short form geolocation names") + longFormGeoNames = False + else : + LOG.error("Invalid geolocation shortname: %s" %(geo_Collection_ShortName)) + return -1 + + # Get the geolocation xml file + + geoXmlFile = "%s.xml" % (string.replace(geo_Collection_ShortName,'-','_')) + geoXmlFile = path.join(ADL_HOME,'xml/VIIRS',geoXmlFile) + if path.exists(geoXmlFile): + LOG.debug("We are using for %s: %s,%s" %(geo_Collection_ShortName,geoXmlFile,geoFiles[0])) + + # Open the geolocation blob and get the latitude and longitude + + endian = self.sdrEndian + + geoBlobObj = adl_blob.map(geoXmlFile,geoFiles[0], endian=endian) + + # Get scan_mode to find any bad scans + + scanMode = geoBlobObj.scan_mode[:] + badScanIdx = np.where(scanMode==254)[0] + LOG.debug("Bad Scans: %r" % (badScanIdx)) + + # Detemine the min, max and range of the latitude and longitude, + # taking care to exclude any fill values. + + if longFormGeoNames : + if endian==adl_blob.BIG_ENDIAN: + latitude = getattr(geoBlobObj,'latitude').byteswap() + longitude = getattr(geoBlobObj,'longitude').byteswap() + latitude = latitude.astype('float') + longitude = longitude.astype('float') + else: + latitude = getattr(geoBlobObj,'latitude').astype('float') + longitude = getattr(geoBlobObj,'longitude').astype('float') + else : + latitude = getattr(geoBlobObj,'lat').astype('float') + longitude = getattr(geoBlobObj,'lon').astype('float') + + latitude = ma.masked_less(latitude,-800.) + latMin,latMax = np.min(latitude),np.max(latitude) + latRange = latMax-latMin + + longitude = ma.masked_less(longitude,-800.) + lonMin,lonMax = np.min(longitude),np.max(longitude) + lonRange = lonMax-lonMin + + LOG.debug("min,max,range of latitide: %f %f %f" % (latMin,latMax,latRange)) + LOG.debug("min,max,range of longitude: %f %f %f" % (lonMin,lonMax,lonRange)) + + # Determine the latitude and longitude fill masks, so we can restore the + # fill values after we have scaled... + + latMask = latitude.mask + lonMask = longitude.mask + + # Check if the geolocation is in radians, convert to degrees + if 'RGEO' in geo_Collection_ShortName : + LOG.debug("Geolocation is in radians, convert to degrees...") + latitude = np.degrees(latitude) + longitude = np.degrees(longitude) + + latMin,latMax = np.min(latitude),np.max(latitude) + latRange = latMax-latMin + lonMin,lonMax = np.min(longitude),np.max(longitude) + lonRange = lonMax-lonMin + + LOG.debug("New min,max,range of latitude: %f %f %f" % (latMin,latMax,latRange)) + LOG.debug("New min,max,range of longitude: %f %f %f" % (lonMin,lonMax,lonRange)) + + # Restore fill values to masked pixels in geolocation + + geoFillValue = self.trimObj.sdrTypeFill['VDNE_FLOAT64_FILL'][latitude.dtype.name] + latitude = ma.array(latitude,mask=latMask,fill_value=geoFillValue) + self.latitude = latitude.filled() + + geoFillValue = self.trimObj.sdrTypeFill['VDNE_FLOAT64_FILL'][longitude.dtype.name] + longitude = ma.array(longitude,mask=lonMask,fill_value=geoFillValue) + self.longitude = longitude.filled() + + # Shift the longitudes to be between -180 and 180 degrees + if lonMax > 180. : + LOG.debug("\nFinal min,max,range of longitude: %f %f %f" % (lonMin,lonMax,lonRange)) + dateLineIdx = np.where(longitude>180.) + LOG.debug("dateLineIdx = %r" % (dateLineIdx)) + longitude[dateLineIdx] -= 360. + lonMax = np.max(ma.array(longitude,mask=lonMask)) + lonMin = np.min(ma.array(longitude,mask=lonMask)) + lonRange = lonMax-lonMin + LOG.debug("\nFinal min,max,range of longitude: %f %f %f" % (lonMin,lonMax,lonRange)) + + # Record the corners, taking care to exclude any bad scans... + nDetectors = 16 + firstGoodScan = np.where(scanMode<=2)[0][0] + lastGoodScan = np.where(scanMode<=2)[0][-1] + firstGoodRow = firstGoodScan * nDetectors + lastGoodRow = lastGoodScan * nDetectors + nDetectors - 1 + + latCrnList = [latitude[firstGoodRow,0],latitude[firstGoodRow,-1],latitude[lastGoodRow,0],latitude[lastGoodRow,-1]] + lonCrnList = [longitude[firstGoodRow,0],longitude[firstGoodRow,-1],longitude[lastGoodRow,0],longitude[lastGoodRow,-1]] + + # Check for dateline/pole crossings + num180Crossings = findDatelineCrossings(latCrnList,lonCrnList) + LOG.debug("We have %d dateline crossings."%(num180Crossings)) + + # Copy the geolocation information to the class object + self.latMin = latMin + self.latMax = latMax + self.latRange = latRange + self.lonMin = lonMin + self.lonMax = lonMax + self.lonRange = lonRange + self.scanMode = scanMode + self.latitude = latitude + self.longitude = longitude + self.latCrnList = latCrnList + self.lonCrnList = lonCrnList + self.num180Crossings = num180Crossings + + # Parse the geolocation asc file to get struct information which will be + # written to the ancillary asc files + + geoAscFileName = path.join(self.inDir,URID+".asc") + LOG.debug("\nOpening %s..." % (geoAscFileName)) + + geoAscFile = open(geoAscFileName,'rt') + + self.ObservedDateTimeStr = getAscLine(geoAscFile,"ObservedDateTime") + #self.RangeDateTimeStr = self.ObservedDateTimeStr + self.RangeDateTimeStr = getAscLine(geoAscFile,"ObservedDateTime") + self.RangeDateTimeStr = string.replace(self.RangeDateTimeStr,"ObservedDateTime","RangeDateTime") + self.GRingLatitudeStr = getAscStructs(geoAscFile,"GRingLatitude",12) + self.GRingLongitudeStr = getAscStructs(geoAscFile,"GRingLongitude",12) + + self.North_Bounding_Coordinate_Str = getAscLine(geoAscFile,"North_Bounding_Coordinate") + self.South_Bounding_Coordinate_Str = getAscLine(geoAscFile,"South_Bounding_Coordinate") + self.East_Bounding_Coordinate_Str = getAscLine(geoAscFile,"East_Bounding_Coordinate") + self.West_Bounding_Coordinate_Str = getAscLine(geoAscFile,"West_Bounding_Coordinate") + + geoAscFile.close() + + + def subset(self): + '''Subsets the LSM dataset to cover the required geolocation range.''' + + # Get the subset of DEM global dataset. + + DEM_dLat = 30.*(1./3600.) + DEM_dLon = 30.*(1./3600.) + + DEM_fileName = path.join(CSPP_RT_ANC_HOME,'LSM/dem30ARC_Global_LandWater_uncompressed.h5') + self.sourceList.append(path.basename(DEM_fileName)) + + try : + # TODO : Use original HDF4 file which contains elevation and LWM. + DEMobj = pytables.openFile(DEM_fileName,'r') + DEM_node = DEMobj.getNode('/demGRID/Data Fields/LandWater') + except Exception, err : + LOG.exception("%s"%(err)) + LOG.exception("Problem opening DEM file (%s), aborting."%(DEM_fileName)) + sys.exit(1) + + try : + DEM_gridLats = -1. * (np.arange(21600.) * DEM_dLat - 90.) + DEM_gridLons = np.arange(43200.) * DEM_dLon - 180. + + LOG.debug("min,max DEM Grid Latitude values : %f,%f"%(DEM_gridLats[0],DEM_gridLats[-1])) + LOG.debug("min,max DEM Grid Longitude values : %f,%f"%(DEM_gridLons[0],DEM_gridLons[-1])) + + latMin = self.latMin + latMax = self.latMax + lonMin = self.lonMin + lonMax = self.lonMax + + DEM_latMask = np.equal((DEM_gridLats<(latMax+DEM_dLat)),(DEM_gridLats>(latMin-DEM_dLat))) + DEM_lonMask = np.equal((DEM_gridLons<(lonMax+DEM_dLon)),(DEM_gridLons>(lonMin-DEM_dLon))) + + DEM_latIdx = np.where(DEM_latMask==True)[0] + DEM_lonIdx = np.where(DEM_lonMask==True)[0] + + DEM_latMinIdx = DEM_latIdx[0] + DEM_latMaxIdx = DEM_latIdx[-1] + DEM_lonMinIdx = DEM_lonIdx[0] + DEM_lonMaxIdx = DEM_lonIdx[-1] + + LOG.debug("DEM_latMinIdx = %d" % (DEM_latMinIdx)) + LOG.debug("DEM_latMaxIdx = %d" % (DEM_latMaxIdx)) + LOG.debug("DEM_lonMinIdx = %d" % (DEM_lonMinIdx)) + LOG.debug("DEM_lonMaxIdx = %d" % (DEM_lonMaxIdx)) + + lat_subset = DEM_gridLats[DEM_latMinIdx:DEM_latMaxIdx+1] + self.gridLat = lat_subset + + if self.num180Crossings == 2 : + + # We have a dateline crossing, so subset the positude and negative + # longitude grids and sandwich them together. + posLonCrn = np.min(ma.masked_less_equal(np.array(self.lonCrnList),0.)) + negLonCrn = np.max(ma.masked_outside(np.array(self.lonCrnList),-800.,0.)) + posIdx = index(DEM_gridLons,find_lt(DEM_gridLons,posLonCrn)) + negIdx = index(DEM_gridLons,find_gt(DEM_gridLons,negLonCrn)) + + posLons_subset = DEM_gridLons[posIdx:] + negLons_subset = DEM_gridLons[:negIdx] + lon_subset = np.concatenate((posLons_subset,negLons_subset)) + + # Do the same with the DEM data + posBlock = DEM_node[DEM_latMinIdx:DEM_latMaxIdx+1,posIdx:] + negBlock = DEM_node[DEM_latMinIdx:DEM_latMaxIdx+1,:negIdx] + DEM_subset = np.concatenate((posBlock,negBlock),axis=1) + + else : + + DEM_subset = DEM_node[DEM_latMinIdx:DEM_latMaxIdx+1,DEM_lonMinIdx:DEM_lonMaxIdx+1] + lon_subset = DEM_gridLons[DEM_lonMinIdx:DEM_lonMaxIdx+1] + + self.gridLon = lon_subset + + # Copy DEM data to the GridIP object + self.gridData = DEM_subset.astype(self.dataType) + + DEM_node.close() + DEMobj.close() + + except Exception, err : + + LOG.debug("EXCEPTION: %s" % (err)) + + DEM_node.close() + DEMobj.close() + + + def _grid2Gran(self, dataLat, dataLon, gridData, gridLat, gridLon): + '''Granulates a gridded dataset using an input geolocation''' + + nData = np.int64(dataLat.size) + gridRows = np.int32(gridLat.shape[0]) + gridCols = np.int32(gridLat.shape[1]) + + data = np.ones(np.shape(dataLat),dtype=np.float64)* -999.9 + dataIdx = np.ones(np.shape(dataLat),dtype=np.int64) * -99999 + + ANC_SCRIPTS_PATH = path.join(CSPP_RT_HOME,'viirs') + + libFile = path.join(ANC_SCRIPTS_PATH,'libgriddingAndGranulation.so.1.0.1') + LOG.debug("Gridding and granulation library file: %s" % (libFile)) + lib = ctypes.cdll.LoadLibrary(libFile) + grid2gran = lib.grid2gran_nearest + grid2gran.restype = None + grid2gran.argtypes = [ + ndpointer(ctypes.c_double,ndim=1,shape=(nData),flags='C_CONTIGUOUS'), + ndpointer(ctypes.c_double,ndim=1,shape=(nData),flags='C_CONTIGUOUS'), + ndpointer(ctypes.c_double,ndim=1,shape=(nData),flags='C_CONTIGUOUS'), + ctypes.c_int64, + ndpointer(ctypes.c_double,ndim=2,shape=(gridRows,gridCols),flags='C_CONTIGUOUS'), + ndpointer(ctypes.c_double,ndim=2,shape=(gridRows,gridCols),flags='C_CONTIGUOUS'), + ndpointer(ctypes.c_double,ndim=2,shape=(gridRows,gridCols),flags='C_CONTIGUOUS'), + ndpointer(ctypes.c_int64,ndim=1,shape=(nData),flags='C_CONTIGUOUS'), + ctypes.c_int32, + ctypes.c_int32 + ] + + ''' + int snapGrid_ctypes(double *lat, + double *lon, + double *data, + long nData, + double *gridLat, + double *gridLon, + double *gridData, + long *gridDataIdx, + int nGridRows, + int nGridCols + ) + ''' + + LOG.debug("Calling C routine grid2gran()...") + + retVal = grid2gran(dataLat, + dataLon, + data, + nData, + gridLat, + gridLon, + gridData, + dataIdx, + gridRows, + gridCols) + + LOG.debug("Returning from C routine grid2gran()") + + + return data,dataIdx + + + def granulate(self,GridIP_objects): + ''' + Granulates the GridIP DEM files. + ''' + + # Generate the lat and lon grids, and flip them and the data over latitude + gridLon,gridLat = np.meshgrid(self.gridLon,self.gridLat[::-1]) + gridData = self.gridData[::-1,:] + + latitude = self.latitude + longitude = self.longitude + + # If we have a dateline crossing, remove the longitude discontinuity + # by adding 360 degrees to the negative longitudes. + if self.num180Crossings == 2 : + gridLonNegIdx = np.where(gridLon < 0.) + gridLon[gridLonNegIdx] += 360. + longitudeNegIdx = np.where(longitude < 0.) + longitude[longitudeNegIdx] += 360. + + LOG.info("Granulating %s ..." % (self.collectionShortName)) + LOG.debug("latitide,longitude shapes: %s, %s"%(str(latitude.shape) , str(longitude.shape))) + LOG.debug("gridData.shape = %s" % (str(gridData.shape))) + LOG.debug("gridLat.shape = %s" % (str(gridLat.shape))) + LOG.debug("gridLon.shape = %s" % (str(gridLon.shape))) + + LOG.debug("min of gridData = %r"%(np.min(gridData))) + LOG.debug("max of gridData = %r"%(np.max(gridData))) + + t1 = time() + data,dataIdx = self._grid2Gran(np.ravel(latitude), + np.ravel(longitude), + gridData.astype(np.float64), + gridLat.astype(np.float64), + gridLon.astype(np.float64)) + t2 = time() + elapsedTime = t2-t1 + LOG.info("Granulation took %f seconds for %d points" % (elapsedTime,latitude.size)) + + data = data.reshape(latitude.shape) + dataIdx = dataIdx.reshape(latitude.shape) + + # Convert granulated data back to original type... + data = data.astype(self.dataType) + + # Convert any "inland water" to "sea water" + shallowInlandWaterValue = self.DEM_dict['DEM_SHALLOW_INLAND_WATER'] + shallowOceanValue = self.DEM_dict['DEM_SHALLOW_OCEAN'] + deepInlandWaterValue = self.DEM_dict['DEM_DEEP_INLAND_WATER'] + deepOceanValue = self.DEM_dict['DEM_DEEP_OCEAN'] + + shallowInlandWaterMask = ma.masked_equal(data,shallowInlandWaterValue).mask + shallowOceanMask = ma.masked_equal(data,shallowOceanValue).mask + deepInlandWaterMask = ma.masked_equal(data,deepInlandWaterValue).mask + + totalWaterMask = shallowInlandWaterMask #+ shallowOceanMask + deepInlandWaterMask + + data = ma.array(data,mask=totalWaterMask,fill_value=deepOceanValue) + data = data.filled() + + + LOG.debug("Shape of granulated %s data is %s" % (self.collectionShortName,np.shape(data))) + LOG.debug("Shape of granulated %s dataIdx is %s" % (self.collectionShortName,np.shape(dataIdx))) + + # Explicitly restore geolocation fill to the granulated data... + fillMask = ma.masked_less(self.latitude,-800.).mask + fillValue = self.trimObj.sdrTypeFill['MISS_FILL'][self.dataType] + data = ma.array(data,mask=fillMask,fill_value=fillValue) + self.data = data.filled() + + # Moderate resolution trim table arrays. These are + # bool arrays, and the trim pixels are set to True. + #modTrimMask = self.trimObj.createModTrimArray(nscans=48,trimType=bool) + + # Fill the required pixel trim rows in the granulated GridIP data with + # the ONBOARD_PT_FILL value for the correct data type + + #fillValue = self.trimObj.sdrTypeFill['ONBOARD_PT_FILL'][self.dataType] + #data = ma.array(data,mask=modTrimMask,fill_value=fillValue) + #self.data = data.filled() + + + def shipOutToFile(self): + ''' Pass the current class instance to this Utils method to generate + a blob/asc file pair from the input ancillary data object.''' + + #shipOutToFile(self) + pass diff --git a/PrecipWater.py b/PrecipWater.py new file mode 100644 index 0000000000000000000000000000000000000000..c34c81fc6f651c128f440ef38c914c8b19df5349 --- /dev/null +++ b/PrecipWater.py @@ -0,0 +1,434 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +PrecipWater.py + + * DESCRIPTION: Class to granulate the ViirsAncPrecipWater data product + +Created by Geoff Cureton on 2013-02-25. +Copyright (c) 2011 University of Wisconsin SSEC. All rights reserved. +""" + +file_Date = '$Date: 2014-09-19 15:13:47 -0500 (Fri, 19 Sep 2014) $' +file_Revision = '$Revision: 2235 $' +file_Author = '$Author: geoffc $' +file_HeadURL = '$HeadURL: https://svn.ssec.wisc.edu/repos/jpss_adl/trunk/scripts/edr/ANC/PrecipWater.py $' +file_Id = '$Id: PrecipWater.py 2235 2014-09-19 20:13:47Z geoffc $' + +__author__ = 'G.P. Cureton <geoff.cureton@ssec.wisc.edu>' +__version__ = '$Id: PrecipWater.py 2235 2014-09-19 20:13:47Z geoffc $' +__docformat__ = 'Epytext' + + + +import os, sys, logging, traceback +from os import path,uname,environ +import string +import re +import uuid +from glob import glob +from time import time +from datetime import datetime,timedelta + +from scipy import round_ + +import numpy as np +from numpy import ma +import copy +from bisect import bisect_left,bisect_right + +import ctypes +from numpy.ctypeslib import ndpointer + +import ViirsData + +from NCEPtoBlob import NCEPclass + +# skim and convert routines for reading .asc metadata fields of interest +import adl_blob2 as adl_blob +import adl_asc +from adl_asc import skim_dir, contiguous_granule_groups, granule_groups_contain, \ + effective_anc_contains,eliminate_duplicates,_is_contiguous, \ + RDR_REQUIRED_KEYS, POLARWANDER_REQUIRED_KEYS +from adl_common import ADL_HOME, CSPP_RT_HOME, CSPP_RT_ANC_PATH, \ + CSPP_RT_ANC_CACHE_DIR, COMMON_LOG_CHECK_TABLE + +# every module should have a LOG object +try : + sourcename= file_Id.split(" ") + LOG = logging.getLogger(sourcename[1]) +except : + LOG = logging.getLogger('PrecipWater') + +from Utils import getURID, getAscLine, getAscStructs, findDatelineCrossings, \ + shipOutToFile + + +class PrecipWater() : + + def __init__(self,inDir=None, sdrEndian=None, ancEndian=None): + self.collectionShortName = 'VIIRS-ANC-Preci-Wtr-Mod-Gran' + self.xmlName = 'VIIRS_ANC_PRECI_WTR_MOD_GRAN.xml' + self.blobDatasetName = 'totalPrecipitableWater' + self.dataType = 'float32' + self.sourceType = 'NCEP_ANC_Int' + self.sourceList = [''] + self.trimObj = ViirsData.ViirsTrimTable() + + if inDir is None : + self.inDir = path.abspath(path.curdir) + else : + self.inDir = inDir + + if sdrEndian is None : + self.sdrEndian = adl_blob.LITTLE_ENDIAN + else : + self.sdrEndian = sdrEndian + + if ancEndian is None : + self.ancEndian = adl_blob.LITTLE_ENDIAN + else : + self.ancEndian = ancEndian + + + def ingest(self,ancBlob=None): + ''' + Ingest the ancillary dataset. + ''' + dates = [] + ncepBlobFiles = [] + for gridBlobStruct in ancBlob: + timeObj = gridBlobStruct[0] + ncepBlobFile = gridBlobStruct[1] + LOG.debug("VIIRS-ANC-Preci-Wtr-Mod-Gran %s --> %s" % \ + (ncepBlobFile,timeObj.strftime("%Y-%m-%d %H:%M:%S:%f"))) + dates.append(timeObj) + ncepBlobFiles.append(ncepBlobFile) + + self.date_0 = dates[0] + self.date_1 = dates[1] + + LOG.debug("Minimum NCEP date is: %s" %(self.date_0.strftime("%Y-%m-%d %H:%M:%S:%f"))) + LOG.debug("Maximum NCEP date is: %s" %(self.date_1.strftime("%Y-%m-%d %H:%M:%S:%f"))) + + ncepBlobFile_0 = ncepBlobFiles[0] + ncepBlobFile_1 = ncepBlobFiles[1] + + self.gridData_0 = getattr(ncepBlobFile_0,self.blobDatasetName).astype(self.dataType) + self.gridData_1 = getattr(ncepBlobFile_1,self.blobDatasetName).astype(self.dataType) + + + def setGeolocationInfo(self,dicts): + ''' + Populate this class instance with the geolocation data for a single granule + ''' + # Set some environment variables and paths + ANC_SCRIPTS_PATH = path.join(CSPP_RT_HOME,'viirs') + ADL_ASC_TEMPLATES = path.join(ANC_SCRIPTS_PATH,'asc_templates') + + # Collect some data from the geolocation dictionary + self.geoDict = dicts + URID = dicts['URID'] + geo_Collection_ShortName = dicts['N_Collection_Short_Name'] + N_Granule_ID = dicts['N_Granule_ID'] + ObservedStartTimeObj = dicts['ObservedStartTime'] + geoFiles = glob('%s/%s*' % (self.inDir,URID)) + geoFiles.sort() + + LOG.debug("###########################") + LOG.debug(" Geolocation Information ") + LOG.debug("###########################") + LOG.debug("N_Granule_ID : %r" % (N_Granule_ID)) + LOG.debug("ObservedStartTime : %s" % (ObservedStartTimeObj.__str__())) + LOG.debug("N_Collection_Short_Name : %s" %(geo_Collection_ShortName)) + LOG.debug("URID : %r" % (URID)) + LOG.debug("geoFiles : %r" % (geoFiles)) + LOG.debug("###########################") + + timeDelta = (self.date_1 - self.date_0).total_seconds() + LOG.debug("timeDelta is %r seconds" %(timeDelta)) + + timePrime = (ObservedStartTimeObj - self.date_0).total_seconds() + LOG.debug("timePrime is %r seconds (%f percent along time interval)" % \ + (timePrime,(timePrime/timeDelta)*100.)) + + delta_gridData = self.gridData_1 - self.gridData_0 + + self.gridData = (delta_gridData/timeDelta) * timePrime + self.gridData_0 + + gridData_0_avg = np.average(self.gridData_0) + gridData_1_avg = np.average(self.gridData_1) + gridData_avg = np.average(self.gridData) + LOG.debug("average(gridData_0) = %f" %(np.average(self.gridData_0))) + LOG.debug("average(gridData_1) = %f" %(np.average(self.gridData_1))) + LOG.debug("average(gridData) = %f" %(np.average(self.gridData))) + + # Do we have terrain corrected geolocation? + terrainCorrectedGeo = True if 'GEO-TC' in geo_Collection_ShortName else False + + # Do we have long or short style geolocation field names? + if (geo_Collection_ShortName=='VIIRS-MOD-GEO-TC' or geo_Collection_ShortName=='VIIRS-MOD-RGEO') : + longFormGeoNames = True + LOG.debug("We have long form geolocation names") + elif (geo_Collection_ShortName=='VIIRS-MOD-GEO' or geo_Collection_ShortName=='VIIRS-MOD-RGEO-TC') : + LOG.debug("We have short form geolocation names") + longFormGeoNames = False + else : + LOG.error("Invalid geolocation shortname: %s" %(geo_Collection_ShortName)) + return -1 + + # Get the geolocation xml file + + geoXmlFile = "%s.xml" % (string.replace(geo_Collection_ShortName,'-','_')) + geoXmlFile = path.join(ADL_HOME,'xml/VIIRS',geoXmlFile) + if path.exists(geoXmlFile): + LOG.debug("We are using for %s: %s,%s" %(geo_Collection_ShortName,geoXmlFile,geoFiles[0])) + + # Open the geolocation blob and get the latitude and longitude + + endian = self.sdrEndian + + geoBlobObj = adl_blob.map(geoXmlFile,geoFiles[0], endian=endian) + + # Get scan_mode to find any bad scans + + scanMode = geoBlobObj.scan_mode[:] + badScanIdx = np.where(scanMode==254)[0] + LOG.debug("Bad Scans: %r" % (badScanIdx)) + + # Detemine the min, max and range of the latitude and longitude, + # taking care to exclude any fill values. + + if longFormGeoNames : + if endian==adl_blob.BIG_ENDIAN: + latitude = getattr(geoBlobObj,'latitude').byteswap() + longitude = getattr(geoBlobObj,'longitude').byteswap() + latitude = latitude.astype('float') + longitude = longitude.astype('float') + else: + latitude = getattr(geoBlobObj,'latitude').astype('float') + longitude = getattr(geoBlobObj,'longitude').astype('float') + else : + latitude = getattr(geoBlobObj,'lat').astype('float') + longitude = getattr(geoBlobObj,'lon').astype('float') + + latitude = ma.masked_less(latitude,-800.) + latMin,latMax = np.min(latitude),np.max(latitude) + latRange = latMax-latMin + + longitude = ma.masked_less(longitude,-800.) + lonMin,lonMax = np.min(longitude),np.max(longitude) + lonRange = lonMax-lonMin + + LOG.debug("min,max,range of latitide: %f %f %f" % (latMin,latMax,latRange)) + LOG.debug("min,max,range of longitude: %f %f %f" % (lonMin,lonMax,lonRange)) + + # Determine the latitude and longitude fill masks, so we can restore the + # fill values after we have scaled... + + latMask = latitude.mask + lonMask = longitude.mask + + # Check if the geolocation is in radians, convert to degrees + if 'RGEO' in geo_Collection_ShortName : + LOG.debug("Geolocation is in radians, convert to degrees...") + latitude = np.degrees(latitude) + longitude = np.degrees(longitude) + + latMin,latMax = np.min(latitude),np.max(latitude) + latRange = latMax-latMin + lonMin,lonMax = np.min(longitude),np.max(longitude) + lonRange = lonMax-lonMin + + LOG.debug("New min,max,range of latitude: %f %f %f" % (latMin,latMax,latRange)) + LOG.debug("New min,max,range of longitude: %f %f %f" % (lonMin,lonMax,lonRange)) + + # Restore fill values to masked pixels in geolocation + + geoFillValue = self.trimObj.sdrTypeFill['VDNE_FLOAT64_FILL'][latitude.dtype.name] + latitude = ma.array(latitude,mask=latMask,fill_value=geoFillValue) + self.latitude = latitude.filled() + + geoFillValue = self.trimObj.sdrTypeFill['VDNE_FLOAT64_FILL'][longitude.dtype.name] + longitude = ma.array(longitude,mask=lonMask,fill_value=geoFillValue) + self.longitude = longitude.filled() + + # Record the corners, taking care to exclude any bad scans... + nDetectors = 16 + firstGoodScan = np.where(scanMode<=2)[0][0] + lastGoodScan = np.where(scanMode<=2)[0][-1] + firstGoodRow = firstGoodScan * nDetectors + lastGoodRow = lastGoodScan * nDetectors + nDetectors - 1 + + latCrnList = [latitude[firstGoodRow,0],latitude[firstGoodRow,-1],latitude[lastGoodRow,0],latitude[lastGoodRow,-1]] + lonCrnList = [longitude[firstGoodRow,0],longitude[firstGoodRow,-1],longitude[lastGoodRow,0],longitude[lastGoodRow,-1]] + + # Check for dateline/pole crossings + num180Crossings = findDatelineCrossings(latCrnList,lonCrnList) + LOG.debug("We have %d dateline crossings."%(num180Crossings)) + + # Copy the geolocation information to the class object + self.latMin = latMin + self.latMax = latMax + self.latRange = latRange + self.lonMin = lonMin + self.lonMax = lonMax + self.lonRange = lonRange + self.scanMode = scanMode + self.latitude = latitude + self.longitude = longitude + self.latCrnList = latCrnList + self.lonCrnList = lonCrnList + self.num180Crossings = num180Crossings + + # Parse the geolocation asc file to get struct information which will be + # written to the ancillary asc files + + geoAscFileName = path.join(self.inDir,URID+".asc") + LOG.debug("\nOpening %s..." % (geoAscFileName)) + + geoAscFile = open(geoAscFileName,'rt') + + self.RangeDateTimeStr = getAscLine(geoAscFile,"ObservedDateTime") + self.RangeDateTimeStr = string.replace(self.RangeDateTimeStr,"ObservedDateTime","RangeDateTime") + self.GRingLatitudeStr = getAscStructs(geoAscFile,"GRingLatitude",12) + self.GRingLongitudeStr = getAscStructs(geoAscFile,"GRingLongitude",12) + + geoAscFile.close() + + + def _grid2Gran_bilinearInterp(self,dataLat, dataLon, gridData, gridLat, gridLon): + '''Granulates a gridded dataset using an input geolocation''' + + nData = np.int64(dataLat.size) + gridRows = np.int32(gridLat.shape[0]) + gridCols = np.int32(gridLat.shape[1]) + + data = np.ones(np.shape(dataLat),dtype=np.float64)* -999.9 + dataIdx = np.ones(np.shape(dataLat),dtype=np.int64) * -99999 + + ANC_SCRIPTS_PATH = path.join(CSPP_RT_HOME,'viirs') + + libFile = path.join(ANC_SCRIPTS_PATH,'libgriddingAndGranulation.so.1.0.1') + LOG.debug("Gridding and granulation library file: %s" % (libFile)) + lib = ctypes.cdll.LoadLibrary(libFile) + grid2gran_bilinearInterp = lib.grid2gran_bilinearInterp + grid2gran_bilinearInterp.restype = None + grid2gran_bilinearInterp.argtypes = [ + ndpointer(ctypes.c_double,ndim=1,shape=(nData),flags='C_CONTIGUOUS'), + ndpointer(ctypes.c_double,ndim=1,shape=(nData),flags='C_CONTIGUOUS'), + ndpointer(ctypes.c_double,ndim=1,shape=(nData),flags='C_CONTIGUOUS'), + ctypes.c_int64, + ndpointer(ctypes.c_double,ndim=2,shape=(gridRows,gridCols),flags='C_CONTIGUOUS'), + ndpointer(ctypes.c_double,ndim=2,shape=(gridRows,gridCols),flags='C_CONTIGUOUS'), + ndpointer(ctypes.c_double,ndim=2,shape=(gridRows,gridCols),flags='C_CONTIGUOUS'), + ndpointer(ctypes.c_int64,ndim=1,shape=(nData),flags='C_CONTIGUOUS'), + ctypes.c_int32, + ctypes.c_int32 + ] + + ''' + int snapGrid_ctypes(double *lat, + double *lon, + double *data, + long nData, + double *gridLat, + double *gridLon, + double *gridData, + long *gridDataIdx, + int nGridRows, + int nGridCols + ) + ''' + + LOG.debug("Calling C routine grid2gran_bilinearInterp()...") + + retVal = grid2gran_bilinearInterp(dataLat, + dataLon, + data, + nData, + gridLat, + gridLon, + gridData, + dataIdx, + gridRows, + gridCols) + + LOG.debug("Returning from C routine grid2gran_bilinearInterp()") + + return data,dataIdx + + + def granulate(self,ANC_objects): + ''' + Granulate the ancillary dataset. + ''' + LOG.info("Granulating %s ..." % (self.collectionShortName)) + + degInc = 0.5 + + lats = np.arange(361.)*degInc - 90. + lons = np.arange(720.)*degInc - 180. + latitude = self.latitude + longitude = self.longitude + + gridData = self.gridData[:,:] + + if self.num180Crossings != 2 : + + #gridData = np.roll(gridData,360) # old + gridData = np.roll(gridData,360,axis=1) # new + gridLon,gridLat = np.meshgrid(lons,lats) + + LOG.debug("start,end NCEP Grid Latitude values : %f,%f"%(gridLat[0,0],gridLat[-1,0])) + LOG.debug("start,end NCEP Grid Longitude values : %f,%f"%(gridLon[0,0],gridLon[0,-1])) + + else : + + negLonIdx = np.where(lons<0) + lons[negLonIdx] += 360. + lons = np.roll(lons,360) + gridLon,gridLat = np.meshgrid(lons,lats) + + longitudeNegIdx = np.where(longitude < 0.) + longitude[longitudeNegIdx] += 360. + + LOG.debug("start,end NCEP Grid Latitude values : %f,%f"%(gridLat[0,0],gridLat[-1,0])) + LOG.debug("start,end NCEP Grid Longitude values : %f,%f"%(gridLon[0,0],gridLon[0,-1])) + + LOG.debug("min of gridData = %r"%(np.min(gridData))) + LOG.debug("max of gridData = %r"%(np.max(gridData))) + + t1 = time() + data,dataIdx = self._grid2Gran_bilinearInterp(np.ravel(latitude), + np.ravel(longitude), + gridData.astype(np.float64), + gridLat, + gridLon) + t2 = time() + elapsedTime = t2-t1 + LOG.info("Granulation took %f seconds for %d points" % (elapsedTime,latitude.size)) + + data = data.reshape(latitude.shape) + dataIdx = dataIdx.reshape(latitude.shape) + + LOG.debug("Shape of granulated %s data is %s" % (self.collectionShortName,np.shape(data))) + LOG.debug("Shape of granulated %s dataIdx is %s" % (self.collectionShortName,np.shape(dataIdx))) + + # Moderate resolution trim table arrays. These are + # bool arrays, and the trim pixels are set to True. + modTrimMask = self.trimObj.createModTrimArray(nscans=48,trimType=bool) + + # Fill the required pixel trim rows in the granulated NCEP data with + # the ONBOARD_PT_FILL value for the correct data type + + fillValue = self.trimObj.sdrTypeFill['ONBOARD_PT_FILL'][self.dataType] + data = ma.array(data,mask=modTrimMask,fill_value=fillValue) + self.data = data.filled() + + + def shipOutToFile(self): + ''' Pass the current class instance to this Utils method to generate + a blob/asc file pair from the input ancillary data object.''' + + return shipOutToFile(self) diff --git a/colourMapTools.py b/colourMapTools.py deleted file mode 100644 index ec047fce8962a63a507e91de95107118ca1d7b24..0000000000000000000000000000000000000000 --- a/colourMapTools.py +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/env python - -import numpy as np -from matplotlib.colors import Colormap, normalize, LinearSegmentedColormap -from types import IntType, FloatType, ListType -from scipy import interpolate - -def cmap_discretize(cmap, N): - """Return a discrete colormap from the continuous colormap cmap. - - cmap: colormap instance, eg. cm.jet. - N: Number of colors. - - Example - x = resize(arange(100), (5,100)) - djet = cmap_discretize(cm.jet, 5) - imshow(x, cmap=djet) - """ - - cdict = cmap._segmentdata.copy() - # N colors - colors_i = np.linspace(0,1.,N) - # N+1 indices - indices = np.linspace(0,1.,N+1) - for key in ('red','green','blue'): - # Find the N colors - D = np.array(cdict[key]) - I = interpolate.interp1d(D[:,0], D[:,1]) - #I = np.interp(indices,D[:,0], D[:,1]) - colors = I(colors_i) - # Place these colors at the correct indices. - A = np.zeros((N+1,3), float) - A[:,0] = indices - A[1:,1] = colors - A[:-1,2] = colors - # Create a tuple for the dictionary. - L = [] - for l in A: - L.append(tuple(l)) - cdict[key] = tuple(L) - - # Return colormap object. - return LinearSegmentedColormap('colormap',cdict,1024) - -def cmap_reverse(my_cmap): - my_newdict = {} - for items in my_cmap._segmentdata : - item_tuple = my_cmap._segmentdata[items] - new_item_tuple = [] - item_tuple_revIter = reversed(item_tuple) - for segments in item_tuple : - dummyTuple = item_tuple_revIter.next() - new_item_tuple.append(tuple((1.-dummyTuple[0],dummyTuple[2],dummyTuple[1]))) - my_newdict[items] = tuple(new_item_tuple) - - return LinearSegmentedColormap('my_newcolormap',my_newdict,256) - -#################################################################### -### Inserts black segment into colourmap up to splitVal, ### -### compressing rest of colourmap towards high end. Colours ### -### in colourmap are preserved. ### -#################################################################### - -def cmap_preTruncate(my_cmap,splitVal): - my_newdict = {} - for items in my_cmap._segmentdata : - item_tuple = my_cmap._segmentdata[items] - new_item_tuple = [] - new_item_tuple.append(tuple((0.0,0.0,0.0))) - new_item_tuple.append(tuple((splitVal,0.0,item_tuple[0][2]))) - - item_tuple_Iter = iter(item_tuple) - item_tuple_Iter.next() - - while 1 : - try : - dummyTuple = item_tuple_Iter.next() - newVal = splitVal + dummyTuple[0]*(1.0 - splitVal) - new_item_tuple.append(tuple((newVal,dummyTuple[2],dummyTuple[1]))) - except StopIteration : - break - - my_newdict[items] = tuple(new_item_tuple) - - return LinearSegmentedColormap('my_newcolormap',my_newdict,256) - -#################################################################### -### Replaces colourmap upto splitVal with black, and rescales ### -### colourmap so that the transition occurs at startVal. ### -### Colours in original colourmap up to splitval are lost. ### -#################################################################### - -def cmap_preTruncate2(my_cmap,startVal,splitVal): - my_newdict = {} - for items in my_cmap._segmentdata : - item_tuple = my_cmap._segmentdata[items] - colourTuple = [] - colourTupleNew = [] - - # Create tuple containing tuples with colorbar coord > splitVal - item_tuple_Iter = iter(item_tuple) - while 1 : - try : - dummyTuple = item_tuple_Iter.next() - if (dummyTuple[0] > splitVal) : - colourTuple.append([dummyTuple[0],dummyTuple[1],dummyTuple[2]]) - except StopIteration : - break - - # Rescale colorbar coord to range [ startVal-->1.0 ] - oldRange = colourTuple[-1][0] - colourTuple[0][0] - newRange = 1.0 - startVal - - for tup in np.arange(np.shape(colourTuple)[0]) : - newCbarCoord = newRange*(colourTuple[tup][0]-colourTuple[0][0])/oldRange + startVal - colourTupleNew.append([newCbarCoord,colourTuple[tup][1],colourTuple[tup][2]]) - - colourTupleNew[0][1] = 0.0 - - new_item_tuple = [] - new_item_tuple.append((0.0,0.0,0.0)) - colourTupleNew_iter = iter(colourTupleNew) - - while 1 : - try : - new_item_tuple.append(colourTupleNew_iter.next()) - except StopIteration : - break - - my_newdict[items] = tuple(new_item_tuple) - - return LinearSegmentedColormap('my_newcolormap',my_newdict,256) - diff --git a/griddingAndGranulation.c b/griddingAndGranulation.c index ee9cb43e1ccb79e5ba1374f3581e2ae202d8e340..ac1dd476919f18e8bd0a0b11d64765322d2d7bcf 100644 --- a/griddingAndGranulation.c +++ b/griddingAndGranulation.c @@ -1,8 +1,8 @@ -/* file_Date = $Date$ - * file_Revision = $Revision$ - * file_Author = $Author$ - * file_HeadURL = $HeadURL$ - * file_Id = $Id$ +/* file_Date = $Date: 2014-09-08 17:09:53 -0500 (Mon, 08 Sep 2014) $ + * file_Revision = $Revision: 2226 $ + * file_Author = $Author: geoffc $ + * file_HeadURL = $HeadURL: https://svn.ssec.wisc.edu/repos/jpss_adl/trunk/scripts/edr/griddingAndGranulation.c $ + * file_Id = $Id: griddingAndGranulation.c 2226 2014-09-08 22:09:53Z geoffc $ * * ________________________ * griddingAndGranulation.c @@ -385,7 +385,7 @@ int gran2grid_moments(double *lat, /* * _________ - * grid2gran + * grid2gran_nearest * _________ * * This routine takes the regular grid gridData[nGridRows*nGridCols], @@ -395,8 +395,8 @@ int gran2grid_moments(double *lat, * * Inputs: * - * gridLat : Non-regular latitude grid (nGridRows*nGridCols) - * gridLon : Non-regular longitude grid (nGridRows*nGridCols) + * gridLat : Regular latitude grid (nGridRows*nGridCols) + * gridLon : Regular longitude grid (nGridRows*nGridCols) * gridData : Data array on grid defined by gridLat and gridLon * grid (nGridRows*nGridCols) * lat : Non-regular latitude grid (nData) @@ -413,17 +413,17 @@ int gran2grid_moments(double *lat, * * */ -int grid2gran(double *lat, - double *lon, - double *data, - long nData, - double *gridLat, - double *gridLon, - double *gridData, - long *gridDataIdx, - int nGridRows, - int nGridCols - ) +int grid2gran_nearest(double *lat, + double *lon, + double *data, + long nData, + double *gridLat, + double *gridLon, + double *gridData, + long *gridDataIdx, + int nGridRows, + int nGridCols + ) { long idx; @@ -439,30 +439,30 @@ int grid2gran(double *lat, int crnrPt,snapCrnrPt; int rowInBounds,colInBounds; - printf("Shape of data is (%ld)\n",nData); - printf("Shape of gridData is (%d, %d)\n", nGridRows,nGridCols); - printf("TRUE = %d\n", TRUE); - printf("FALSE = %d\n", FALSE); + /*printf("Shape of data is (%ld)\n",nData);*/ + /*printf("Shape of gridData is (%d, %d)\n", nGridRows,nGridCols);*/ + /*printf("TRUE = %d\n", TRUE);*/ + /*printf("FALSE = %d\n", FALSE);*/ int numShow = 10; int i; - printf("\nDisplaying lat,lon,data...\n"); - for (i=0;i<numShow;i++){ - printf("%6.1f %6.1f %6.1f\n",lat[i],lon[i],data[i]); - } + /*printf("\nDisplaying lat,lon,data...\n");*/ + /*for (i=0;i<numShow;i++){*/ + /*printf("%6.1f %6.1f %6.1f\n",lat[i],lon[i],data[i]);*/ + /*}*/ // Determine the lat and lon grid spacings - printf("nGridRows = %d\n", nGridRows); - printf("nGridCols = %d\n", nGridCols); - printf("gridLat[nGridCols] = %f\n", gridLat[nGridCols]); - printf("gridLat[0] = %f\n", gridLat[0]); - printf("gridLon[1] = %f\n", gridLon[1]); - printf("gridLon[0] = %f\n", gridLon[0]); + /*printf("nGridRows = %d\n", nGridRows);*/ + /*printf("nGridCols = %d\n", nGridCols);*/ + /*printf("gridLat[nGridCols] = %f\n", gridLat[nGridCols]);*/ + /*printf("gridLat[0] = %f\n", gridLat[0]);*/ + /*printf("gridLon[1] = %f\n", gridLon[1]);*/ + /*printf("gridLon[0] = %f\n", gridLon[0]);*/ gridLatInc = fabs(gridLat[nGridCols]-gridLat[0]); gridLonInc = fabs(gridLon[1]-gridLon[0]); - printf("gridLatInc,gridLonInc = (%6.1f %6.1f)\n",gridLatInc,gridLonInc); + /*printf("gridLatInc,gridLonInc = (%8.3f %8.3f)\n",gridLatInc,gridLonInc);*/ // Loop through non-gridded data points, find matching gridpoint, and assign // gridpoint data value to that non-gridded data point. @@ -553,3 +553,341 @@ int grid2gran(double *lat, return(0); } + + +/* + * _________ + * grid2gran_weightedAvg + * _________ + * + * This routine takes the regular grid gridData[nGridRows*nGridCols], + * defined by gridLat[nGridRows*nGridCols] and gridLon[nGridRows*nGridRows], + * and regrids to the data[nData] array, defined on the non-regular grid + * defined by lat[ndata] and lon[nData]. + * + * Inputs: + * + * gridLat : Non-regular latitude grid (nGridRows*nGridCols) + * gridLon : Non-regular longitude grid (nGridRows*nGridCols) + * gridData : Data array on grid defined by gridLat and gridLon + * grid (nGridRows*nGridCols) + * lat : Non-regular latitude grid (nData) + * lon : Non-regular longitude grid (nData) + * nData : Length of non-regular grid arrays + * nGridRows : Number of grid rows of the regular grid + * nGridCols : Number of grid columns of the regular grid + * + * Outputs: + * + * data : Data array on non-regular grid defined by lat and lon (nData) + * gridDataIdx : Array of indices into gridData, defined by lat and lon + * non-regular grid (nData) + * + * + */ +int grid2gran_weightedAvg(double *lat, + double *lon, + double *data, + long nData, + double *gridLat, + double *gridLon, + double *gridData, + long *gridDataIdx, + int nGridRows, + int nGridCols + ) +{ + + long idx; + int latGridIdxLo, latGridIdxHi, lonGridIdxLo, lonGridIdxHi; + + double latVal,lonVal,dataVal; + double gridLatInc,gridLonInc; + + int gridLatPt,gridLonPt; + int gridLatPoints[4], gridLonPoints[4]; + double weight, weights[4], weightSum, weightedSum, eps; + double wAvgData; + double minDist,dist,latDist,lonDist; + double gridLatVal,gridLonVal; + int crnrPt,snapCrnrPt; + int rowInBounds,colInBounds; + + /*printf("Shape of data is (%ld)\n",nData);*/ + /*printf("Shape of gridData is (%d, %d)\n", nGridRows,nGridCols);*/ + /*printf("TRUE = %d\n", TRUE);*/ + /*printf("FALSE = %d\n", FALSE);*/ + + int numShow = 10; + int i; + /*printf("\nDisplaying lat,lon,data...\n");*/ + /*for (i=0;i<numShow;i++){*/ + /*printf("%6.1f %6.1f %6.1f\n",lat[i],lon[i],data[i]);*/ + /*}*/ + + // Determine the lat and lon grid spacings + /*printf("nGridRows = %d\n", nGridRows);*/ + /*printf("nGridCols = %d\n", nGridCols);*/ + /*printf("gridLat[nGridCols] = %f\n", gridLat[nGridCols]);*/ + /*printf("gridLat[0] = %f\n", gridLat[0]);*/ + /*printf("gridLon[1] = %f\n", gridLon[1]);*/ + /*printf("gridLon[0] = %f\n", gridLon[0]);*/ + + gridLatInc = fabs(gridLat[nGridCols]-gridLat[0]); + gridLonInc = fabs(gridLon[1]-gridLon[0]); + + /*printf("gridLatInc,gridLonInc = (%6.1f %6.1f)\n",gridLatInc,gridLonInc);*/ + + // Loop through non-gridded data points, find matching gridpoint, and assign + // gridpoint data value to that non-gridded data point. + + eps = 1.0e-12; + + for (idx=0;idx<nData;idx++){ + /*for (idx=0;idx<numShow;idx++){*/ + latVal = lat[idx]; + lonVal = lon[idx]; + + // Determine lat/lon grid indices which bound the non-gridded point + latGridIdxLo = (int) floor((latVal-gridLat[0])/gridLatInc); + latGridIdxHi = latGridIdxLo + 1; + lonGridIdxLo = (int) floor((lonVal-gridLon[0])/gridLonInc); + lonGridIdxHi = lonGridIdxLo + 1; + + rowInBounds = TRUE; + colInBounds = TRUE; + + // If the grid indices bounding the non-gridded point are off the + // grid, mark this non-gridded point as out-of-bounds. + if ((latGridIdxLo<0) || (latGridIdxHi>=nGridRows)){ + rowInBounds = FALSE; + /*printf("Row idx out of bounds...\n");*/ + } + if ((lonGridIdxLo<0) || (lonGridIdxHi>=nGridCols)){ + colInBounds = FALSE; + /*printf("Column idx out of bounds...\n");*/ + } + + if (rowInBounds==FALSE){ + continue; + }else if (colInBounds==FALSE){ + continue; + }else{ + gridLatPoints[0] = latGridIdxLo; + gridLatPoints[1] = latGridIdxLo; + gridLatPoints[2] = latGridIdxHi; + gridLatPoints[3] = latGridIdxHi; + + gridLonPoints[0] = lonGridIdxLo; + gridLonPoints[1] = lonGridIdxHi; + gridLonPoints[2] = lonGridIdxLo; + gridLonPoints[3] = lonGridIdxHi; + + minDist = 1.e+30; + snapCrnrPt = 0; + + weightSum = 0.; + weightedSum = 0.; + wAvgData = 0.; + + // Loop through the corners bounding the non-gridded point, + // to calculate the inverse-distance weights. + for (crnrPt=0;crnrPt<4;crnrPt++){ + + gridLatPt = (int) gridLatPoints[crnrPt]; + gridLonPt = (int) gridLonPoints[crnrPt]; + + // Get the lat and lon values at this corner + gridLatVal = gridLat[nGridCols * gridLatPt + gridLonPt]; + gridLonVal = gridLon[nGridCols * gridLatPt + gridLonPt]; + + // The Pythagorean distance of this corner from the non-gridded + // data point... + latDist = latVal-gridLatVal; + lonDist = lonVal-gridLonVal; + dist = sqrt(latDist*latDist + lonDist*lonDist); + + weight = 1./(dist + eps); + + weightedSum += weight * gridData[nGridCols * gridLatPt + gridLonPt]; + + weightSum += weight; + } + + // Assign the value of the grid point closest to the non-gridded data + // point to that non-gridded data point . + data[idx] = weightedSum / weightSum; + + // Save the index of the closest grid data point to the non-gridded + // data point. + gridDataIdx[idx] = nGridCols * gridLatPt + gridLonPt; + + } + } + + return(0); +} + + +/* + * _________ + * grid2gran_bilinearInterp + * _________ + * + * This routine takes the regular grid gridData[nGridRows*nGridCols], + * defined by gridLat[nGridRows*nGridCols] and gridLon[nGridRows*nGridRows], + * and regrids to the data[nData] array, defined on the non-regular grid + * defined by lat[ndata] and lon[nData]. + * + * Inputs: + * + * gridLat : Non-regular latitude grid (nGridRows*nGridCols) + * gridLon : Non-regular longitude grid (nGridRows*nGridCols) + * gridData : Data array on grid defined by gridLat and gridLon + * grid (nGridRows*nGridCols) + * lat : Non-regular latitude grid (nData) + * lon : Non-regular longitude grid (nData) + * nData : Length of non-regular grid arrays + * nGridRows : Number of grid rows of the regular grid + * nGridCols : Number of grid columns of the regular grid + * + * Outputs: + * + * data : Data array on non-regular grid defined by lat and lon (nData) + * gridDataIdx : Array of indices into gridData, defined by lat and lon + * non-regular grid (nData) + * + * + * The notation below is based on the description at + * http://en.wikipedia.org/wiki/Bilinear_interpolation + * + * | | | + * |Q_12 |R_2 |Q_22 + * lat_hi ------o----------o----------o----- \ \ + * | | | | | + * | | | |-- dlat_plus | + * | | | | | + * latVal --|--------- P ---------|----- / |-- dlat + * | | | \ | + * | | | | | + * | | | |-- dlat_plus | + * |Q_11 |R_1 |Q_21 | | + * lat_lo ------o----------o----------o----- / / + * | | | + * | | | + * | lonVal | + * lon_lo lon_hi + * + * \__________/\_________/ + * | | + * dlon_minus dlon_plus + * + * \_____________________/ + * | + * dlon + * + * + */ +int grid2gran_bilinearInterp(double *lat, + double *lon, + double *data, + long nData, + double *gridLat, + double *gridLon, + double *gridData, + long *gridDataIdx, + int nGridRows, + int nGridCols + ) +{ + + long idx; + int latGridIdxLo, latGridIdxHi, lonGridIdxLo, lonGridIdxHi; + + double latVal,lonVal,dataVal; + double gridLatInc,gridLonInc; + double lat_lo, lat_hi, dlat, dlat_minus, dlat_plus; + double lon_lo, lon_hi, dlon, dlon_minus, dlon_plus; + double Q_11, Q_12, Q_21, Q_22; + double f_R1, f_R2, f_P; + + int gridLatPt,gridLonPt; + double eps; + double gridLatVal,gridLonVal; + int rowInBounds,colInBounds; + + int numShow = 10; + int i; + + // Calculate the grid lat and lon increments + gridLatInc = fabs(gridLat[nGridCols]-gridLat[0]); + gridLonInc = fabs(gridLon[1]-gridLon[0]); + + // Loop through non-gridded data points, find the bounding gridpoints, + // bilinearly interpolate. + + eps = 1.0e-12; + + for (idx=0;idx<nData;idx++){ + + latVal = lat[idx]; + lonVal = lon[idx]; + + // Determine lat/lon grid indices which bound the non-gridded point + latGridIdxLo = (int) floor((latVal-gridLat[0])/gridLatInc); + latGridIdxHi = latGridIdxLo + 1; + lonGridIdxLo = (int) floor((lonVal-gridLon[0])/gridLonInc); + lonGridIdxHi = lonGridIdxLo + 1; + + rowInBounds = TRUE; + colInBounds = TRUE; + + // If the grid indices bounding the non-gridded point are off the + // grid, mark this non-gridded point as out-of-bounds. + if ((latGridIdxLo<0) || (latGridIdxHi>=nGridRows)){ + rowInBounds = FALSE; + } + if ((lonGridIdxLo<0) || (lonGridIdxHi>=nGridCols)){ + colInBounds = FALSE; + } + + if (rowInBounds==FALSE){ + continue; + }else if (colInBounds==FALSE){ + continue; + }else{ + + lat_lo = gridLat[nGridCols * latGridIdxLo + lonGridIdxLo]; + lat_hi = gridLat[nGridCols * latGridIdxHi + lonGridIdxLo]; + dlat_minus = latVal - lat_lo; + dlat_plus = lat_hi - latVal; + dlat = lat_hi - lat_lo; + + lon_lo = gridLon[nGridCols * latGridIdxLo + lonGridIdxLo]; + lon_hi = gridLon[nGridCols * latGridIdxLo + lonGridIdxHi]; + dlon_minus = lonVal - lon_lo; + dlon_plus = lon_hi - lonVal; + dlon = lon_hi - lon_lo; + + Q_11 = gridData[nGridCols * latGridIdxLo + lonGridIdxLo]; + Q_12 = gridData[nGridCols * latGridIdxHi + lonGridIdxLo]; + Q_21 = gridData[nGridCols * latGridIdxLo + lonGridIdxHi]; + Q_22 = gridData[nGridCols * latGridIdxHi + lonGridIdxHi]; + + f_R1 = (dlon_plus * Q_11 + dlon_minus * Q_21) / dlon; + f_R2 = (dlon_plus * Q_12 + dlon_minus * Q_22) / dlon; + f_P = (dlat_plus * f_R1 + dlat_minus * f_R2) / dlat; + + // Assign the value of the grid point closest to the non-gridded data + // point to that non-gridded data point . + data[idx] = f_P; + + // Save the index of the closest grid data point to the non-gridded + // data point. + gridDataIdx[idx] = nGridCols * gridLatPt + gridLonPt; + + } + } + + return(0); +} diff --git a/netCDF2bin_imager.py b/netCDF2bin_imager.py deleted file mode 100644 index 2e0850563212421e3f16e81f03f4416973c42e9f..0000000000000000000000000000000000000000 --- a/netCDF2bin_imager.py +++ /dev/null @@ -1,568 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 -""" -netCDF2bin_imager.py - -Purpose: Read band arrays from a netcdf4/cf created by pytroll/mpop, and write them to flat -binary files. - -Input: - * netCDF file containing band arrays - -Output: - * flat binary files for each band - -Details: - * - -Preconditions: - * netCDF4 python module - -Optional: - * - -Minimum commandline: - - python netCDF2bin_imager.py --input_files=INPUTFILES - -where... - - INPUTFILES: The fully qualified path to the input files. May be a directory or a file glob. - - -Created by Geoff Cureton <geoff.cureton@ssec.wisc.edu> on 2014-03-25. -Copyright (c) 2014 University of Wisconsin Regents. All rights reserved. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -""" - -file_Date = '$Date: 2014-01-23 09:02:22 -0600 (Thu, 23 Jan 2014) $' -file_Revision = '$Revision: 1885 $' -file_Author = '$Author: kathys $' -file_HeadURL = '$HeadURL: https://svn.ssec.wisc.edu/repos/jpss_adl/trunk/scripts/edr/adl_viirs_edr.py $' -file_Id = '$Id: adl_viirs_edr.py 1885 2014-01-23 15:02:22Z kathys $' - -__author__ = 'Geoff Cureton <geoff.cureton@ssec.wisc.edu>' -__version__ = '$Id: adl_viirs_edr.py 1885 2014-01-23 15:02:22Z kathys $' -__docformat__ = 'Epytext' - -import os, sys, logging, traceback -from os import path,uname,environ -import string -import re -import uuid -from shutil import rmtree,copyfile -from glob import glob -from time import time -from datetime import datetime,timedelta - -import numpy as np -from numpy import ma -import copy - -import matplotlib -import matplotlib.cm as cm -from matplotlib.colors import ListedColormap -from matplotlib.figure import Figure - -matplotlib.use('Agg') -from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas - -# This must come *after* the backend is specified. -import matplotlib.pyplot as ppl - -from mpl_toolkits.basemap import Basemap - -from netCDF4 import Dataset -from netCDF4 import num2date - - -def listData(file_attrs,file_dicts): - ''' - List various file and data attributes - ''' - - print "\n### File Attributes ###\n" - for keys in file_attrs.keys(): - print "{} : {}".format(keys,file_attrs[keys]) - - print "\n### File Dimensions ###\n" - print "Keys = {}".format(file_dicts['dimensions'].keys()) - for keys in file_dicts['dimensions'].keys(): - print "{} : {}\n".format(keys,file_dicts['dimensions'][keys]) - - print "\n### File Variables ###\n" - var_attrs = ['_FillValue','scale_factor','add_offset','coordinates','long_name','resolution','units','ndim','size','dtype','shape','dimensions'] - print "Keys = {}".format(file_dicts['variables'].keys()) - for var_key in file_dicts['variables'].keys(): - print "\nvar = {}".format(var_key) - var = file_dicts['variables'][var_key] - for attr in var_attrs: - try: - var_attr = getattr(var,attr) - print "{}.{} = {}".format(var_key,attr,var_attr) - except: - pass - - - -def plotData(data,data_mask,units,pngName,stride=1,dpi=200): - ''' - Plot the input dataset in swath projection - ''' - - # General Setup - dataShape = data.shape - aspectRatio = float(dataShape[0])/float(dataShape[1]) - - figWidth,figHeight = 4.,aspectRatio*4. - ax_rect = [0.05, 0.15, 0.90, 0.80 ] # [left,bottom,width,height] - - fig = Figure(figsize=(figWidth,figHeight)) - canvas = FigureCanvas(fig) - - ax = fig.add_axes(ax_rect) - - data = ma.array(data,mask=data_mask) - - im = ax.imshow(data,interpolation='nearest') - - if units == '%': - txt = ax.set_title('Reflectance') - elif units == 'K': - txt = ax.set_title('Brightness Temperature') - else: - txt = ax.set_title('scaled data') - - - ppl.setp(ax.get_xticklines(),visible=False) - ppl.setp(ax.get_yticklines(),visible=False) - ppl.setp(ax.get_xticklabels(), visible=False) - ppl.setp(ax.get_yticklabels(), visible=False) - - ax.set_aspect('equal') - - cax_rect = [0.05 , 0.05, 0.90 , 0.05 ] # [left,bottom,width,height] - cax = fig.add_axes(cax_rect,frameon=False) # setup colorbar axes - cb = fig.colorbar(im, cax=cax, orientation='horizontal') - - if units == '%': - txt = cax.set_title('Reflectance (%)') - elif units == 'K': - txt = cax.set_title('Brightness Temperature (K)') - else: - txt = cax.set_title('scaled units') - - # Redraw the figure - canvas.draw() - canvas.print_figure(pngName,dpi=dpi) - - -def plotMapData(lat,lon,data,data_mask,units,pngName,stride=1,pointSize=1.,scatterPlot=False, \ - lat_0=None,lon_0=None,latMin=None,lonMin=None,latMax=None,lonMax=None,dpi=200): - ''' - Plot the input dataset in mapped to particular projection - ''' - - # General Setup - figWidth,figHeight = 8.,8. - ax_rect = [0.05, 0.15, 0.90, 0.80 ] # [left,bottom,width,height] - - fig = Figure(figsize=(figWidth,figHeight)) - canvas = FigureCanvas(fig) - - ax = fig.add_axes(ax_rect) - - lat_0,lon_0 = 0.,0. - - #print "lonMin = {} degrees".format(lonMin) - #print "lonMax = {} degrees".format(lonMax) - #print "latMin = {} degrees".format(latMin) - #print "latMax = {} degrees".format(latMax) - - m = Basemap(projection='cyl',lon_0=lon_0,lat_0=lat_0,lat_ts=lat_0,ax=ax, - llcrnrlon=lonMin, - llcrnrlat=latMin, - urcrnrlon=lonMax, - urcrnrlat=latMax - ) - - x,y=m(lon[::stride,::stride],lat[::stride,::stride]) - - m.drawcoastlines() - #m.drawstates() - m.drawcountries() - m.fillcontinents(color='0.85',zorder=0) - m.drawparallels(np.arange( -90, 91,30), color = '0.25', linewidth = 0.5,labels=[1,0,1,0]) - m.drawmeridians(np.arange(-180,180,30), color = '0.25', linewidth = 0.5,labels=[1,0,1,0]) - - data = ma.array(data[::stride,::stride],mask=data_mask[::stride,::stride]) - - if scatterPlot: - cs = m.scatter(x,y,s=pointSize,c=data,axes=ax,edgecolors='none') - else: - cs = m.pcolor(x,y,data,axes=ax,edgecolors='none',antialiased=False) - - if units == '%': - txt = ax.set_title('Reflectance') - elif units == 'K': - txt = ax.set_title('Brightness Temperature') - else: - txt = ax.set_title('scaled data') - - ppl.setp(ax.get_xticklines(),visible=False) - ppl.setp(ax.get_yticklines(),visible=False) - ppl.setp(ax.get_xticklabels(), visible=False) - ppl.setp(ax.get_yticklabels(), visible=False) - - #ax.set_aspect('equal') - - cax_rect = [0.05 , 0.05, 0.90 , 0.05 ] # [left,bottom,width,height] - cax = fig.add_axes(cax_rect,frameon=False) # setup colorbar axes - #cb = fig.colorbar(im, cax=cax, orientation='horizontal') - cb = fig.colorbar(cs, cax=cax, orientation='horizontal') - - if units == '%': - txt = cax.set_title('Reflectance (%)') - elif units == 'K': - txt = cax.set_title('Brightness Temperature (K)') - else: - txt = cax.set_title('scaled units') - - # Redraw the figure - canvas.draw() - canvas.print_figure(pngName,dpi=dpi) - - -class dataset(): - ''' - Define a "dataset" datatpe, which contains useful attributes for a particular - dataset. - ''' - def __init__(self): - self.bandNum - self.units = None - self.dpi = 200 - self.arrayName = None - self.mapRes = None - - -def _argparse(): - ''' - Method to encapsulate the option parsing and various setup tasks. - ''' - - import argparse - - endianChoices = ['little','big'] - - dataChoices=['REF1','REF2','REF3','BT1','BT2','REF','BT','ALL'] - - mapRes = ['c','l','i'] - - defaults = { - 'input_file':None, - 'dataset':'ALL', - 'stride':1, - 'unscaled':False, - 'scatter_plot':False, - 'dpi':200, - 'list_attrs':False, - 'pointSize':1, - 'make_plots':False, - 'map_plot':False, - 'no_binaries':False - } - - description = '''Read band arrays from a netcdf4/cf created by pytroll/mpop, and write them to flat -binary files.''' - usage = "usage: %prog [mandatory args] [options]" - version = __version__ - - parser = argparse.ArgumentParser() - - # Mandatory arguments - - parser.add_argument('-i','--input_file', - action='store', - dest='input_file', - type=str, - required=True, - help="The fully qualified path to the input file." - ) - - # Optional arguments - - parser.add_argument('--dset', - action="store", - dest="dataset", - default=defaults["dataset"], - type=str, - choices=dataChoices, - help='''The imager dataset(s) toVIIRS algorithm to run.\n\n - Possible values are... - {}. - '''.format(dataChoices.__str__()[1:-1]) - ) - - parser.add_argument('--unscaled', - action="store_true", - dest="doUnscale", - default=defaults["unscaled"], - help="Attempt to unscale the band arrays before output to flat binary files. [default: {}]".format(defaults["unscaled"]) - ) - - parser.add_argument('--make_plots', - action="store_true", - dest="doPlots", - default=defaults["make_plots"], - help="Plot the band arrays to png files. [default: {}]".format(defaults["make_plots"]) - ) - - parser.add_argument('--map_plot', - action="store_true", - dest="doMapPlots", - default=defaults["map_plot"], - help="Plot the band arrays to png files, using a map projection (has no effect if --make_plots is not set). [default: {}]".format(defaults["map_plot"]) - ) - - parser.add_argument('--no_binaries', - action="store_true", - dest="noBinaries", - default=defaults["no_binaries"], - help="Plot the band arrays to png files. [default: {}]".format(defaults["make_plots"]) - ) - - parser.add_argument('--plotMin', - action="store", - dest="plotMin", - type=float, - help="Minimum value to plot." - ) - - parser.add_argument('--plotMax', - action="store", - dest="plotMax", - type=float, - help="Maximum value to plot." - ) - - parser.add_argument('-d','--dpi', - action="store", - dest="dpi", - default='200.', - type=float, - help="The resolution in dots per inch of the output png file. [default: {}]".format(defaults["dpi"]) - ) - - parser.add_argument('-S','--stride', - action="store", - dest="stride", - default=defaults["stride"], - type=int, - help="Sample every STRIDE pixels in the band data. [default: {}]".format(defaults["stride"]) - ) - - parser.add_argument('--scatter_plot', - action="store_true", - dest="doScatterPlot", - default=defaults["scatter_plot"], - help="Generate the plot using a scatterplot approach." - ) - - parser.add_argument('--list_attrs', - action="store_true", - dest="doListAttrs", - default=defaults["list_attrs"], - help="List some file and dataset attributes." - ) - - parser.add_argument('-P','--pointSize', - action="store", - dest="pointSize", - default=defaults["pointSize"], - type=float, - help="Size of the plot point used to represent each pixel. [default: {}]".format(defaults["pointSize"]) - ) - - parser.add_argument('--lat_0', - action="store", - dest="lat_0", - type=float, - help="Center latitude of plot." - ) - - parser.add_argument('--lon_0', - action="store", - dest="lon_0", - type=float, - help="Center longitude of plot." - ) - - parser.add_argument('--latMin', - action="store", - dest="latMin", - type=float, - help="Minimum latitude to plot." - ) - - parser.add_argument('--latMax', - action="store", - dest="latMax", - type=float, - help="Maximum latitude to plot." - ) - - parser.add_argument('--lonMin', - action="store", - dest="lonMin", - type=float, - help="Minimum longitude to plot." - ) - - parser.add_argument('--lonMax', - action="store", - dest="lonMax", - type=float, - help="Maximum longitude to plot." - ) - - args = parser.parse_args() - - return args - -def main(): - - options = _argparse() - - stride = options.stride - lat_0 = options.lat_0 - lon_0 = options.lon_0 - latMin = options.latMin - lonMin = options.lonMin - latMax = options.latMax - lonMax = options.lonMax - doScatterPlot = options.doScatterPlot - pointSize = options.pointSize - dpi = options.dpi - - # Create the dataset dictionaries - #dataDict = {} - #if options.dataset == "REF": - - #if options.dataset == "BT": - #if options.dataset == "ALL": - - dataDict = { - 'REF1', - 'REF2', - 'REF3', - 'BT1', - 'BT2', - 'REF', - 'BT', - 'ALL' - } - - # Open the file object - print "Opening {} ...".format(options.input_file) - fileObj = Dataset(options.input_file) - - file_attrs = {} - file_attrs['history'] = fileObj.history - file_attrs['orbit'] = fileObj.orbit - file_attrs['platform'] = fileObj.platform - - file_dicts = {} - file_dicts['dimensions'] = fileObj.dimensions - file_dicts['variables'] = fileObj.variables - - # List various file and data attributes - if options.doListAttrs: - listData(file_attrs,file_dicts) - - if not (options.noBinaries and not options.doPlots): - - print "We are reading the file Datasets" - - # Output the lats and lons - - lats = file_dicts['variables']['lat0'][:,:].astype(np.float32) - lons = file_dicts['variables']['lon0'][:,:].astype(np.float32) - - if not options.noBinaries: - geo_fileName = string.replace(options.input_file,'.nc','_latitude.bin') - print "Writing latitude to {}...".format(geo_fileName) - lats.tofile(geo_fileName) - - geo_fileName = string.replace(options.input_file,'.nc','_longitude.bin') - print "Writing longitude to {}...".format(geo_fileName) - lons.tofile(geo_fileName) - - # Output the bands - - for band_var in ['Image0','Image1']: - #for band_var in ['Image0']: - nBands = file_dicts['variables'][band_var].shape[2] - dtype = file_dicts['variables'][band_var].dtype.name - units = '' - - for band in range(nBands): - #for band in [0]: - var = file_dicts['variables'][band_var][:,:,band] - fillValue = file_dicts['variables'][band_var]._FillValue - print "fill value is {}".format(fillValue) - if band_var == 'Image1': - var_mask = ma.masked_inside(var,32720,32768).mask - else: - var_mask = ma.masked_equal(var,fillValue).mask - - scale_factor = file_dicts['variables'][band_var].scale_factor[band] - offset = file_dicts['variables'][band_var].add_offset[band] - print "{} band {} (scale,offset) = ({},{})".format(band_var,band,scale_factor,offset) - - if options.doUnscale : - dtype = 'float32' - units = file_dicts['variables'][band_var].units - var = var * scale_factor + offset - var = ma.array(var,mask=var_mask,fill_value=-999.9).filled() - - print "{} band {} fill value = {}".format(band_var,band,np.min(var)) - print "{} band {} min value = {}".format(band_var,band,np.min(ma.array(var,mask=var_mask))) - print "{} band {} max value = {}".format(band_var,band,np.max(ma.array(var,mask=var_mask))) - - bin_fileName = string.replace(options.input_file,'.nc','') - bin_fileName = "{}_{}_band{}_{}.bin".format(bin_fileName,band_var,band,dtype) - - if not options.noBinaries: - print "Writing {} band {} to {}...".format(band_var,band,bin_fileName) - var.astype(dtype).tofile(bin_fileName) - - if options.doPlots: - png_fileName = string.replace(bin_fileName,'_{}.bin'.format(dtype),'.png') - print "Plotting {} band {} to {}...".format(band_var,band,png_fileName) - if options.doMapPlots: - plotMapData(lats,lons,var,var_mask,units,png_fileName,stride=stride,lat_0=lat_0,lon_0=lon_0,latMin=latMin,lonMin=lonMin,latMax=latMax,lonMax=lonMax,scatterPlot=doScatterPlot,pointSize=pointSize,dpi=dpi) - else: - plotData(var,var_mask,units,png_fileName,stride=stride,dpi=dpi) - - - print "Closing {} ...".format(options.input_file) - fileObj.close() - - sys.exit(0) - - -if __name__=='__main__': - sys.exit(main()) diff --git a/resampleTools.py b/resampleTools.py deleted file mode 100644 index cdeb81bc7654c43757bee9165a2fe3bc30f4de21..0000000000000000000000000000000000000000 --- a/resampleTools.py +++ /dev/null @@ -1,106 +0,0 @@ -import numpy as np -import scipy.interpolate -import scipy.ndimage - -def congrid(a, newdims, method='linear', centre=False, minusone=False): - '''Arbitrary resampling of source array to new dimension sizes. - Currently only supports maintaining the same number of dimensions. - To use 1-D arrays, first promote them to shape (x,1). - - Uses the same parameters and creates the same co-ordinate lookup points - as IDL''s congrid routine, which apparently originally came from a VAX/VMS - routine of the same name. - - method: - neighbour - closest value from original data - nearest and linear - uses n x 1-D interpolations using - scipy.interpolate.interp1d - (see Numerical Recipes for validity of use of n 1-D interpolations) - spline - uses ndimage.map_coordinates - - centre: - True - interpolation points are at the centres of the bins - False - points are at the front edge of the bin - - minusone: - For example- inarray.shape = (i,j) & new dimensions = (x,y) - False - inarray is resampled by factors of (i/x) * (j/y) - True - inarray is resampled by(i-1)/(x-1) * (j-1)/(y-1) - This prevents extrapolation one element beyond bounds of input array. - ''' - if not a.dtype in [np.float64, np.float32]: - a = np.cast[float](a) - - m1 = np.cast[int](minusone) - ofs = np.cast[int](centre) * 0.5 - old = np.array( a.shape ) - ndims = len( a.shape ) - if len( newdims ) != ndims: - print "[congrid] dimensions error. " \ - "This routine currently only support " \ - "rebinning to the same number of dimensions." - return None - newdims = np.asarray( newdims, dtype=float ) - dimlist = [] - - if method == 'neighbour': - for i in range( ndims ): - base = np.indices(newdims)[i] - dimlist.append( (old[i] - m1) / (newdims[i] - m1) \ - * (base + ofs) - ofs ) - cd = np.array( dimlist ).round().astype(int) - newa = a[list( cd )] - return newa - - elif method in ['nearest','linear']: - # calculate new dims - for i in range( ndims ): - base = np.arange( newdims[i] ) - dimlist.append( (old[i] - m1) / (newdims[i] - m1) \ - * (base + ofs) - ofs ) - # specify old dims - olddims = [np.arange(i, dtype = np.float) for i in list( a.shape )] - - # first interpolation - for ndims = any - mint = scipy.interpolate.interp1d( olddims[-1], a, kind=method ) - newa = mint( dimlist[-1] ) - - trorder = [ndims - 1] + range( ndims - 1 ) - for i in range( ndims - 2, -1, -1 ): - newa = newa.transpose( trorder ) - - mint = scipy.interpolate.interp1d( olddims[i], newa, kind=method ) - newa = mint( dimlist[i] ) - - if ndims > 1 : - # need one more transpose to return to original dimensions - newa = newa.transpose( trorder ) - - return newa - elif method in ['spline']: - oslices = [ slice(0,j) for j in old ] - oldcoords = np.ogrid[oslices] - nslices = [ slice(0,j) for j in list(newdims) ] - newcoords = np.mgrid[nslices] - - newcoords_dims = range(np.rank(newcoords)) - #make first index last - newcoords_dims.append(newcoords_dims.pop(0)) - newcoords_tr = newcoords.transpose(newcoords_dims) - # makes a view that affects newcoords - - newcoords_tr += ofs - - deltas = (np.asarray(old) - m1) / (newdims - m1) - newcoords_tr *= deltas - - newcoords_tr -= ofs - - newa = scipy.ndimage.map_coordinates(a, newcoords) - return newa - else : - print "Congrid error: Unrecognized interpolation type.\n", \ - "Currently only \'neighbour\', \'nearest\',\'linear\',", \ - "and \'spline\' are supported." - return None - diff --git a/snapgrid.m b/snapgrid.m deleted file mode 100644 index b089158e3927b09853837d7f3b54193fd5187b6c..0000000000000000000000000000000000000000 --- a/snapgrid.m +++ /dev/null @@ -1,87 +0,0 @@ -function grid = snapgrid(spacing, geodata, param, first,grid) - -% input: -% spacing - lat/lon grid spacing, e.g. 0.25, 0.5, 1.0, etc. -% geodata.lat, geodata.lon, geodata.satzen, geodata.sunzen -% param - the parameter you want to snap to grid, 'rad'for radiance or -% 'cld' for cloud -% first - 1=first; 0=not -% grid - regrid out put from a previous run. -% type - 1=day; 0=night -% output: -% regrid - data structure with regridded fields -% -% Nadia Smith, April 2010 -% Variation on Nick Bearson's code -% ======================================================= - -% if this is the first granule/file, then create new grids -if first==1 - % day time - grid.d_dist = ones(180/spacing, 360/spacing)*NaN; - grid.d_satzen = ones(180/spacing, 360/spacing)*NaN; - grid.d_solzen = ones(180/spacing, 360/spacing)*NaN; - grid.d_param = ones(180/spacing, 360/spacing)*NaN; - grid.d_type = ones(180/spacing, 360/spacing)*NaN; - % night time - grid.n_dist = ones(180/spacing, 360/spacing)*NaN; - grid.n_satzen = ones(180/spacing, 360/spacing)*NaN; - grid.n_solzen = ones(180/spacing, 360/spacing)*NaN; - grid.n_param = ones(180/spacing, 360/spacing)*NaN; - grid.n_type = ones(180/spacing, 360/spacing)*NaN; -end - -for i=1:length(geodata.lat(:,1)) - for j=1:length(geodata.lat(1,:)) - - % get the lat/lon of the data point - lat = geodata.lat(i, j); - lon = geodata.lon(i, j); - - if isnan(lat) == 0 && isnan(lon) == 0 - - % adjust the lon/lat to be 0-360 and 0-180, and then - % adjust them to our spacing grid for easier calculation - adjlat = ((lat + 90) / spacing); - adjlon = ((lon + 180) / spacing); - - % now find the closest grid crossing - latbin = round(adjlat); - lonbin = round(adjlon); - - % We don't want to reference the 0 spot, so we'll use the 180/360 instead (lat/lon) - if latbin == 0 - latbin = 180 / spacing; - end - if lonbin == 0 - lonbin = 360 / spacing; - end - - % determine our point's distance from the grid crossing - % note that we're finding the hypotenuse distance, as that differs from just adding the two - dist = sqrt(((latbin - adjlat)^2) + ((lonbin - adjlon)^2)); - if geodata.sunzen(i, j) < 84 % day time grid - % now we're ready to get the current distance in that bin and see if ours is closer - if (dist < (1/spacing)/4) && (isnan(grid.d_satzen(latbin, lonbin)) == 1 ||... - abs(geodata.satzen(i, j)) < abs(grid.d_satzen(latbin, lonbin))) - grid.d_dist(latbin, lonbin) = dist; - grid.d_satzen(latbin, lonbin) = geodata.satzen(i, j); - grid.d_sunzen(latbin, lonbin) = geodata.sunzen(i, j); - grid.d_param(latbin, lonbin) = param(i, j); - end - else % night time grid - % now we're ready to get the current distance in that bin and see if ours is closer - if (dist < (1/spacing)/4) && (isnan(grid.n_satzen(latbin, lonbin)) == 1 ||... - abs(geodata.satzen(i, j)) < abs(grid.n_satzen(latbin, lonbin))) - grid.n_dist(latbin, lonbin) = dist; - grid.n_satzen(latbin, lonbin) = geodata.satzen(i, j); - grid.n_sunzen(latbin, lonbin) = geodata.sunzen(i, j); - grid.n_param(latbin, lonbin) = param(i, j); - end - end - end - end -end - - -