def clim_from_histogram(self, **kwargs): '''Estimate min and max pixel values from histogram if ratio=1.0, simply the minimum and maximum values are returned. if 0 < ratio < 1.0, get the histogram of the pixel values. Then get rid of (1.0-ratio)/2 from the both sides and return the minimum and maximum values. Parameters ----------- Any of Figure.__init__() parameters Returns -------- clim : numpy array 2D ((3x2) or (1x2)) minimum and maximum pixel values for each band ''' # modify default values self._set_defaults(kwargs) ratio = self.ratio # find masked pixels if mask_array and mask_lut provided masked = None if self.mask_array is not None and self.mask_lut is not None: masked = np.zeros(self.mask_array.shape, 'bool') for lutVal in self.mask_lut: masked = masked + (self.mask_array == lutVal) # create a ratio list for each band if not (isinstance(ratio, float) or isinstance(ratio, int)): raise OptionError('Incorrect input ratio %s' % str(ratio)) # create a ratio list for each band if ratio <= 0 or ratio > 1: raise OptionError('Incorrect input ratio %s' % str(ratio)) # create a 2D array and set min and max values clim = [[0] * self.array.shape[0], [0] * self.array.shape[0]] for iBand in range(self.array.shape[0]): bandArray = self.array[iBand, :, :] # remove masked data if masked is not None: bandArray = bandArray[masked == 0] # remove nan, inf bandArray = bandArray[np.isfinite(bandArray)] # get percentile percentileMin = 100 * (1 - ratio) / 2. percentileMax = 100 * (1 - (1 - ratio) / 2.) if bandArray.size > 0: clim[0][iBand] = np.percentile(bandArray, percentileMin) clim[1][iBand] = np.percentile(bandArray, percentileMax) else: clim[0][iBand], clim[1][iBand] = 0, 1 self.color_limits = clim return clim
def _get_geotransform(self, extentDic): ''' the new coordinates and raster size are calculated based on the given extentDic. Parameters ----------- extentDic : dictionary includes 'te' key and 'ts' or 'tr' key Raises ------- OptionError : occurs when maxX - minX < 0 or maxY - minY < 0 OptionError : occurs when the given resolution is larger than width or height. Returns -------- coordinate : list with 6 float GeoTransform rasterSize : list with two int rasterXSize and rasterYSize ''' # recalculate GeoTransform based on extent option minX = extentDic['te'][0] minY = extentDic['te'][1] maxX = extentDic['te'][2] maxY = extentDic['te'][3] cornerX = minX cornerY = maxY width = maxX - minX height = maxY - minY if width <= 0 or height <= 0: raise OptionError('The extent is illegal. ' '"-te xMin yMin xMax yMax" ') if 'tr' in extentDic.keys(): resolutionX = extentDic['tr'][0] resolutionY = -(extentDic['tr'][1]) if (width < resolutionX or height < resolutionY): raise OptionError('"-tr" is too large. ' 'width is %s, height is %s ' % (str(width), str(height))) rasterXSize = width / resolutionX rasterYSize = abs(height / resolutionY) else: rasterXSize = extentDic['ts'][0] rasterYSize = extentDic['ts'][1] resolutionX = width / rasterXSize resolutionY = -abs(height / rasterYSize) # create a list for GeoTransform coordinates = [cornerX, resolutionX, 0.0, cornerY, 0.0, resolutionY] return coordinates, int(rasterXSize), int(rasterYSize)
def get_dataset(self, ds): ''' Open Dataset ''' if ds is None: try: ds = Dataset(self.fileName) except: raise OptionError('Cannot open %s' % self.fileName) elif type(ds) != Dataset: raise OptionError('Input ds is not netCDF.Dataset!') return ds
def _get_auto_ticks(self, ticks, grid): ''' Automatically create a list of lon or lat ticks from number of list Parameters ---------- ticks : int or list number or location of ticks grid : ndarray grid with lon or lat Returns ------- ticks : list location of ticks ''' gridMin = grid.min() gridMax = grid.max() if type(ticks) is int: ticks = np.linspace(gridMin, gridMax, ticks) elif type(ticks) in [list, tuple]: newTicks = [] for tick in ticks: if tick >= gridMin and tick <= gridMax: newTicks.append(tick) ticks = newTicks else: raise OptionError('Incorrect type of ticks') return ticks
def get_layer_datetime(self, date, datetimes): ''' Get datetime of the matching layer and layer number ''' if len(datetimes) == 1 or date is None: layerNumber = 0 else: # find closest layer datetimeResolution = np.abs(datetimes[0] - datetimes[1]) date = np.datetime64(date).astype('M8[s]') matchingDateDiff = np.min(np.abs(datetimes - date)) if matchingDateDiff > datetimeResolution: raise OptionError('Date %s is out of range' % date) layerNumber = np.argmin(np.abs(datetimes - date)) layerDate = datetimes[layerNumber] return layerNumber, layerDate
def __init__(self, srs=None, ext=None, ds=None, lon=None, lat=None, name='', logLevel=None): '''Create Domain from GDALDataset or string options or lat/lon grids d = Domain(srs, ext) Size, extent and spatial reference is given by strings d = Domain(ds=GDALDataset): Size, extent and spatial reference is copied from input GDAL dataset d = Domain(srs, ds=GDALDataset): Spatial reference is given by srs, but size and extent is determined from input GDAL dataset d = Domain(lon=lonGrid, lat=latGrid) Size, extent and spatial reference is given by two grids Parameters ---------- srs : PROJ4 or EPSG or WKT or NSR or osr.SpatialReference() Input parameter for nansat.NSR() ext : string some gdalwarp options + additional options [http://www.gdal.org/gdalwarp.html] Specifies extent, resolution / size Available options: (('-te' or '-lle') and ('-tr' or '-ts')) (e.g. '-lle -10 30 55 60 -ts 1000 1000' or '-te 100 2000 300 10000 -tr 300 200') -tr resolutionx resolutiony -ts sizex sizey -te xmin ymin xmax ymax -lle lonmin latmin lonmax latmax ds : GDAL dataset lat : Numpy array Grid with latitudes lon : Numpy array Grid with longitudes name : string, optional Name to be added to the Domain object logLevel : int, optional, default=30 level of logging Raises ------- ProjectionError : occurs when Projection() is empty despite it is required for creating extentDic. OptionError : occures when the arguments are not proper. Modifies --------- self.vrt.datasetset : dataset in memory dataset is created based on the input arguments See Also --------- Nansat.reproject() [http://www.gdal.org/gdalwarp.html] [http://trac.osgeo.org/proj/] [http://spatialreference.org/] [http://www.gdal.org/ogr/osr_tutorial.html] ''' # set default attributes self.logger = add_logger('Nansat', logLevel) self.name = name self.logger.debug('ds: %s' % str(ds)) self.logger.debug('srs: %s' % srs) self.logger.debug('ext: %s' % ext) # If too much information is given raise error if ds is not None and srs is not None and ext is not None: raise OptionError('Ambiguous specification of both ' 'dataset, srs- and ext-strings.') # choose between input opitons: # ds # ds and srs # srs and ext # lon and lat # if only a dataset is given: # copy geo-reference from the dataset if ds is not None and srs is None: self.vrt = VRT(gdalDataset=ds) # If dataset and srs are given (but not ext): # use AutoCreateWarpedVRT to determine bounds and resolution elif ds is not None and srs is not None: srs = NSR(srs) tmpVRT = gdal.AutoCreateWarpedVRT(ds, None, srs.wkt) if tmpVRT is None: raise ProjectionError('Could not warp the given dataset' 'to the given SRS.') else: self.vrt = VRT(gdalDataset=tmpVRT) # If SpatialRef and extent string are given (but not dataset) elif srs is not None and ext is not None: srs = NSR(srs) # create full dictionary of parameters extentDic = self._create_extentDic(ext) # convert -lle to -te if 'lle' in extentDic.keys(): extentDic = self._convert_extentDic(srs, extentDic) # get size/extent from the created extet dictionary [geoTransform, rasterXSize, rasterYSize] = self._get_geotransform(extentDic) # create VRT object with given geo-reference parameters self.vrt = VRT(srcGeoTransform=geoTransform, srcProjection=srs.wkt, srcRasterXSize=rasterXSize, srcRasterYSize=rasterYSize) self.extentDic = extentDic elif lat is not None and lon is not None: # create self.vrt from given lat/lon self.vrt = VRT(lat=lat, lon=lon) else: raise OptionError('"dataset" or "srsString and extentString" ' 'or "dataset and srsString" are required') self.logger.debug('vrt.dataset: %s' % str(self.vrt.dataset))
def _create_extentDic(self, extentString): '''Create a dictionary from extentString Check if extentString is proper. * '-te' and '-lle' take 4 numbers. * '-ts' and '-tr' take 2 numbers. * the combination should be ('-te' or '-lle') and ('-ts' or '-tr') If it is proper, create a dictionary Otherwise, raise the error. Parameters ----------- extentString : string '-te xMin yMin xMax yMax', '-tr xResolution yResolution', '-ts width height', '-lle minlon minlat maxlon maxlat' Returns -------- extentDic : dictionary has key ('te' or 'lle') and ('tr' or 'ts') and their values. Raises ------- OptionError : occurs when the extentString is improper ''' extentDic = {} # Find -re text str_tr = re.findall('-tr\s+[-+]?\d*[.\d*]*\s+[-+]?\d*[.\d*]*\s?', extentString) if str_tr != []: # Check the number of -tr elements elm_str = str(str_tr[0].rstrip()) elms_str = elm_str.split(None) if len(elms_str) != 3 or elms_str[2] == '-': raise OptionError('Domain._create_extentDic():' '-tr is used as' '"-tr xResolution yResolution"') # Add the key and value to extentDic extentString = extentString.replace(str_tr[0], '') trElem = str(str_tr).split(None) trkey = trElem[0].translate(string.maketrans('', ''), "[]-'") if trkey != '': elements = [] for i in range(2): elements.append( float(trElem[i + 1].translate(string.maketrans('', ''), "'[]'"))) extentDic[trkey] = elements # Find -ts text str_ts = re.findall('-ts\s+[-+]?\d*[.\d*]*\s+[-+]?\d*[.\d*]*\s?', extentString) if str_ts != []: # Check the number of -ts elements elm_str = str(str_ts[0].rstrip()) elms_str = elm_str.split(None) if len(elms_str) != 3 or elms_str[2] == '-': raise OptionError('Domain._create_extentDic(): ' '"-ts" is used as "-ts width height"') # Add the key and value to extentDic extentString = extentString.replace(str_ts[0], '') tsElem = str(str_ts).split(None) tskey = tsElem[0].translate(string.maketrans('', ''), "[]-'") if tskey != '': elements = [] for i in range(2): elements.append( float(tsElem[i + 1].translate(string.maketrans('', ''), "[]'"))) extentDic[tskey] = elements # Find -te text str_te = re.findall( '-te\s+[-+]?\d*[.\d*]*\s+[-+]?\d*[.\d*]*\s' '+[-+]?\d*[.\d*]*\s+[-+]?\d*[.\d*]*\s?', extentString) if str_te != []: # Check the number of -te elements elm_str = str(str_te[0].rstrip()) elms_str = elm_str.split(None) if len(elms_str) != 5: raise OptionError('Domain._create_extentDic():' '-te is used as "-te xMin yMin xMax yMax"') # Add the key and value to extentDic extentString = extentString.replace(str_te[0], '') teElem = str(str_te).split(None) tekey = teElem[0].translate(string.maketrans('', ''), "[]-'") if tekey != '': elements = [] for i in range(4): elements.append( float(teElem[i + 1].translate(string.maketrans('', ''), "[]'"))) extentDic[tekey] = elements # Find -lle text str_lle = re.findall( '-lle\s+[-+]?\d*[.\d*]*\s+[-+]?\d*[.\d*]*\s' '+[-+]?\d*[.\d*]*\s+[-+]?\d*[.\d*]*\s?', extentString) if str_lle != []: # Check the number of -lle elements elm_str = str(str_lle[0].rstrip()) elms_str = elm_str.split(None) if len(elms_str) != 5: raise OptionError('Domain._create_extentDic():' '-lle is used as ' '"-lle minlon minlat maxlon maxlat"') # Add the key and value to extentDic extentString = extentString.replace(str_lle[0], '') lleElem = str(str_lle).split(None) llekey = lleElem[0].translate(string.maketrans('', ''), "[]-'") if llekey != '': elements = [] for i in range(4): elements.append( float(lleElem[i + 1].translate( string.maketrans('', ''), "[]'"))) extentDic[llekey] = elements result = re.search('\S', extentString) # if there are unnecessary letters, give an error if result is not None: raise OptionError( 'Domain._create_extentDic():' 'extentString is not redable :', extentString) # check if one of '-te' and '-lle' is given if ('lle' not in extentDic) and ('te' not in extentDic): raise OptionError('Domain._create_extentDic():' '"-lle" or "-te" is required.') elif ('lle' in extentDic) and ('te' in extentDic): raise OptionError('Domain._create_extentDic():' '"-lle" or "-te" should be chosen.') # check if one of '-ts' and '-tr' is given if ('ts' not in extentDic) and ('tr' not in extentDic): raise OptionError('Domain._create_extentDic():' '"-ts" or "-tr" is required.') elif ('ts' in extentDic) and ('tr' in extentDic): raise OptionError('Domain._create_extentDic():' '"-ts" or "-tr" should be chosen.') return extentDic
def write_kml_image(self, kmlFileName=None, kmlFigureName=None): '''Create KML file for already projected image Write Domain Image into KML-file for GoogleEarth Parameters ----------- kmlFileName : string, optional Name of the KML-file to generate from the current Domain kmlFigureName : string, optional Name of the projected image stored in .png format Examples --------- # First of all, reproject an image into Lat/Lon WGS84 (Simple Cylindrical) projection # 1. Cancel previous reprojection # 2. Get corners of the image and the pixel resolution # 3. Create Domain with stereographic projection, # corner coordinates and resolution 1000m # 4. Reproject # 5. Write image # 6. Write KML for the image n.reproject() # 1. lons, lats = n.get_corners() # 2. srsString = '+proj=latlong +datum=WGS84 +ellps=WGS84 +no_defs' extentString = '-lle %f %f %f %f -ts 3000 3000' % (min(lons), min(lats), max(lons), max(lats)) d = Domain(srs=srsString, ext=extentString) # 3. n.reproject(d) # 4. n.write_figure(fileName=figureName, bands=[3], clim=[0,0.15], cmapName='gray', transparency=0) # 5. n.write_kml_image(kmlFileName=oPath + fileName + '.kml', kmlFigureName=figureName) # 6. ''' # test input options if kmlFileName is None: raise OptionError('kmlFileName(%s) is wrong' % (kmlFileName)) if kmlFigureName is None: raise OptionError('kmlFigureName(%s) is not specified' % (kmlFigureName)) # open KML, write header kmlFile = file(kmlFileName, 'wt') kmlFile.write('<?xml version="1.0" encoding="UTF-8"?>\n') kmlFile.write('<kml xmlns="http://www.opengis.net/kml/2.2" ' 'xmlns:gx="http://www.google.com/kml/ext/2.2" ' 'xmlns:kml="http://www.opengis.net/kml/2.2" ' 'xmlns:atom="http://www.w3.org/2005/Atom">\n') kmlFile.write('<GroundOverlay>\n') kmlFile.write(' <name>%s</name>\n' % kmlFileName) kmlFile.write(' <Icon>\n') kmlFile.write(' <href>%s</href>\n' % kmlFigureName) kmlFile.write(' <viewBoundScale>0.75</viewBoundScale>\n') kmlFile.write(' </Icon>\n') # get corner of the domain and add to KML domainLon, domainLat = self.get_corners() kmlFile.write(' <LatLonBox>\n') kmlFile.write(' <north>%s</north>\n' % max(domainLat)) kmlFile.write(' <south>%s</south>\n' % min(domainLat)) kmlFile.write(' <east>%s</east>\n' % max(domainLon)) kmlFile.write(' <west>%s</west>\n' % min(domainLon)) kmlFile.write(' </LatLonBox>\n') # write footer and close kmlFile.write('</GroundOverlay>\n') kmlFile.write('</kml>') kmlFile.close()
def write_kml(self, xmlFileName=None, kmlFileName=None): '''Write KML file with domains Convert XML-file with domains into KML-file for GoogleEarth or write KML-file with the current Domain Parameters ----------- xmlFileName : string, optional Name of the XML-file to convert. If only this value is given - kmlFileName=xmlFileName+'.kml' kmlFileName : string, optional Name of the KML-file to generate from the current Domain ''' # test input options if xmlFileName is not None and kmlFileName is None: # if only input XML-file is given - convert it to KML # open XML, get all domains xmlFile = file(xmlFileName, 'rb') kmlFileName = xmlFileName + '.kml' xmlDomains = ElementTree(file=xmlFile).getroot() xmlFile.close() # convert domains in XML into list of domains domains = [] for xmlDomain in list(xmlDomains): # append Domain object to domains list domainName = xmlDomain.attrib['name'] domains.append(Domain(srs=xmlFileName, ext=domainName)) elif xmlFileName is None and kmlFileName is not None: # if only output KML-file is given # then convert the current domain to KML domains = [self] else: # otherwise it is potentially error raise OptionError('Either xmlFileName(%s)\ or kmlFileName(%s) are wrong' % (xmlFileName, kmlFileName)) # open KML, write header kmlFile = file(kmlFileName, 'wt') kmlFile.write('<?xml version="1.0" encoding="UTF-8"?>\n') kmlFile.write('<kml xmlns="http://www.opengis.net/kml/2.2" ' 'xmlns:gx="http://www.google.com/kml/ext/2.2" ' 'xmlns:kml="http://www.opengis.net/kml/2.2" ' 'xmlns:atom="http://www.w3.org/2005/Atom">\n') kmlFile.write('<Document>\n') kmlFile.write(' <name>%s</name>\n' % kmlFileName) kmlFile.write(' <Folder><name>%s</name><open>1</open>\n' % kmlFileName) # get border of each domain and add to KML for domain in list(domains): kmlEntry = domain._get_border_kml() kmlFile.write(kmlEntry) # write footer and close kmlFile.write(' </Folder></Document></kml>\n') kmlFile.close()