def test_get_time(self): n1 = Nansat(self.test_file_gcps, logLevel=40) t = n1.get_time() self.assertEqual(len(t), len(n1.bands())) self.assertEqual(type(t[0]), datetime.datetime)
class SARWind(Nansat, object): ''' A class for calculating wind speed from SAR images using CMOD ''' def __init__(self, sar_image, winddir=None, pixelsize=500): ''' Parameters ----------- sar_image : string or Nansat object SAR image filename (original, raw file) winddir : int, string, Nansat, None Auxiliary wind field information needed to calculate SAR wind (must be or have wind direction) ''' if isinstance(sar_image, str) or isinstance(sar_image, unicode): super(SARWind, self).__init__(sar_image) elif isinstance(sar_image, Nansat): super(SARWind, self).__init__(domain=sar_image) self.vrt = sar_image.vrt # Check that this is a SAR image with VV pol NRCS try: self.sigma0_bandNo = self._get_band_number( {'standard_name': 'surface_backwards_scattering_coefficient_of_radar_wave', 'polarization': 'VV'}) except: raise TypeError(self.fileName + ' does not have SAR NRCS in VV polarization') self.SAR_image_time = self.get_time( self.sigma0_bandNo).replace(tzinfo=None) if pixelsize != 'fullres': print 'Resizing SAR image to ' + str(pixelsize) + ' m pixel size' self.resize(pixelsize=pixelsize) self.winddir = winddir if winddir is not None: self.calculate_wind() def calculate_wind(self, winddir=None, storeModelSpeed=True): # Calculate wind speed from SAR sigma0 in VV polarization if winddir: self.winddir = winddir if self.winddir is None or self.winddir == 'online': self.winddir = 'ncep_wind_online' # default source if isinstance(self.winddir, int): # Constant wind direction is input print 'Using constant wind (from) direction: ' + str(self.winddir) + \ ' degrees clockwise from North' winddirArray = np.ones(self.shape())*self.winddir winddir_time = None storeModelSpeed = False # Not relevant if direction given as number else: # Nansat readable file if isinstance(self.winddir, str): try: self.winddir = Nansat(self.winddir) except: try: self.winddir = Nansat(self.winddir + datetime.strftime( self.SAR_image_time, ':%Y%m%d%H%M')) except: pass if not isinstance(self.winddir, Nansat): raise ValueError('Wind direction not available') winddir_time = self.winddir.get_time()[0] # Bi-linear interpolation onto SAR image self.winddir.reproject(self, eResampleAlg=1) # Check time difference between SAR image and wind direction object timediff = self.SAR_image_time - winddir_time hoursDiff = np.abs(timediff.total_seconds()/3600.) print 'Time difference between SAR image and wind direction: ' \ + '%.2f' % hoursDiff + ' hours' print 'SAR image time: ' + str(self.SAR_image_time) print 'Wind dir time: ' + str(winddir_time) if hoursDiff > 3: print '#########################################' print 'WARNING: time difference exceeds 3 hours!' print '#########################################' wind_u_bandNo = self.winddir._get_band_number({ 'standard_name': 'eastward_wind', }) wind_v_bandNo = self.winddir._get_band_number({ 'standard_name': 'northward_wind', }) # Get wind direction u_array = self.winddir[wind_u_bandNo] v_array = self.winddir[wind_v_bandNo] winddirArray = np.degrees( np.arctan2(-u_array, -v_array)) # 0 from North, 90 from East # Calculate SAR wind with CMOD # TODO: # - add other CMOD versions than CMOD5 print 'Calculating SAR wind with CMOD...' startTime = datetime.now() windspeed = cmod5n_inverse(self[self.sigma0_bandNo], np.mod(winddirArray - self['SAR_look_direction'], 360), self['incidence_angle']) print 'Calculation time: ' + str(datetime.now() - startTime) windspeed[np.where(np.isnan(windspeed))] = np.nan windspeed[np.where(np.isinf(windspeed))] = np.nan # Add wind speed and direction as bands # TODO: make it possible to update existing bands... See # https://github.com/nansencenter/nansat/issues/58 self.add_band(array=windspeed, parameters={ 'wkv': 'wind_speed', 'name': 'windspeed', 'time': self.get_time(self.sigma0_bandNo), 'winddir_time': winddir_time }) self.add_band(array=winddirArray, parameters={ 'wkv': 'wind_from_direction', 'name': 'winddirection', 'time': winddir_time }) if storeModelSpeed: self.add_band(array=self.winddir['windspeed'], parameters={ 'wkv': 'wind_speed', 'name': 'model_windspeed', 'time': winddir_time, }) # TODO: Replace U and V bands with pixelfunctions u = -windspeed*np.sin((180.0 - winddirArray)*np.pi/180.0) v = windspeed*np.cos((180.0 - winddirArray)*np.pi/180.0) self.add_band(array=u, parameters={ 'wkv': 'eastward_wind', }) self.add_band(array=v, parameters={ 'wkv': 'northward_wind', }) def _get_masked_windspeed(self, landmask=True, icemask=True): try: sar_windspeed = self['windspeed'] except: raise ValueError('SAR wind has not been calculated, ' \ 'execute calculate_wind(winddir) first.') sar_windspeed[sar_windspeed<0] = 0 palette = jet if landmask: try: # Land mask sar_windspeed = np.ma.masked_where( self.watermask()[1]==2, sar_windspeed) palette.set_bad([.3, .3, .3], 1.0) # Land is masked (bad) except: print 'Land mask not available' if icemask: try: # Ice mask try: # first try local file ice = Nansat('metno_local_hires_seaice_' + self.SAR_image_time.strftime('%Y%m%d'), mapperName='metno_local_hires_seaice') except: # otherwise Thredds ice = Nansat('metno_hires_seaice:' + self.SAR_image_time.strftime('%Y%m%d')) ice.reproject(self) iceBandNo = ice._get_band_number( {'standard_name': 'sea_ice_area_fraction'}) sar_windspeed[ice[iceBandNo]>0] = -1 palette.set_under('w', 1.0) # Ice is 'under' (-1) except: print 'Ice mask not available' return sar_windspeed, palette def write_geotiff(self, filename, landmask=True, icemask=True): sar_windspeed, palette = self._get_masked_windspeed(landmask, icemask) nansat_geotiff = Nansat(array=sar_windspeed, domain=self, parameters = {'name': 'masked_windspeed', 'minmax': '0 20'}) nansat_geotiff.write_geotiffimage(filename) def plot(self, filename=None, numVectorsX = 16, show=True, landmask=True, icemask=True, flip=True, maskWindAbove=35): ''' Basic plotting function showing CMOD wind speed overlaid vectors in SAR image projection''' try: sar_windspeed, palette = self._get_masked_windspeed(landmask, icemask) except: raise ValueError('SAR wind has not been calculated, ' \ 'execute calculate_wind(winddir) before plotting.') sar_windspeed[sar_windspeed>maskWindAbove] = np.nan winddirReductionFactor = np.round( self.vrt.dataset.RasterXSize/numVectorsX) # model_winddir is direction from which wind is blowing winddir_relative_up = 360 - self['winddirection'] + \ self.azimuth_up() indX = range(0, self.vrt.dataset.RasterXSize, winddirReductionFactor) indY = range(0, self.vrt.dataset.RasterYSize, winddirReductionFactor) X, Y = np.meshgrid(indX, indY) try: # scaling of wind vector length, if model wind is available model_windspeed = self['model_windspeed'] model_windspeed = model_windspeed[Y, X] except: model_windspeed = 8*np.ones(X.shape) Ux = np.sin(np.radians(winddir_relative_up[Y, X]))*model_windspeed Vx = np.cos(np.radians(winddir_relative_up[Y, X]))*model_windspeed # Make sure North is up, and east is right if flip == True: lon, lat = self.get_corners() if lat[0] < lat[1]: sar_windspeed = np.flipud(sar_windspeed) Ux = -np.flipud(Ux) Vx = -np.flipud(Vx) if lon[0] > lon[2]: sar_windspeed = np.fliplr(sar_windspeed) Ux = np.fliplr(Ux) Vx = np.fliplr(Vx) # Plotting figSize = sar_windspeed.shape legendPixels = 60.0 legendPadPixels = 5.0 legendFraction = legendPixels/figSize[0] legendPadFraction = legendPadPixels/figSize[0] dpi=100.0 fig = plt.figure() fig.set_size_inches((figSize[1]/dpi, (figSize[0]/dpi)* (1+legendFraction+legendPadFraction))) ax = fig.add_axes([0,0,1,1+legendFraction]) ax.set_axis_off() plt.imshow(sar_windspeed, cmap=palette, interpolation='nearest') plt.clim([0, 20]) cbar = plt.colorbar(orientation='horizontal', shrink=.80, aspect=40, fraction=legendFraction, pad=legendPadFraction) cbar.ax.set_ylabel('[m/s]', rotation=0) cbar.ax.yaxis.set_label_position('right') ax.quiver(X, Y, Ux, Vx, angles='xy', width=0.004, scale=200, scale_units='width', color=[.0, .0, .0], headaxislength=4) if filename is not None: fig.savefig(filename, pad_inches=0, dpi=dpi) if show: plt.show() return fig
# Open an input file # Create a Nansat object <n> for futher high-level operations n = Nansat(iFileName) # Open an input file, specify which Mapper to use, set logging level n = Nansat(iFileName, mapperName='generic', logLevel=10) # list bands and georeference of the object print 'Raw Nansat:', n, '\n' # get dictionary with metadata from all bands print 'Bands:', n.bands(), '\n' # get time of the image aquisition print 'Time:', n.get_time()[0], '\n' # set GlobalMetadata n.set_metadata(key='GlobalKey', value='GlobalVal') # get Global Metadata print 'Global Metadata:', n.get_metadata(), '\n' # set BandMetadata to the 1st band n.set_metadata(key='BandKey', value='BandVal', bandID=1) # get 1st Band Metadata print '1st Band Metadata:', n.get_metadata(bandID=1), '\n' # add a band from file (copy the 2nd band to the end (4th band) n.add_band(fileName=n.fileName, bandID=2) # add a band from numpy array (copy the 1st band to the end (5th band)) n.add_band(array=n[1], parameters={'name': 'Name1',
print 'Raw Nansat:', n # get dictionary with metadata from all bands print 'Bands:', n.bands() # get size of the object (Y and X dimensions, to follow Numpy style) print 'Shape:', n.shape() # get list with coordinates of the object corners print 'Corners:', n.get_corners() # get lists with coordinates of the object borders print 'Border:', n.get_border() # get time of the image aquisition print 'Time:', n.get_time()[0] # Get band data and do some operations # 1. Get data from 1st band as numpy array # 2. Plot the array (pyplot image is save to a PNG file) # 3. Save as Matlab file a = n[1] plt.imshow(a);plt.colorbar();plt.savefig(oFileName + '_imshow.png');plt.close() savemat(oFileName + '.mat', {'band_1': a}) # make simple indexed image from 1st band with default colormap n.write_figure(oFileName + '.png') # make RGB image from bands 1,2,3 with brightness correction n.write_figure(oFileName + '_rgb.png', bands=[1,2,3], clim='hist', ratio=0.9)
class SARWind(Nansat, object): ''' A class for calculating wind speed from SAR images using CMOD ''' def __init__(self, sar_image, winddir=None, pixelsize=500): ''' Parameters ----------- sar_image : string or Nansat object SAR image filename (original, raw file) winddir : int, string, Nansat, None Auxiliary wind field information needed to calculate SAR wind (must be or have wind direction) ''' if isinstance(sar_image, str) or isinstance(sar_image, unicode): super(SARWind, self).__init__(sar_image) elif isinstance(sar_image, Nansat): super(SARWind, self).__init__(domain=sar_image) self.vrt = sar_image.vrt # Check that this is a SAR image with VV pol NRCS try: self.sigma0_bandNo = self._get_band_number({ 'standard_name': 'surface_backwards_scattering_coefficient_of_radar_wave', 'polarization': 'VV' }) except: raise TypeError(self.fileName + ' does not have SAR NRCS in VV polarization') self.SAR_image_time = self.get_time( self.sigma0_bandNo).replace(tzinfo=None) if pixelsize != 'fullres': print 'Resizing SAR image to ' + str(pixelsize) + ' m pixel size' self.resize(pixelsize=pixelsize) self.winddir = winddir if winddir is not None: self.calculate_wind() def calculate_wind(self, winddir=None, storeModelSpeed=True): # Calculate wind speed from SAR sigma0 in VV polarization if winddir: self.winddir = winddir if self.winddir is None or self.winddir == 'online': self.winddir = 'ncep_wind_online' # default source if isinstance(self.winddir, int): # Constant wind direction is input print 'Using constant wind (from) direction: ' + str(self.winddir) + \ ' degrees clockwise from North' winddirArray = np.ones(self.shape()) * self.winddir winddir_time = None storeModelSpeed = False # Not relevant if direction given as number else: # Nansat readable file if isinstance(self.winddir, str): try: self.winddir = Nansat(self.winddir) except: try: self.winddir = Nansat(self.winddir + datetime.strftime( self.SAR_image_time, ':%Y%m%d%H%M')) except: pass if not isinstance(self.winddir, Nansat): raise ValueError('Wind direction not available') winddir_time = self.winddir.get_time()[0] # Bi-linear interpolation onto SAR image self.winddir.reproject(self, eResampleAlg=1) # Check time difference between SAR image and wind direction object timediff = self.SAR_image_time - winddir_time hoursDiff = np.abs(timediff.total_seconds() / 3600.) print 'Time difference between SAR image and wind direction: ' \ + '%.2f' % hoursDiff + ' hours' print 'SAR image time: ' + str(self.SAR_image_time) print 'Wind dir time: ' + str(winddir_time) if hoursDiff > 3: print '#########################################' print 'WARNING: time difference exceeds 3 hours!' print '#########################################' wind_u_bandNo = self.winddir._get_band_number({ 'standard_name': 'eastward_wind', }) wind_v_bandNo = self.winddir._get_band_number({ 'standard_name': 'northward_wind', }) # Get wind direction u_array = self.winddir[wind_u_bandNo] v_array = self.winddir[wind_v_bandNo] winddirArray = np.degrees(np.arctan2( -u_array, -v_array)) # 0 from North, 90 from East # Calculate SAR wind with CMOD # TODO: # - add other CMOD versions than CMOD5 print 'Calculating SAR wind with CMOD...' startTime = datetime.now() windspeed = cmod5n_inverse( self[self.sigma0_bandNo], np.mod(winddirArray - self['SAR_look_direction'], 360), self['incidence_angle']) print 'Calculation time: ' + str(datetime.now() - startTime) windspeed[np.where(np.isnan(windspeed))] = np.nan windspeed[np.where(np.isinf(windspeed))] = np.nan # Add wind speed and direction as bands # TODO: make it possible to update existing bands... See # https://github.com/nansencenter/nansat/issues/58 self.add_band(array=windspeed, parameters={ 'wkv': 'wind_speed', 'name': 'windspeed', 'time': self.get_time(self.sigma0_bandNo), 'winddir_time': winddir_time }) self.add_band(array=winddirArray, parameters={ 'wkv': 'wind_from_direction', 'name': 'winddirection', 'time': winddir_time }) if storeModelSpeed: self.add_band(array=self.winddir['windspeed'], parameters={ 'wkv': 'wind_speed', 'name': 'model_windspeed', 'time': winddir_time, }) # TODO: Replace U and V bands with pixelfunctions u = -windspeed * np.sin((180.0 - winddirArray) * np.pi / 180.0) v = windspeed * np.cos((180.0 - winddirArray) * np.pi / 180.0) self.add_band(array=u, parameters={ 'wkv': 'eastward_wind', }) self.add_band(array=v, parameters={ 'wkv': 'northward_wind', }) def _get_masked_windspeed(self, landmask=True, icemask=True): try: sar_windspeed = self['windspeed'] except: raise ValueError('SAR wind has not been calculated, ' \ 'execute calculate_wind(winddir) first.') sar_windspeed[sar_windspeed < 0] = 0 palette = jet if landmask: try: # Land mask sar_windspeed = np.ma.masked_where(self.watermask()[1] == 2, sar_windspeed) palette.set_bad([.3, .3, .3], 1.0) # Land is masked (bad) except: print 'Land mask not available' if icemask: try: # Ice mask try: # first try local file ice = Nansat('metno_local_hires_seaice_' + self.SAR_image_time.strftime('%Y%m%d'), mapperName='metno_local_hires_seaice') except: # otherwise Thredds ice = Nansat('metno_hires_seaice:' + self.SAR_image_time.strftime('%Y%m%d')) ice.reproject(self) iceBandNo = ice._get_band_number( {'standard_name': 'sea_ice_area_fraction'}) sar_windspeed[ice[iceBandNo] > 0] = -1 palette.set_under('w', 1.0) # Ice is 'under' (-1) except: print 'Ice mask not available' return sar_windspeed, palette def write_geotiff(self, filename, landmask=True, icemask=True): sar_windspeed, palette = self._get_masked_windspeed(landmask, icemask) nansat_geotiff = Nansat(array=sar_windspeed, domain=self, parameters={ 'name': 'masked_windspeed', 'minmax': '0 20' }) nansat_geotiff.write_geotiffimage(filename) def plot(self, filename=None, numVectorsX=16, show=True, landmask=True, icemask=True, flip=True, maskWindAbove=35): ''' Basic plotting function showing CMOD wind speed overlaid vectors in SAR image projection''' try: sar_windspeed, palette = self._get_masked_windspeed( landmask, icemask) except: raise ValueError('SAR wind has not been calculated, ' \ 'execute calculate_wind(winddir) before plotting.') sar_windspeed[sar_windspeed > maskWindAbove] = np.nan winddirReductionFactor = np.round(self.vrt.dataset.RasterXSize / numVectorsX) # model_winddir is direction from which wind is blowing winddir_relative_up = 360 - self['winddirection'] + \ self.azimuth_up() indX = range(0, self.vrt.dataset.RasterXSize, winddirReductionFactor) indY = range(0, self.vrt.dataset.RasterYSize, winddirReductionFactor) X, Y = np.meshgrid(indX, indY) try: # scaling of wind vector length, if model wind is available model_windspeed = self['model_windspeed'] model_windspeed = model_windspeed[Y, X] except: model_windspeed = 8 * np.ones(X.shape) Ux = np.sin(np.radians(winddir_relative_up[Y, X])) * model_windspeed Vx = np.cos(np.radians(winddir_relative_up[Y, X])) * model_windspeed # Make sure North is up, and east is right if flip == True: lon, lat = self.get_corners() if lat[0] < lat[1]: sar_windspeed = np.flipud(sar_windspeed) Ux = -np.flipud(Ux) Vx = -np.flipud(Vx) if lon[0] > lon[2]: sar_windspeed = np.fliplr(sar_windspeed) Ux = np.fliplr(Ux) Vx = np.fliplr(Vx) # Plotting figSize = sar_windspeed.shape legendPixels = 60.0 legendPadPixels = 5.0 legendFraction = legendPixels / figSize[0] legendPadFraction = legendPadPixels / figSize[0] dpi = 100.0 fig = plt.figure() fig.set_size_inches( (figSize[1] / dpi, (figSize[0] / dpi) * (1 + legendFraction + legendPadFraction))) ax = fig.add_axes([0, 0, 1, 1 + legendFraction]) ax.set_axis_off() plt.imshow(sar_windspeed, cmap=palette, interpolation='nearest') plt.clim([0, 20]) cbar = plt.colorbar(orientation='horizontal', shrink=.80, aspect=40, fraction=legendFraction, pad=legendPadFraction) cbar.ax.set_ylabel('[m/s]', rotation=0) cbar.ax.yaxis.set_label_position('right') ax.quiver(X, Y, Ux, Vx, angles='xy', width=0.004, scale=200, scale_units='width', color=[.0, .0, .0], headaxislength=4) if filename is not None: fig.savefig(filename, pad_inches=0, dpi=dpi) if show: plt.show() return fig