def remove_products(xml_filename, product_list): ''' Description: Removes the specified products from the file system, as well as from the XML file. ''' if not product_list: # We don't error, just nothing to do. return espa_xml = metadata_api.parse(xml_filename, silence=True) bands = espa_xml.get_bands() # Gather all the filenames to be removed filenames = list() for band in bands.band: if band.product in product_list: # Add the .img file filenames.append(band.file_name) # Add the .hdr file hdr_filename = band.file_name.replace('.img', '.hdr') filenames.append(hdr_filename) # If we found some then remove them if len(filenames) > 0: # First remove from disk for filename in filenames: if os.path.exists(filename): os.unlink(filename) # Second remove from metadata XML # Remove them from the XML by creating a new list of all the # others bands.band[:] = [ band for band in bands.band if band.product not in product_list ] try: # Export to the file with validation with open(xml_filename, 'w') as xml_fd: metadata_api.export(xml_fd, espa_xml) except Exception: raise del bands del espa_xml
def remove_products(xml_filename, product_list): ''' Description: Removes the specified products from the file system, as well as from the XML file. ''' if not product_list: # We don't error, just nothing to do. return espa_xml = metadata_api.parse(xml_filename, silence=True) bands = espa_xml.get_bands() # Gather all the filenames to be removed filenames = list() for band in bands.band: if band.product in product_list: # Add the .img file filenames.append(band.file_name) # Add the .hdr file hdr_filename = band.file_name.replace('.img', '.hdr') filenames.append(hdr_filename) # If we found some then remove them if len(filenames) > 0: # First remove from disk for filename in filenames: if os.path.exists(filename): os.unlink(filename) # Second remove from metadata XML # Remove them from the XML by creating a new list of all the # others bands.band[:] = [band for band in bands.band if band.product not in product_list] try: # Export to the file with validation with open(xml_filename, 'w') as xml_fd: metadata_api.export(xml_fd, espa_xml) except Exception: raise del bands del espa_xml
def generate_data(self): ''' Description: Provides the main processing algorithm for building the Surface Temperature product. It produces the final ST product. ''' try: self.retrieve_metadata_information() except Exception: self.logger.exception('Failed reading input XML metadata file') raise # Register all the gdal drivers and choose the ENVI for our output gdal.AllRegister() envi_driver = gdal.GetDriverByName('ENVI') # Read the bands into memory # Landsat Radiance at sensor for thermal band self.logger.info('Loading intermediate thermal band data [{0}]'.format( self.thermal_name)) dataset = gdal.Open(self.thermal_name) x_dim = dataset.RasterXSize # They are all the same size y_dim = dataset.RasterYSize thermal_data = dataset.GetRasterBand(1).ReadAsArray(0, 0, x_dim, y_dim) # Atmospheric transmittance self.logger.info( 'Loading intermediate transmittance band data [{0}]'.format( self.transmittance_name)) dataset = gdal.Open(self.transmittance_name) trans_data = dataset.GetRasterBand(1).ReadAsArray(0, 0, x_dim, y_dim) # Atmospheric path radiance - upwelled radiance self.logger.info( 'Loading intermediate upwelled band data [{0}]'.format( self.upwelled_name)) dataset = gdal.Open(self.upwelled_name) upwelled_data = dataset.GetRasterBand(1).ReadAsArray( 0, 0, x_dim, y_dim) self.logger.info('Calculating surface radiance') # Surface radiance with np.errstate(invalid='ignore'): surface_radiance = (thermal_data - upwelled_data) / trans_data # Fix the no data locations no_data_locations = np.where(thermal_data == self.no_data_value) surface_radiance[no_data_locations] = self.no_data_value no_data_locations = np.where(trans_data == self.no_data_value) surface_radiance[no_data_locations] = self.no_data_value no_data_locations = np.where(upwelled_data == self.no_data_value) surface_radiance[no_data_locations] = self.no_data_value # Memory cleanup del thermal_data del trans_data del upwelled_data del no_data_locations # Downwelling sky irradiance self.logger.info( 'Loading intermediate downwelled band data [{0}]'.format( self.downwelled_name)) dataset = gdal.Open(self.downwelled_name) downwelled_data = dataset.GetRasterBand(1).ReadAsArray( 0, 0, x_dim, y_dim) # Landsat emissivity estimated from ASTER GED data self.logger.info( 'Loading intermediate emissivity band data [{0}]'.format( self.emissivity_name)) dataset = gdal.Open(self.emissivity_name) emissivity_data = dataset.GetRasterBand(1).ReadAsArray( 0, 0, x_dim, y_dim) # Save for the output product ds_srs = osr.SpatialReference() ds_srs.ImportFromWkt(dataset.GetProjection()) ds_transform = dataset.GetGeoTransform() # Memory cleanup del dataset # Estimate Earth-emitted radiance by subtracting off the reflected # downwelling component radiance = (surface_radiance - (1.0 - emissivity_data) * downwelled_data) # Account for surface emissivity to get Plank emitted radiance self.logger.info('Calculating Plank emitted radiance') with np.errstate(invalid='ignore'): radiance_emitted = radiance / emissivity_data # Fix the no data locations no_data_locations = np.where(surface_radiance == self.no_data_value) radiance_emitted[no_data_locations] = self.no_data_value no_data_locations = np.where(downwelled_data == self.no_data_value) radiance_emitted[no_data_locations] = self.no_data_value no_data_locations = np.where(emissivity_data == self.no_data_value) radiance_emitted[no_data_locations] = self.no_data_value # Memory cleanup del downwelled_data del emissivity_data del surface_radiance del radiance del no_data_locations # Use Brightness Temperature LUT to get skin temperature # Read the correct one for what we are processing if self.satellite == 'LANDSAT_8': self.logger.info('Using Landsat 8 Brightness Temperature LUT') bt_name = 'L8_Brightness_Temperature_LUT.txt' elif self.satellite == 'LANDSAT_7': self.logger.info('Using Landsat 7 Brightness Temperature LUT') bt_name = 'L7_Brightness_Temperature_LUT.txt' elif self.satellite == 'LANDSAT_5': self.logger.info('Using Landsat 5 Brightness Temperature LUT') bt_name = 'L5_Brightness_Temperature_LUT.txt' elif self.satellite == 'LANDSAT_4': self.logger.info('Using Landsat 4 Brightness Temperature LUT') bt_name = 'L4_Brightness_Temperature_LUT.txt' bt_data = np.loadtxt(os.path.join(self.st_data_dir, bt_name), dtype=float, delimiter=' ') bt_radiance_lut = bt_data[:, 1] bt_temp_lut = bt_data[:, 0] self.logger.info('Generating ST results') st_data = np.interp(radiance_emitted, bt_radiance_lut, bt_temp_lut) # Scale the result st_data = st_data * MULT_FACTOR # Add the fill and scan gaps back into the results, since they may # have been lost self.logger.info('Adding fill and data gaps back into the Surface' ' Temperature results') # Fix the no data locations no_data_locations = np.where(radiance_emitted == self.no_data_value) st_data[no_data_locations] = self.no_data_value # Memory cleanup del radiance_emitted del no_data_locations product_id = self.xml_filename.split('.xml')[0] st_img_filename = ''.join([product_id, '_st', '.img']) st_hdr_filename = ''.join([product_id, '_st', '.hdr']) st_aux_filename = ''.join([st_img_filename, '.aux', '.xml']) self.logger.info('Creating {0}'.format(st_img_filename)) util.Geo.generate_raster_file(envi_driver, st_img_filename, st_data, x_dim, y_dim, ds_transform, ds_srs.ExportToWkt(), self.no_data_value, gdal.GDT_Int16) self.logger.info('Updating {0}'.format(st_hdr_filename)) util.Geo.update_envi_header(st_hdr_filename, self.no_data_value) # Memory cleanup del ds_srs del ds_transform # Remove the *.aux.xml file generated by GDAL if os.path.exists(st_aux_filename): os.unlink(st_aux_filename) self.logger.info('Adding {0} to {1}'.format(st_img_filename, self.xml_filename)) # Add the estimated Surface Temperature product to the metadata espa_xml = metadata_api.parse(self.xml_filename, silence=True) bands = espa_xml.get_bands() sensor_code = product_id[0:4] # Find the TOA Band 1 to use for the specific band details base_band = None for band in bands.band: if band.product == 'toa_refl' and band.name == 'toa_band1': base_band = band if base_band is None: raise Exception('Failed to find the TOA band 1' ' in the input data') st_band = metadata_api.band(product='st', source='toa_refl', name='surface_temperature', category='image', data_type='INT16', scale_factor=SCALE_FACTOR, add_offset=0, nlines=base_band.get_nlines(), nsamps=base_band.get_nsamps(), fill_value=str(self.no_data_value)) st_band.set_short_name('{0}ST'.format(sensor_code)) st_band.set_long_name('Surface Temperature') st_band.set_file_name(st_img_filename) st_band.set_data_units('temperature (kelvin)') pixel_size = metadata_api.pixel_size(base_band.pixel_size.x, base_band.pixel_size.x, base_band.pixel_size.units) st_band.set_pixel_size(pixel_size) st_band.set_resample_method('none') valid_range = metadata_api.valid_range(min=1500, max=3730) st_band.set_valid_range(valid_range) # Set the date, but first clean the microseconds off of it production_date = (datetime.datetime.strptime( datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S'), '%Y-%m-%dT%H:%M:%S')) st_band.set_production_date(production_date) st_band.set_app_version(util.Version.app_version()) bands.add_band(st_band) # Write the XML metadata file out with open(self.xml_filename, 'w') as metadata_fd: metadata_api.export(metadata_fd, espa_xml) # Memory cleanup del st_band del bands del espa_xml del st_data
def update_espa_xml(parms, xml, xml_filename): logger = EspaLogging.get_logger(settings.PROCESSING_LOGGER) try: # Default the datum to WGS84 datum = settings.WGS84 if parms['datum'] is not None: datum = parms['datum'] bands = xml.get_bands() for band in bands.band: img_filename = band.get_file_name() logger.info("Updating XML for %s" % img_filename) ds = gdal.Open(img_filename) if ds is None: msg = "GDAL failed to open (%s)" % img_filename raise RuntimeError(msg) try: ds_band = ds.GetRasterBand(1) ds_transform = ds.GetGeoTransform() ds_srs = osr.SpatialReference() ds_srs.ImportFromWkt(ds.GetProjection()) except Exception as excep: raise ee.ESPAException(ee.ErrorCodes.warping, str(excep)), None, sys.exc_info()[2] projection_name = ds_srs.GetAttrValue('PROJECTION') number_of_lines = float(ds_band.YSize) number_of_samples = float(ds_band.XSize) # Need to abs these because they are coming from the transform, # which may becorrect for the transform, # but not how us humans understand it x_pixel_size = abs(ds_transform[1]) y_pixel_size = abs(ds_transform[5]) del ds_band del ds # Update the band information in the XML file band.set_nlines(number_of_lines) band.set_nsamps(number_of_samples) band_pixel_size = band.get_pixel_size() band_pixel_size.set_x(x_pixel_size) band_pixel_size.set_y(y_pixel_size) # For sanity report the resample method applied to the data resample_method = band.get_resample_method() logger.info("RESAMPLE METHOD [%s]" % resample_method) # We only support one unit type for each projection if projection_name is not None: if projection_name.lower().startswith('transverse_mercator'): band_pixel_size.set_units('meters') elif projection_name.lower().startswith('polar'): band_pixel_size.set_units('meters') elif projection_name.lower().startswith('albers'): band_pixel_size.set_units('meters') elif projection_name.lower().startswith('sinusoidal'): band_pixel_size.set_units('meters') else: # Must be Geographic Projection band_pixel_size.set_units('degrees') ###################################################################### # Fix the projection information for the warped data ###################################################################### gm = xml.get_global_metadata() # If the image extents were changed, then the scene center time is # meaningless so just remove it # We don't have any way to calculate a new one if parms['image_extents']: del gm.scene_center_time gm.scene_center_time = None # Remove the projection parameter object from the structure so that it # can be replaced with the new one # Geographic doesn't have one if gm.projection_information.utm_proj_params is not None: del gm.projection_information.utm_proj_params gm.projection_information.utm_proj_params = None if gm.projection_information.ps_proj_params is not None: del gm.projection_information.ps_proj_params gm.projection_information.ps_proj_params = None if gm.projection_information.albers_proj_params is not None: del gm.projection_information.albers_proj_params gm.projection_information.albers_proj_params = None if gm.projection_information.sin_proj_params is not None: del gm.projection_information.sin_proj_params gm.projection_information.sin_proj_params = None # Rebuild the projection parameters projection_name = ds_srs.GetAttrValue('PROJECTION') if projection_name is not None: # ---------------------------------------------------------------- if projection_name.lower().startswith('transverse_mercator'): logger.info("---- Updating UTM Parameters") # Get the parameter values zone = int(ds_srs.GetUTMZone()) # Get a new UTM projection parameter object and populate it utm_projection = metadata_api.utm_proj_params() utm_projection.set_zone_code(zone) # Add the object to the projection information gm.projection_information.set_utm_proj_params(utm_projection) # Update the attribute values gm.projection_information.set_projection("UTM") gm.projection_information.set_datum(settings.WGS84) # ---------------------------------------------------------------- elif projection_name.lower().startswith('polar'): logger.info("---- Updating Polar Stereographic Parameters") # Get the parameter values latitude_true_scale = ds_srs.GetProjParm('latitude_of_origin') longitude_pole = ds_srs.GetProjParm('central_meridian') false_easting = ds_srs.GetProjParm('false_easting') false_northing = ds_srs.GetProjParm('false_northing') # Get a new PS projection parameter object and populate it ps_projection = metadata_api.ps_proj_params() ps_projection.set_latitude_true_scale(latitude_true_scale) ps_projection.set_longitude_pole(longitude_pole) ps_projection.set_false_easting(false_easting) ps_projection.set_false_northing(false_northing) # Add the object to the projection information gm.projection_information.set_ps_proj_params(ps_projection) # Update the attribute values gm.projection_information.set_projection("PS") gm.projection_information.set_datum(settings.WGS84) # ---------------------------------------------------------------- elif projection_name.lower().startswith('albers'): logger.info("---- Updating Albers Equal Area Parameters") # Get the parameter values standard_parallel1 = ds_srs.GetProjParm('standard_parallel_1') standard_parallel2 = ds_srs.GetProjParm('standard_parallel_2') origin_latitude = ds_srs.GetProjParm('latitude_of_center') central_meridian = ds_srs.GetProjParm('longitude_of_center') false_easting = ds_srs.GetProjParm('false_easting') false_northing = ds_srs.GetProjParm('false_northing') # Get a new ALBERS projection parameter object and populate it albers_projection = metadata_api.albers_proj_params() albers_projection.set_standard_parallel1(standard_parallel1) albers_projection.set_standard_parallel2(standard_parallel2) albers_projection.set_origin_latitude(origin_latitude) albers_projection.set_central_meridian(central_meridian) albers_projection.set_false_easting(false_easting) albers_projection.set_false_northing(false_northing) # Add the object to the projection information gm.projection_information. \ set_albers_proj_params(albers_projection) # Update the attribute values gm.projection_information.set_projection("ALBERS") # This projection can have different datums, so use the datum # requested by the user gm.projection_information.set_datum(datum) # ---------------------------------------------------------------- elif projection_name.lower().startswith('sinusoidal'): logger.info("---- Updating Sinusoidal Parameters") # Get the parameter values central_meridian = ds_srs.GetProjParm('longitude_of_center') false_easting = ds_srs.GetProjParm('false_easting') false_northing = ds_srs.GetProjParm('false_northing') # Get a new SIN projection parameter object and populate it sin_projection = metadata_api.sin_proj_params() sin_projection.set_sphere_radius( settings.SINUSOIDAL_SPHERE_RADIUS) sin_projection.set_central_meridian(central_meridian) sin_projection.set_false_easting(false_easting) sin_projection.set_false_northing(false_northing) # Add the object to the projection information gm.projection_information.set_sin_proj_params(sin_projection) # Update the attribute values gm.projection_information.set_projection("SIN") # This projection doesn't have a datum del gm.projection_information.datum gm.projection_information.datum = None else: # ---------------------------------------------------------------- # Must be Geographic Projection logger.info("---- Updating Geographic Parameters") gm.projection_information.set_projection('GEO') gm.projection_information.set_datum(settings.WGS84) # WGS84 only gm.projection_information.set_units('degrees') # always degrees # Fix the UL and LR center of pixel map coordinates (map_ul_x, map_ul_y) = convert_imageXY_to_mapXY(0.5, 0.5, ds_transform) (map_lr_x, map_lr_y) = convert_imageXY_to_mapXY( number_of_samples - 0.5, number_of_lines - 0.5, ds_transform) for cp in gm.projection_information.corner_point: if cp.location == 'UL': cp.set_x(map_ul_x) cp.set_y(map_ul_y) if cp.location == 'LR': cp.set_x(map_lr_x) cp.set_y(map_lr_y) # Fix the UL and LR center of pixel latitude and longitude coordinates srs_lat_lon = ds_srs.CloneGeogCS() coord_tf = osr.CoordinateTransformation(ds_srs, srs_lat_lon) for corner in gm.corner: if corner.location == 'UL': (lon, lat, height) = \ coord_tf.TransformPoint(map_ul_x, map_ul_y) corner.set_longitude(lon) corner.set_latitude(lat) if corner.location == 'LR': (lon, lat, height) = \ coord_tf.TransformPoint(map_lr_x, map_lr_y) corner.set_longitude(lon) corner.set_latitude(lat) # Determine the bounding coordinates # Initialize using the UL and LR, then walk the edges of the image, # because some projections may not have the values in the corners of # the image # UL (map_x, map_y) = convert_imageXY_to_mapXY(0.0, 0.0, ds_transform) (ul_lon, ul_lat, height) = coord_tf.TransformPoint(map_x, map_y) # LR (map_x, map_y) = convert_imageXY_to_mapXY(number_of_samples, number_of_lines, ds_transform) (lr_lon, lr_lat, height) = coord_tf.TransformPoint(map_x, map_y) # Set the initial values west_lon = min(ul_lon, lr_lon) east_lon = max(ul_lon, lr_lon) north_lat = max(ul_lat, lr_lat) south_lat = min(ul_lat, lr_lat) # Walk across the top and bottom of the image for sample in range(0, int(number_of_samples)+1): (map_x, map_y) = \ convert_imageXY_to_mapXY(sample, 0.0, ds_transform) (top_lon, top_lat, height) = coord_tf.TransformPoint(map_x, map_y) (map_x, map_y) = \ convert_imageXY_to_mapXY(sample, number_of_lines, ds_transform) (bottom_lon, bottom_lat, height) = \ coord_tf.TransformPoint(map_x, map_y) west_lon = min(top_lon, bottom_lon, west_lon) east_lon = max(top_lon, bottom_lon, east_lon) north_lat = max(top_lat, bottom_lat, north_lat) south_lat = min(top_lat, bottom_lat, south_lat) # Walk down the left and right of the image for line in range(0, int(number_of_lines)+1): (map_x, map_y) = \ convert_imageXY_to_mapXY(0.0, line, ds_transform) (left_lon, left_lat, height) = \ coord_tf.TransformPoint(map_x, map_y) (map_x, map_y) = \ convert_imageXY_to_mapXY(number_of_samples, line, ds_transform) (right_lon, right_lat, height) = \ coord_tf.TransformPoint(map_x, map_y) west_lon = min(left_lon, right_lon, west_lon) east_lon = max(left_lon, right_lon, east_lon) north_lat = max(left_lat, right_lat, north_lat) south_lat = min(left_lat, right_lat, south_lat) # Update the bounding coordinates in the XML bounding_coords = gm.get_bounding_coordinates() bounding_coords.set_west(west_lon) bounding_coords.set_east(east_lon) bounding_coords.set_north(north_lat) bounding_coords.set_south(south_lat) del ds_transform del ds_srs # Write out a new XML file after validation logger.info("---- Validating XML Modifications and" " Creating Temp Output File") tmp_xml_filename = 'tmp-%s' % xml_filename with open(tmp_xml_filename, 'w') as tmp_fd: # Call the export with validation metadata_api.export(tmp_fd, xml) # Remove the original if os.path.exists(xml_filename): os.unlink(xml_filename) # Rename the temp file back to the original name os.rename(tmp_xml_filename, xml_filename) except Exception as excep: raise ee.ESPAException(ee.ErrorCodes.warping, str(excep)), None, sys.exc_info()[2]
def createXML(self, scene_xml_file=None, output_xml_file=None, start_year=None, end_year=None, fill_value=None, imgfile=None, log_handler=None): """Creates an XML file for the products produced by runAnnualBurnSummaries. Description: routine to create the XML file for the burned area summary bands. The sample scene-based XML file will be used as the basis for the projection information for the output XML file. The image size, extents, etc. will need to be updated, as will the band information. History: Created on May 12, 2014 by Gail Schmidt, USGS/EROS LSRD Project Args: scene_xml_file - scene-based XML file to be used as the base XML information for the projection metadata. output_xml_file - name of the XML file to be written start_year - starting year of the scenes to process end_year - ending year of the scenes to process fill_value - fill or nodata value for this dataset imgfile - name of burned area image file with associated ENVI header which can be used to obtain the extents and geographic information for these products log_handler - handler for the logging information Returns: ERROR - error creating the XML file SUCCESS - successful creation of the XML file """ # parse the scene-based XML file, just as a basis for the output XML # file. the global attributes will be similar, but the extents and # size of the image will be different. the bands will be based on the # bands that are output from this routine. xml = metadata_api.parse (scene_xml_file, silence=True) meta_bands = xml.get_bands() meta_global = xml.get_global_metadata() # update the global information meta_global.set_data_provider("USGS/EROS") meta_global.set_satellite("LANDSAT") meta_global.set_instrument("combination") del (meta_global.acquisition_date) meta_global.set_acquisition_date(None) # open the image file to obtain the geospatial and spatial reference # information ds = gdal.Open (imgfile) if ds is None: msg = "GDAL failed to open %s" % imgfile logIt (msg, log_handler) return ERROR ds_band = ds.GetRasterBand (1) if ds_band is None: msg = "GDAL failed to get the first band in %s" % imgfile logIt (msg, log_handler) return ERROR nlines = float(ds_band.YSize) nsamps = float(ds_band.XSize) nlines_int = ds_band.YSize nsamps_int = ds_band.XSize del (ds_band) ds_transform = ds.GetGeoTransform() if ds_transform is None: msg = "GDAL failed to get the geographic transform information " \ "from %s" % imgfile logIt (msg, log_handler) return ERROR ds_srs = osr.SpatialReference() if ds_srs is None: msg = "GDAL failed to get the spatial reference information " \ "from %s" % imgfile logIt (msg, log_handler) return ERROR ds_srs.ImportFromWkt (ds.GetProjection()) del (ds) # get the UL and LR center of pixel map coordinates (map_ul_x, map_ul_y) = convert_imageXY_to_mapXY (0.5, 0.5, ds_transform) (map_lr_x, map_lr_y) = convert_imageXY_to_mapXY ( nsamps - 0.5, nlines - 0.5, ds_transform) # update the UL and LR projection corners along with the origin of the # corners, for the center of the pixel (global projection information) for mycorner in meta_global.projection_information.corner_point: if mycorner.location == 'UL': mycorner.set_x (map_ul_x) mycorner.set_y (map_ul_y) if mycorner.location == 'LR': mycorner.set_x (map_lr_x) mycorner.set_y (map_lr_y) meta_global.projection_information.set_grid_origin("CENTER") # update the UL and LR latitude and longitude coordinates, using the # center of the pixel srs_lat_lon = ds_srs.CloneGeogCS() coord_tf = osr.CoordinateTransformation (ds_srs, srs_lat_lon) for mycorner in meta_global.corner: if mycorner.location == 'UL': (lon, lat, height) = \ coord_tf.TransformPoint (map_ul_x, map_ul_y) mycorner.set_longitude (lon) mycorner.set_latitude (lat) if mycorner.location == 'LR': (lon, lat, height) = \ coord_tf.TransformPoint (map_lr_x, map_lr_y) mycorner.set_longitude (lon) mycorner.set_latitude (lat) # determine the bounding coordinates; initialize using the UL and LR # then work around the scene edges # UL (map_x, map_y) = convert_imageXY_to_mapXY (0.0, 0.0, ds_transform) (ul_lon, ul_lat, height) = coord_tf.TransformPoint (map_x, map_y) # LR (map_x, map_y) = convert_imageXY_to_mapXY (nsamps, nlines, ds_transform) (lr_lon, lr_lat, height) = coord_tf.TransformPoint (map_x, map_y) # find the min and max values accordingly, for initialization west_lon = min (ul_lon, lr_lon) east_lon = max (ul_lon, lr_lon) north_lat = max (ul_lat, lr_lat) south_lat = min (ul_lat, lr_lat) # traverse the boundaries of the image to determine the bounding # coords; traverse one extra line and sample to get the the outer # extents of the image vs. just the UL of the outer edge. # top and bottom edges for samp in range (0, nsamps_int+1): # top edge (map_x, map_y) = convert_imageXY_to_mapXY (samp, 0.0, ds_transform) (top_lon, top_lat, height) = coord_tf.TransformPoint (map_x, map_y) # lower edge (map_x, map_y) = convert_imageXY_to_mapXY (samp, nlines, ds_transform) (low_lon, low_lat, height) = coord_tf.TransformPoint (map_x, map_y) # update the min and max values west_lon = min (top_lon, low_lon, west_lon) east_lon = max (top_lon, low_lon, east_lon) north_lat = max (top_lat, low_lat, north_lat) south_lat = min (top_lat, low_lat, south_lat) # left and right edges for line in range (0, nlines_int+1): # left edge (map_x, map_y) = convert_imageXY_to_mapXY (0.0, line, ds_transform) (left_lon, left_lat, height) = coord_tf.TransformPoint (map_x, map_y) # right edge (map_x, map_y) = convert_imageXY_to_mapXY (nsamps, line, ds_transform) (right_lon, right_lat, height) = coord_tf.TransformPoint (map_x, map_y) # update the min and max values west_lon = min (left_lon, right_lon, west_lon) east_lon = max (left_lon, right_lon, east_lon) north_lat = max (left_lat, right_lat, north_lat) south_lat = min (left_lat, right_lat, south_lat) # update the XML bounding_coords = meta_global.get_bounding_coordinates() bounding_coords.set_west (west_lon) bounding_coords.set_east (east_lon) bounding_coords.set_north (north_lat) bounding_coords.set_south (south_lat) del (ds_transform) del (ds_srs) # clear some of the global information that doesn't apply for these # products del (meta_global.scene_center_time) meta_global.set_scene_center_time(None) del (meta_global.lpgs_metadata_file) meta_global.set_lpgs_metadata_file(None) del (meta_global.orientation_angle) meta_global.set_orientation_angle(None) del (meta_global.level1_production_date) meta_global.set_level1_production_date(None) # clear the solar angles del (meta_global.solar_angles) meta_global.set_solar_angles(None) # save the first band and then wipe the bands out so that new bands # can be added for the burned area bands myband_save = meta_bands.band[0] del (meta_bands.band) meta_bands.band = [] # create the band information; there are 4 output products per year # for the burned area dataset; add enough bands to cover the products # and years # 1. first date a burned area was observed (burned_area) # 2. number of times burn was observed (burn_count) # 3. number of good looks (good_looks_count) # 4. maximum probability for burned area (max_burn_prob) nproducts = 4 nyears = end_year - start_year + 1 nbands = nproducts * nyears for i in range (0, nbands): # add the new band myband = metadata_api.band() meta_bands.band.append(myband) # how many bands are there in the new XML file num_scene_bands = len (meta_bands.band) print "New XML file has %d bands" % num_scene_bands # loop through the products and years to create the band metadata band_count = 0 for product in range (1, nproducts+1): for year in range (start_year, end_year+1): myband = meta_bands.band[band_count] myband.set_product("burned_area") myband.set_short_name("LNDBA") myband.set_data_type("INT16") myband.set_pixel_size(myband_save.get_pixel_size()) myband.set_fill_value(fill_value) myband.set_nlines(nlines) myband.set_nsamps(nsamps) myband.set_app_version(self.burned_area_version) production_date = time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime()) myband.set_production_date ( \ datetime_.datetime.strptime(production_date, '%Y-%m-%dT%H:%M:%S')) # clear some of the band-specific fields that don't apply for # this product del (myband.source) myband.set_source(None) del (myband.saturate_value) myband.set_saturate_value(None) del (myband.scale_factor) myband.set_scale_factor(None) del (myband.add_offset) myband.set_add_offset(None) del (myband.toa_reflectance) myband.set_toa_reflectance(None) del (myband.bitmap_description) myband.set_bitmap_description(None) del (myband.class_values) myband.set_class_values(None) del (myband.qa_description) myband.set_qa_description(None) del (myband.calibrated_nt) myband.set_calibrated_nt(None) # handle the band-specific differences valid_range = metadata_api.valid_range() if product == 1: name = "burned_area_%d" % year long_name = "first DOY a burn was observed" file_name = "burned_area_%d.img" % year category = "image" data_units = "day of year" valid_range.min = 0 valid_range.max = 366 qa_description = "0: no burn observed" elif product == 2: name = "burn_count_%d" % year long_name = "number of times a burn was observed" file_name = "burn_count_%d.img" % year category = "image" data_units = "count" valid_range.min = 0 valid_range.max = 366 qa_description = "0: no burn observed" elif product == 3: name = "good_looks_count_%d" % year long_name = "number of good looks (pixels with good QA)" file_name = "good_looks_count_%d.img" % year category = "qa" data_units = "count" valid_range.min = 0 valid_range.max = 366 qa_description = "0: no valid pixels (water, cloud, " \ "snow, etc.)" elif product == 4: name = "max_burn_prob_%d" % year long_name = "maximum probability for burned area" file_name = "max_burn_prob_%d.img" % year category = "image" data_units = "probability" valid_range.min = 0 valid_range.max = 100 qa_description = "-9998: bad QA (water, cloud, snow, etc.)" myband.set_name(name) myband.set_long_name(long_name) myband.set_file_name(file_name) myband.set_category(category) myband.set_data_units(data_units) myband.set_valid_range(valid_range) myband.set_qa_description(qa_description) # increment the band counter band_count += 1 # end for year # end for nproducts # write out a the XML file after validation # call the export with validation fd = open (output_xml_file, 'w') if fd == None: msg = "Unable to open the output XML file (%s) for writing." % \ output_xml_file logIt (msg, log_handler) return ERROR metadata_api.export (fd, xml) fd.flush() fd.close() return SUCCESS
def generate_product(self): ''' Description: Provides the main processing algorithm for generating the estimated Landsat emissivity product. It produces the final emissivity product. ''' self.logger = logging.getLogger(__name__) self.logger.info('Start - Estimate Landsat Emissivity') try: self.retrieve_metadata_information() except Exception: self.logger.exception('Failed reading input XML metadata file') raise try: self.determine_sensor_specific_coefficients() except Exception: self.logger.exception('Failed determining sensor coefficients') raise # Register all the gdal drivers and choose the GeoTiff for our temp # output gdal.AllRegister() geotiff_driver = gdal.GetDriverByName('GTiff') envi_driver = gdal.GetDriverByName('ENVI') # ==================================================================== # Build NDVI in memory self.logger.info('Building TOA based NDVI band for Landsat data') # NIR ---------------------------------------------------------------- data_set = gdal.Open(self.toa_nir_name) x_dim = data_set.RasterXSize # They are all the same size y_dim = data_set.RasterYSize ls_nir_data = data_set.GetRasterBand(1).ReadAsArray(0, 0, x_dim, y_dim) nir_no_data_locations = np.where(ls_nir_data == self.no_data_value) ls_nir_data = ls_nir_data * self.toa_nir_scale_factor # RED ---------------------------------------------------------------- data_set = gdal.Open(self.toa_red_name) ls_red_data = data_set.GetRasterBand(1).ReadAsArray(0, 0, x_dim, y_dim) red_no_data_locations = np.where(ls_red_data == self.no_data_value) ls_red_data = ls_red_data * self.toa_red_scale_factor # NDVI --------------------------------------------------------------- ls_ndvi_data = ((ls_nir_data - ls_red_data) / (ls_nir_data + ls_red_data)) # Cleanup no data locations ls_ndvi_data[nir_no_data_locations] = self.no_data_value ls_ndvi_data[red_no_data_locations] = self.no_data_value if self.keep_intermediate_data: geo_transform = data_set.GetGeoTransform() ds_srs = osr.SpatialReference() ds_srs.ImportFromWkt(data_set.GetProjection()) # Memory cleanup del ls_red_data del ls_nir_data del nir_no_data_locations del red_no_data_locations # ==================================================================== # Build NDSI in memory self.logger.info('Building TOA based NDSI band for Landsat data') # GREEN -------------------------------------------------------------- data_set = gdal.Open(self.toa_green_name) ls_green_data = data_set.GetRasterBand(1).ReadAsArray(0, 0, x_dim, y_dim) green_no_data_locations = ( np.where(ls_green_data == self.no_data_value)) ls_green_data = ls_green_data * self.toa_green_scale_factor # SWIR1 -------------------------------------------------------------- data_set = gdal.Open(self.toa_swir1_name) ls_swir1_data = data_set.GetRasterBand(1).ReadAsArray(0, 0, x_dim, y_dim) swir1_no_data_locations = ( np.where(ls_swir1_data == self.no_data_value)) ls_swir1_data = ls_swir1_data * self.toa_swir1_scale_factor # Build the Landsat TOA NDSI data self.logger.info('Building TOA based NDSI for Landsat data') ls_ndsi_data = ((ls_green_data - ls_swir1_data) / (ls_green_data + ls_swir1_data)) # Cleanup no data locations ls_ndsi_data[green_no_data_locations] = self.no_data_value # Cleanup no data locations ls_ndsi_data[swir1_no_data_locations] = self.no_data_value # Memory cleanup del ls_green_data del ls_swir1_data del green_no_data_locations del swir1_no_data_locations # Save for the output products ds_tmp_srs = osr.SpatialReference() ds_tmp_srs.ImportFromWkt(data_set.GetProjection()) ds_tmp_transform = data_set.GetGeoTransform() # Memory cleanup del data_set # Save the locations for the specfied snow pixels self.logger.info('Determine snow pixel locations') selected_snow_locations = np.where(ls_ndsi_data > 0.4) # Save ndvi and ndsi no data locations ndvi_no_data_locations = np.where(ls_ndvi_data == self.no_data_value) ndsi_no_data_locations = np.where(ls_ndsi_data == self.no_data_value) # Memory cleanup del ls_ndsi_data # Turn all negative values to zero # Use a realy small value so that we don't have negative zero (-0.0) ls_ndvi_data[ls_ndvi_data < 0.0000001] = 0 if self.keep_intermediate_data: self.logger.info('Writing Landsat NDVI raster') util.Geo.generate_raster_file(geotiff_driver, 'internal_landsat_ndvi.tif', ls_ndvi_data, x_dim, y_dim, geo_transform, ds_srs.ExportToWkt(), self.no_data_value, gdal.GDT_Float32) # Build the estimated Landsat EMIS data from the ASTER GED data and # warp it to the Landsat scenes projection and image extents # For convenience the ASTER NDVI is also extracted and warped to the # Landsat scenes projection and image extents self.logger.info('Build thermal emissivity band and' ' retrieve ASTER NDVI') (ls_emis_warped_name, aster_ndvi_warped_name) = self.build_ls_emis_data(geotiff_driver) # Load the warped estimated Landsat EMIS into memory data_set = gdal.Open(ls_emis_warped_name) ls_emis_data = data_set.GetRasterBand(1).ReadAsArray(0, 0, x_dim, y_dim) ls_emis_gap_locations = np.where(ls_emis_data == 0) ls_emis_no_data_locations = ( np.where(ls_emis_data == self.no_data_value)) # Load the warped ASTER NDVI into memory data_set = gdal.Open(aster_ndvi_warped_name) aster_ndvi_data = data_set.GetRasterBand(1).ReadAsArray(0, 0, x_dim, y_dim) aster_ndvi_gap_locations = np.where(aster_ndvi_data == 0) aster_ndvi_no_data_locations = ( np.where(aster_ndvi_data == self.no_data_value)) # Turn all negative values to zero # Use a realy small value so that we don't have negative zero (-0.0) aster_ndvi_data[aster_ndvi_data < 0.0000001] = 0 # Memory cleanup del data_set if not self.keep_intermediate_data: # Cleanup the temp files since we have them in memory if os.path.exists(ls_emis_warped_name): os.unlink(ls_emis_warped_name) if os.path.exists(aster_ndvi_warped_name): os.unlink(aster_ndvi_warped_name) self.logger.info('Normalizing Landsat and ASTER NDVI') # Normalize Landsat NDVI by max value max_ls_ndvi = ls_ndvi_data.max() self.logger.info('Max LS NDVI {0}'.format(max_ls_ndvi)) ls_ndvi_data = ls_ndvi_data / float(max_ls_ndvi) if self.keep_intermediate_data: self.logger.info('Writing Landsat NDVI NORM MAX raster') util.Geo.generate_raster_file(geotiff_driver, 'internal_landsat_ndvi_norm_max.tif', ls_ndvi_data, x_dim, y_dim, geo_transform, ds_srs.ExportToWkt(), self.no_data_value, gdal.GDT_Float32) # Normalize ASTER NDVI by max value max_aster_ndvi = aster_ndvi_data.max() self.logger.info('Max ASTER NDVI {0}'.format(max_aster_ndvi)) aster_ndvi_data = aster_ndvi_data / float(max_aster_ndvi) if self.keep_intermediate_data: self.logger.info('Writing Aster NDVI NORM MAX raster') util.Geo.generate_raster_file(geotiff_driver, 'internal_aster_ndvi_norm_max.tif', aster_ndvi_data, x_dim, y_dim, geo_transform, ds_srs.ExportToWkt(), self.no_data_value, gdal.GDT_Float32) # Soil - From prototype code variable name ls_emis_final = ((ls_emis_data - 0.975 * aster_ndvi_data) / (1.0 - aster_ndvi_data)) # Memory cleanup del aster_ndvi_data del ls_emis_data # Adjust estimated Landsat EMIS for vegetation and snow, to generate # the final Landsat EMIS data self.logger.info('Adjusting estimated EMIS for vegetation') ls_emis_final = (self.vegetation_coeff * ls_ndvi_data + ls_emis_final * (1.0 - ls_ndvi_data)) # Medium snow self.logger.info('Adjusting estimated EMIS for snow') ls_emis_final[selected_snow_locations] = self.snow_emis_value # Memory cleanup del ls_ndvi_data del selected_snow_locations # Add the fill and scan gaps and ASTER gaps back into the results, # since they may have been lost self.logger.info('Adding fill and data gaps back into the estimated' ' Landsat emissivity results') ls_emis_final[ls_emis_no_data_locations] = self.no_data_value ls_emis_final[ls_emis_gap_locations] = self.no_data_value ls_emis_final[aster_ndvi_no_data_locations] = self.no_data_value ls_emis_final[aster_ndvi_gap_locations] = self.no_data_value ls_emis_final[ndvi_no_data_locations] = self.no_data_value ls_emis_final[ndsi_no_data_locations] = self.no_data_value # Memory cleanup del ls_emis_no_data_locations del ls_emis_gap_locations del aster_ndvi_no_data_locations del aster_ndvi_gap_locations product_id = self.xml_filename.split('.xml')[0] ls_emis_img_filename = ''.join([product_id, '_emis', '.img']) ls_emis_hdr_filename = ''.join([product_id, '_emis', '.hdr']) ls_emis_aux_filename = ''.join([ls_emis_img_filename, '.aux', '.xml']) self.logger.info('Creating {0}'.format(ls_emis_img_filename)) util.Geo.generate_raster_file(envi_driver, ls_emis_img_filename, ls_emis_final, x_dim, y_dim, ds_tmp_transform, ds_tmp_srs.ExportToWkt(), self.no_data_value, gdal.GDT_Float32) self.logger.info('Updating {0}'.format(ls_emis_hdr_filename)) util.Geo.update_envi_header(ls_emis_hdr_filename, self.no_data_value) # Remove the *.aux.xml file generated by GDAL if os.path.exists(ls_emis_aux_filename): os.unlink(ls_emis_aux_filename) self.logger.info('Adding {0} to {1}'.format(ls_emis_img_filename, self.xml_filename)) # Add the estimated Landsat emissivity to the metadata XML espa_xml = metadata_api.parse(self.xml_filename, silence=True) bands = espa_xml.get_bands() sensor_code = product_id[0:3] source_product = 'toa_refl' # Find the TOA Band 1 to use for the specific band details base_band = None for band in bands.band: if band.product == source_product and band.name == 'toa_band1': base_band = band if base_band is None: raise Exception('Failed to find the TOA BLUE band' ' in the input data') emis_band = metadata_api.band(product='lst_temp', source=source_product, name='landsat_emis', category='image', data_type='FLOAT32', nlines=base_band.get_nlines(), nsamps=base_band.get_nsamps(), fill_value=str(self.no_data_value)) emis_band.set_short_name('{0}EMIS'.format(sensor_code)) emis_band.set_long_name('Landsat emissivity estimated from ASTER GED' ' data') emis_band.set_file_name(ls_emis_img_filename) emis_band.set_data_units('Emissivity Coefficient') pixel_size = metadata_api.pixel_size(base_band.pixel_size.x, base_band.pixel_size.x, base_band.pixel_size.units) emis_band.set_pixel_size(pixel_size) valid_range = metadata_api.valid_range(min=0.0, max=1.0) emis_band.set_valid_range(valid_range) # Set the date, but first clean the microseconds off of it production_date = ( datetime.datetime.strptime(datetime.datetime.now(). strftime('%Y-%m-%dT%H:%M:%S'), '%Y-%m-%dT%H:%M:%S')) emis_band.set_production_date(production_date) emis_band.set_app_version(util.Version.app_version()) bands.add_band(emis_band) # Write the XML metadata file out with open(self.xml_filename, 'w') as output_fd: metadata_api.export(output_fd, espa_xml) # Memory cleanup del ls_emis_final self.logger.info('Completed - Estimate Landsat Emissivity')
def generate_product(self): ''' Description: Provides the main processing algorithm for generating the estimated Landsat emissivity product. It produces the final emissivity product. ''' self.logger = logging.getLogger(__name__) self.logger.info('Start - Estimate Landsat Emissivity') try: self.retrieve_metadata_information() except Exception: self.logger.exception('Failed reading input XML metadata file') raise try: self.determine_sensor_specific_coefficients() except Exception: self.logger.exception('Failed determining sensor coefficients') raise # Register all the gdal drivers and choose the GeoTiff for our temp # output gdal.AllRegister() geotiff_driver = gdal.GetDriverByName('GTiff') envi_driver = gdal.GetDriverByName('ENVI') # ==================================================================== # Build NDVI in memory self.logger.info('Building TOA based NDVI band for Landsat data') # NIR ---------------------------------------------------------------- data_set = gdal.Open(self.toa_nir_name) x_dim = data_set.RasterXSize # They are all the same size y_dim = data_set.RasterYSize ls_nir_data = data_set.GetRasterBand(1).ReadAsArray(0, 0, x_dim, y_dim) nir_no_data_locations = np.where(ls_nir_data == self.no_data_value) ls_nir_data = ls_nir_data * self.toa_nir_scale_factor # RED ---------------------------------------------------------------- data_set = gdal.Open(self.toa_red_name) ls_red_data = data_set.GetRasterBand(1).ReadAsArray(0, 0, x_dim, y_dim) red_no_data_locations = np.where(ls_red_data == self.no_data_value) ls_red_data = ls_red_data * self.toa_red_scale_factor # NDVI --------------------------------------------------------------- ls_ndvi_data = ((ls_nir_data - ls_red_data) / (ls_nir_data + ls_red_data)) # Cleanup no data locations ls_ndvi_data[nir_no_data_locations] = self.no_data_value ls_ndvi_data[red_no_data_locations] = self.no_data_value if self.keep_intermediate_data: geo_transform = data_set.GetGeoTransform() ds_srs = osr.SpatialReference() ds_srs.ImportFromWkt(data_set.GetProjection()) # Memory cleanup del ls_red_data del ls_nir_data del nir_no_data_locations del red_no_data_locations # ==================================================================== # Build NDSI in memory self.logger.info('Building TOA based NDSI band for Landsat data') # GREEN -------------------------------------------------------------- data_set = gdal.Open(self.toa_green_name) ls_green_data = data_set.GetRasterBand(1).ReadAsArray( 0, 0, x_dim, y_dim) green_no_data_locations = (np.where( ls_green_data == self.no_data_value)) ls_green_data = ls_green_data * self.toa_green_scale_factor # SWIR1 -------------------------------------------------------------- data_set = gdal.Open(self.toa_swir1_name) ls_swir1_data = data_set.GetRasterBand(1).ReadAsArray( 0, 0, x_dim, y_dim) swir1_no_data_locations = (np.where( ls_swir1_data == self.no_data_value)) ls_swir1_data = ls_swir1_data * self.toa_swir1_scale_factor # Build the Landsat TOA NDSI data self.logger.info('Building TOA based NDSI for Landsat data') ls_ndsi_data = ((ls_green_data - ls_swir1_data) / (ls_green_data + ls_swir1_data)) # Cleanup no data locations ls_ndsi_data[green_no_data_locations] = self.no_data_value # Cleanup no data locations ls_ndsi_data[swir1_no_data_locations] = self.no_data_value # Memory cleanup del ls_green_data del ls_swir1_data del green_no_data_locations del swir1_no_data_locations # Save for the output products ds_tmp_srs = osr.SpatialReference() ds_tmp_srs.ImportFromWkt(data_set.GetProjection()) ds_tmp_transform = data_set.GetGeoTransform() # Memory cleanup del data_set # Save the locations for the specfied snow pixels self.logger.info('Determine snow pixel locations') selected_snow_locations = np.where(ls_ndsi_data > 0.4) # Save ndvi and ndsi no data locations ndvi_no_data_locations = np.where(ls_ndvi_data == self.no_data_value) ndsi_no_data_locations = np.where(ls_ndsi_data == self.no_data_value) # Memory cleanup del ls_ndsi_data # Turn all negative values to zero # Use a realy small value so that we don't have negative zero (-0.0) ls_ndvi_data[ls_ndvi_data < 0.0000001] = 0 if self.keep_intermediate_data: self.logger.info('Writing Landsat NDVI raster') util.Geo.generate_raster_file(geotiff_driver, 'internal_landsat_ndvi.tif', ls_ndvi_data, x_dim, y_dim, geo_transform, ds_srs.ExportToWkt(), self.no_data_value, gdal.GDT_Float32) # Build the estimated Landsat EMIS data from the ASTER GED data and # warp it to the Landsat scenes projection and image extents # For convenience the ASTER NDVI is also extracted and warped to the # Landsat scenes projection and image extents self.logger.info('Build thermal emissivity band and' ' retrieve ASTER NDVI') (ls_emis_warped_name, aster_ndvi_warped_name) = self.build_ls_emis_data(geotiff_driver) # Load the warped estimated Landsat EMIS into memory data_set = gdal.Open(ls_emis_warped_name) ls_emis_data = data_set.GetRasterBand(1).ReadAsArray( 0, 0, x_dim, y_dim) ls_emis_gap_locations = np.where(ls_emis_data == 0) ls_emis_no_data_locations = (np.where( ls_emis_data == self.no_data_value)) # Load the warped ASTER NDVI into memory data_set = gdal.Open(aster_ndvi_warped_name) aster_ndvi_data = data_set.GetRasterBand(1).ReadAsArray( 0, 0, x_dim, y_dim) aster_ndvi_gap_locations = np.where(aster_ndvi_data == 0) aster_ndvi_no_data_locations = (np.where( aster_ndvi_data == self.no_data_value)) # Turn all negative values to zero # Use a realy small value so that we don't have negative zero (-0.0) aster_ndvi_data[aster_ndvi_data < 0.0000001] = 0 # Memory cleanup del data_set if not self.keep_intermediate_data: # Cleanup the temp files since we have them in memory if os.path.exists(ls_emis_warped_name): os.unlink(ls_emis_warped_name) if os.path.exists(aster_ndvi_warped_name): os.unlink(aster_ndvi_warped_name) self.logger.info('Normalizing Landsat and ASTER NDVI') # Normalize Landsat NDVI by max value max_ls_ndvi = ls_ndvi_data.max() self.logger.info('Max LS NDVI {0}'.format(max_ls_ndvi)) ls_ndvi_data = ls_ndvi_data / float(max_ls_ndvi) if self.keep_intermediate_data: self.logger.info('Writing Landsat NDVI NORM MAX raster') util.Geo.generate_raster_file( geotiff_driver, 'internal_landsat_ndvi_norm_max.tif', ls_ndvi_data, x_dim, y_dim, geo_transform, ds_srs.ExportToWkt(), self.no_data_value, gdal.GDT_Float32) # Normalize ASTER NDVI by max value max_aster_ndvi = aster_ndvi_data.max() self.logger.info('Max ASTER NDVI {0}'.format(max_aster_ndvi)) aster_ndvi_data = aster_ndvi_data / float(max_aster_ndvi) if self.keep_intermediate_data: self.logger.info('Writing Aster NDVI NORM MAX raster') util.Geo.generate_raster_file(geotiff_driver, 'internal_aster_ndvi_norm_max.tif', aster_ndvi_data, x_dim, y_dim, geo_transform, ds_srs.ExportToWkt(), self.no_data_value, gdal.GDT_Float32) # Soil - From prototype code variable name self.logger.info('Calculating EMIS Final') with np.errstate(divide='ignore'): ls_emis_final = ((ls_emis_data - 0.975 * aster_ndvi_data) / (1.0 - aster_ndvi_data)) # Memory cleanup del aster_ndvi_data del ls_emis_data # Adjust estimated Landsat EMIS for vegetation and snow, to generate # the final Landsat EMIS data self.logger.info('Adjusting estimated EMIS for vegetation') ls_emis_final = (self.vegetation_coeff * ls_ndvi_data + ls_emis_final * (1.0 - ls_ndvi_data)) # Medium snow self.logger.info('Adjusting estimated EMIS for snow') ls_emis_final[selected_snow_locations] = self.snow_emis_value # Memory cleanup del ls_ndvi_data del selected_snow_locations # Add the fill and scan gaps and ASTER gaps back into the results, # since they may have been lost self.logger.info('Adding fill and data gaps back into the estimated' ' Landsat emissivity results') ls_emis_final[ls_emis_no_data_locations] = self.no_data_value ls_emis_final[ls_emis_gap_locations] = self.no_data_value ls_emis_final[aster_ndvi_no_data_locations] = self.no_data_value ls_emis_final[aster_ndvi_gap_locations] = self.no_data_value ls_emis_final[ndvi_no_data_locations] = self.no_data_value ls_emis_final[ndsi_no_data_locations] = self.no_data_value # Memory cleanup del ls_emis_no_data_locations del ls_emis_gap_locations del aster_ndvi_no_data_locations del aster_ndvi_gap_locations product_id = self.xml_filename.split('.xml')[0] ls_emis_img_filename = ''.join([product_id, '_emis', '.img']) ls_emis_hdr_filename = ''.join([product_id, '_emis', '.hdr']) ls_emis_aux_filename = ''.join([ls_emis_img_filename, '.aux', '.xml']) self.logger.info('Creating {0}'.format(ls_emis_img_filename)) util.Geo.generate_raster_file(envi_driver, ls_emis_img_filename, ls_emis_final, x_dim, y_dim, ds_tmp_transform, ds_tmp_srs.ExportToWkt(), self.no_data_value, gdal.GDT_Float32) self.logger.info('Updating {0}'.format(ls_emis_hdr_filename)) util.Geo.update_envi_header(ls_emis_hdr_filename, self.no_data_value) # Remove the *.aux.xml file generated by GDAL if os.path.exists(ls_emis_aux_filename): os.unlink(ls_emis_aux_filename) self.logger.info('Adding {0} to {1}'.format(ls_emis_img_filename, self.xml_filename)) # Add the estimated Landsat emissivity to the metadata XML espa_xml = metadata_api.parse(self.xml_filename, silence=True) bands = espa_xml.get_bands() sensor_code = product_id[0:3] source_product = 'toa_refl' # Find the TOA Band 1 to use for the specific band details base_band = None for band in bands.band: if band.product == source_product and band.name == 'toa_band1': base_band = band if base_band is None: raise Exception('Failed to find the TOA BLUE band' ' in the input data') emis_band = metadata_api.band(product='lst_temp', source=source_product, name='landsat_emis', category='image', data_type='FLOAT32', nlines=base_band.get_nlines(), nsamps=base_band.get_nsamps(), fill_value=str(self.no_data_value)) emis_band.set_short_name('{0}EMIS'.format(sensor_code)) emis_band.set_long_name('Landsat emissivity estimated from ASTER GED' ' data') emis_band.set_file_name(ls_emis_img_filename) emis_band.set_data_units('Emissivity Coefficient') pixel_size = metadata_api.pixel_size(base_band.pixel_size.x, base_band.pixel_size.x, base_band.pixel_size.units) emis_band.set_pixel_size(pixel_size) emis_band.set_resample_method('none') valid_range = metadata_api.valid_range(min=0.0, max=1.0) emis_band.set_valid_range(valid_range) # Set the date, but first clean the microseconds off of it production_date = (datetime.datetime.strptime( datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S'), '%Y-%m-%dT%H:%M:%S')) emis_band.set_production_date(production_date) emis_band.set_app_version(util.Version.app_version()) bands.add_band(emis_band) # Write the XML metadata file out with open(self.xml_filename, 'w') as output_fd: metadata_api.export(output_fd, espa_xml) # Memory cleanup del ls_emis_final self.logger.info('Completed - Estimate Landsat Emissivity')
band.set_short_name("LT5DN") band.set_long_name("band 1 digital numbers") band.set_file_name("LT50460282002042EDC01_B1.img") pixel_size = metadata_api.pixel_size("30.000000", 30, "meters") band.set_pixel_size(pixel_size) band.set_data_units("digital numbers") valid_range = metadata_api.valid_range(min="1", max=255) band.set_valid_range(valid_range) toa_reflectance = metadata_api.toa_reflectance(gain=1.448, bias="-4.28819") band.set_toa_reflectance(toa_reflectance) band.set_app_version("LPGS_12.3.1") production_date = \ datetime.datetime.strptime ('2014-01-13T06:49:56', '%Y-%m-%dT%H:%M:%S') band.set_production_date(production_date) bands.add_band(band) # Add the bands to the top-level object xml.set_bands(bands) # Create the output file **WITH** validation f = open('test-2-with-validation.xml', 'w') metadata_api.export(f, xml) f.close()
xmlns='http://espa.cr.usgs.gov/v1.0', xmlns_xsi='http://www.w3.org/2001/XMLSchema-instance', schema_uri= 'http://espa.cr.usgs.gov/static/schema/espa_internal_metadata_v1_0.xsd' ) except Exception, e: print "Validation Error: %s" % e # Export with validation f = open('exported_1.xml', 'w') # Create the file and specify the namespace/schema metadata_api.export( f, xml, xmlns="http://espa.cr.usgs.gov/v1.0", xmlns_xsi="http://www.w3.org/2001/XMLSchema-instance", schema_uri= "http://espa.cr.usgs.gov/static/schema/espa_internal_metadata_v1_0.xsd") f.close() # This method does not validate the schema f = open('exported_2.xml', 'w') ns_def = metadata_api.build_ns_def( xmlns='http://espa.cr.usgs.gov/v1.0', xmlns_xsi='http://www.w3.org/2001/XMLSchema-instance', schema_uri= 'http://espa.cr.usgs.gov/static/schema/espa_internal_metadata_v1_0.xsd') xml.export(f, 0, namespacedef_=ns_def) f.close()
def generate_data(self): ''' Description: Provides the main processing algorithm for building the Land Surface Temperature product. It produces the final LST product. ''' try: self.retrieve_metadata_information() except Exception: self.logger.exception('Failed reading input XML metadata file') raise # Register all the gdal drivers and choose the ENVI for our output gdal.AllRegister() envi_driver = gdal.GetDriverByName('ENVI') # Read the bands into memory # Landsat Radiance at sensor for thermal band self.logger.info('Loading intermediate thermal band data') ds = gdal.Open(self.thermal_name) x_dim = ds.RasterXSize # They are all the same size y_dim = ds.RasterYSize thermal_data = ds.GetRasterBand(1).ReadAsArray(0, 0, x_dim, y_dim) # Atmospheric transmittance self.logger.info('Loading intermediate transmittance band data') ds = gdal.Open(self.transmittance_name) trans_data = ds.GetRasterBand(1).ReadAsArray(0, 0, x_dim, y_dim) # Atmospheric path radiance - upwelled radiance self.logger.info('Loading intermediate upwelled band data') ds = gdal.Open(self.upwelled_name) upwelled_data = ds.GetRasterBand(1).ReadAsArray(0, 0, x_dim, y_dim) self.logger.info('Calculating surface radiance') # Surface radiance surface_radiance = (thermal_data - upwelled_data) / trans_data # Fix the no data locations no_data_locations = np.where(thermal_data == self.no_data_value) surface_radiance[no_data_locations] = self.no_data_value no_data_locations = np.where(trans_data == self.no_data_value) surface_radiance[no_data_locations] = self.no_data_value no_data_locations = np.where(upwelled_data == self.no_data_value) surface_radiance[no_data_locations] = self.no_data_value # Memory cleanup del (thermal_data) del (trans_data) del (upwelled_data) del (no_data_locations) # Downwelling sky irradiance self.logger.info('Loading intermediate downwelled band data') ds = gdal.Open(self.downwelled_name) downwelled_data = ds.GetRasterBand(1).ReadAsArray(0, 0, x_dim, y_dim) # Landsat emissivity estimated from ASTER GED data self.logger.info('Loading intermediate emissivity band data') ds = gdal.Open(self.emissivity_name) emissivity_data = ds.GetRasterBand(1).ReadAsArray(0, 0, x_dim, y_dim) # Save for the output product ds_srs = osr.SpatialReference() ds_srs.ImportFromWkt(ds.GetProjection()) ds_transform = ds.GetGeoTransform() # Memory cleanup del (ds) # Estimate Earth-emitted radiance by subtracting off the reflected # downwelling component radiance = (surface_radiance - (1.0 - emissivity_data) * downwelled_data) # Account for surface emissivity to get Plank emitted radiance self.logger.info('Calculating Plank emitted radiance') radiance_emitted = radiance / emissivity_data # Fix the no data locations no_data_locations = np.where(surface_radiance == self.no_data_value) radiance_emitted[no_data_locations] = self.no_data_value no_data_locations = np.where(downwelled_data == self.no_data_value) radiance_emitted[no_data_locations] = self.no_data_value no_data_locations = np.where(emissivity_data == self.no_data_value) radiance_emitted[no_data_locations] = self.no_data_value # Memory cleanup del (downwelled_data) del (emissivity_data) del (surface_radiance) del (radiance) del (no_data_locations) # Use Brightness Temperature LUT to get skin temperature # Read the correct one for what we are processing if self.satellite == 'LANDSAT_7': self.logger.info('Using Landsat 7 Brightness Temperature LUT') bt_name = 'L7_Brightness_Temperature_LUT.txt' elif self.satellite == 'LANDSAT_5': self.logger.info('Using Landsat 5 Brightness Temperature LUT') bt_name = 'L5_Brightness_Temperature_LUT.txt' bt_data = np.loadtxt(os.path.join(self.lst_data_dir, bt_name), dtype=float, delimiter=' ') bt_radiance_LUT = bt_data[:, 1] bt_temp_LUT = bt_data[:, 0] self.logger.info('Generating LST results') lst_data = np.interp(radiance_emitted, bt_radiance_LUT, bt_temp_LUT) # Scale the result and convert it to an int16 lst_data = lst_data * MULT_FACTOR lst_fata = lst_data.astype(np.int16) # Add the fill and scan gaps back into the results, since they may # have been lost self.logger.info('Adding fill and data gaps back into the Land' ' Surface Temperature results') # Fix the no data locations no_data_locations = np.where(radiance_emitted == self.no_data_value) lst_data[no_data_locations] = self.no_data_value # Memory cleanup del (radiance_emitted) del (no_data_locations) product_id = self.xml_filename.split('.xml')[0] lst_img_filename = ''.join([product_id, '_lst', '.img']) lst_hdr_filename = ''.join([product_id, '_lst', '.hdr']) lst_aux_filename = ''.join([lst_img_filename, '.aux', '.xml']) self.logger.info('Creating {0}'.format(lst_img_filename)) util.Geo.generate_raster_file(envi_driver, lst_img_filename, lst_data, x_dim, y_dim, ds_transform, ds_srs.ExportToWkt(), self.no_data_value, gdal.GDT_Int16) self.logger.info('Updating {0}'.format(lst_hdr_filename)) util.Geo.update_envi_header(lst_hdr_filename, self.no_data_value) # Memory cleanup del (ds_srs) del (ds_transform) # Remove the *.aux.xml file generated by GDAL if os.path.exists(lst_aux_filename): os.unlink(lst_aux_filename) self.logger.info('Adding {0} to {1}'.format(lst_img_filename, self.xml_filename)) # Add the estimated Land Surface Temperature product to the metadata espa_xml = metadata_api.parse(self.xml_filename, silence=True) bands = espa_xml.get_bands() sensor_code = product_id[0:3] # Find the TOA Band 1 to use for the specific band details base_band = None for band in bands.band: if band.product == 'toa_refl' and band.name == 'toa_band1': base_band = band if base_band is None: raise Exception('Failed to find the TOA BLUE band' ' in the input data') lst_band = metadata_api.band(product='lst', source='toa_refl', name='land_surface_temperature', category='image', data_type='INT16', scale_factor=SCALE_FACTOR, add_offset=0, nlines=base_band.get_nlines(), nsamps=base_band.get_nsamps(), fill_value=str(self.no_data_value)) lst_band.set_short_name('{0}LST'.format(sensor_code)) lst_band.set_long_name('Land Surface Temperature') lst_band.set_file_name(lst_img_filename) lst_band.set_data_units('temperature (kelvin)') pixel_size = metadata_api.pixel_size(base_band.pixel_size.x, base_band.pixel_size.x, base_band.pixel_size.units) lst_band.set_pixel_size(pixel_size) valid_range = metadata_api.valid_range(min=1500, max=3730) lst_band.set_valid_range(valid_range) # Set the date, but first clean the microseconds off of it production_date = ( datetime.datetime.strptime(datetime.datetime.now(). strftime('%Y-%m-%dT%H:%M:%S'), '%Y-%m-%dT%H:%M:%S')) lst_band.set_production_date(production_date) lst_band.set_app_version(util.Version.app_version()) bands.add_band(lst_band) # Write the XML metadata file out with open(self.xml_filename, 'w') as fd: metadata_api.export(fd, espa_xml) # Memory cleanup del (lst_band) del (bands) del (espa_xml) del (lst_data)
parser = ArgumentParser (description=description) parser.add_argument ('--xml-file', action='store', dest='xml_file', required=True, help="ESPA XML file to validate") # Parse the command line arguments args = parser.parse_args() xml = metadata_api.parse (args.xml_file, silence=True) # Export with validation f = open ('val_01-' + args.xml_file, 'w') # Create the file and specify the namespace/schema metadata_api.export (f, xml) f.close() # LXML - Validation Example try: f = open ('../../../htdocs/schema/espa_internal_metadata_v1_0.xsd') schema_root = etree.parse (f) f.close() schema = etree.XMLSchema (schema_root) tree = etree.parse ('val_01-' + args.xml_file) schema.assertValid (tree) except Exception, e: print "lxml Validation Error: %s" % e
# Validate the schema try: metadata_api.validate_xml(xml, xmlns='http://espa.cr.usgs.gov/v1.0', xmlns_xsi='http://www.w3.org/2001/XMLSchema-instance', schema_uri='http://espa.cr.usgs.gov/static/schema/espa_internal_metadata_v1_0.xsd') except Exception, e: print "Validation Error: %s" % e # Export with validation f = open('exported_1.xml', 'w') # Create the file and specify the namespace/schema metadata_api.export(f, xml, xmlns="http://espa.cr.usgs.gov/v1.0", xmlns_xsi="http://www.w3.org/2001/XMLSchema-instance", schema_uri="http://espa.cr.usgs.gov/static/schema/espa_internal_metadata_v1_0.xsd") f.close() # This method does not validate the schema f = open('exported_2.xml', 'w') ns_def = metadata_api.build_ns_def( xmlns='http://espa.cr.usgs.gov/v1.0', xmlns_xsi='http://www.w3.org/2001/XMLSchema-instance', schema_uri='http://espa.cr.usgs.gov/static/schema/espa_internal_metadata_v1_0.xsd') xml.export(f, 0, namespacedef_=ns_def) f.close() # LXML - Validation Example try:
def createXML(self, scene_xml_file=None, output_xml_file=None, start_year=None, end_year=None, fill_value=None, imgfile=None, log_handler=None): """Creates an XML file for the products produced by runAnnualBurnSummaries. Description: routine to create the XML file for the burned area summary bands. The sample scene-based XML file will be used as the basis for the projection information for the output XML file. The image size, extents, etc. will need to be updated, as will the band information. History: Created on May 12, 2014 by Gail Schmidt, USGS/EROS LSRD Project Args: scene_xml_file - scene-based XML file to be used as the base XML information for the projection metadata. output_xml_file - name of the XML file to be written start_year - starting year of the scenes to process end_year - ending year of the scenes to process fill_value - fill or nodata value for this dataset imgfile - name of burned area image file with associated ENVI header which can be used to obtain the extents and geographic information for these products log_handler - handler for the logging information Returns: ERROR - error creating the XML file SUCCESS - successful creation of the XML file """ # parse the scene-based XML file, just as a basis for the output XML # file. the global attributes will be similar, but the extents and # size of the image will be different. the bands will be based on the # bands that are output from this routine. xml = metadata_api.parse(scene_xml_file, silence=True) meta_bands = xml.get_bands() meta_global = xml.get_global_metadata() # update the global information meta_global.set_data_provider("USGS/EROS") meta_global.set_satellite("LANDSAT") meta_global.set_instrument("combination") del (meta_global.acquisition_date) meta_global.set_acquisition_date(None) # open the image file to obtain the geospatial and spatial reference # information ds = gdal.Open(imgfile) if ds is None: msg = "GDAL failed to open %s" % imgfile logIt(msg, log_handler) return ERROR ds_band = ds.GetRasterBand(1) if ds_band is None: msg = "GDAL failed to get the first band in %s" % imgfile logIt(msg, log_handler) return ERROR nlines = float(ds_band.YSize) nsamps = float(ds_band.XSize) nlines_int = ds_band.YSize nsamps_int = ds_band.XSize del (ds_band) ds_transform = ds.GetGeoTransform() if ds_transform is None: msg = "GDAL failed to get the geographic transform information " \ "from %s" % imgfile logIt(msg, log_handler) return ERROR ds_srs = osr.SpatialReference() if ds_srs is None: msg = "GDAL failed to get the spatial reference information " \ "from %s" % imgfile logIt(msg, log_handler) return ERROR ds_srs.ImportFromWkt(ds.GetProjection()) del (ds) # get the UL and LR center of pixel map coordinates (map_ul_x, map_ul_y) = convert_imageXY_to_mapXY(0.5, 0.5, ds_transform) (map_lr_x, map_lr_y) = convert_imageXY_to_mapXY(nsamps - 0.5, nlines - 0.5, ds_transform) # update the UL and LR projection corners along with the origin of the # corners, for the center of the pixel (global projection information) for mycorner in meta_global.projection_information.corner_point: if mycorner.location == 'UL': mycorner.set_x(map_ul_x) mycorner.set_y(map_ul_y) if mycorner.location == 'LR': mycorner.set_x(map_lr_x) mycorner.set_y(map_lr_y) meta_global.projection_information.set_grid_origin("CENTER") # update the UL and LR latitude and longitude coordinates, using the # center of the pixel srs_lat_lon = ds_srs.CloneGeogCS() coord_tf = osr.CoordinateTransformation(ds_srs, srs_lat_lon) for mycorner in meta_global.corner: if mycorner.location == 'UL': (lon, lat, height) = \ coord_tf.TransformPoint (map_ul_x, map_ul_y) mycorner.set_longitude(lon) mycorner.set_latitude(lat) if mycorner.location == 'LR': (lon, lat, height) = \ coord_tf.TransformPoint (map_lr_x, map_lr_y) mycorner.set_longitude(lon) mycorner.set_latitude(lat) # determine the bounding coordinates; initialize using the UL and LR # then work around the scene edges # UL (map_x, map_y) = convert_imageXY_to_mapXY(0.0, 0.0, ds_transform) (ul_lon, ul_lat, height) = coord_tf.TransformPoint(map_x, map_y) # LR (map_x, map_y) = convert_imageXY_to_mapXY(nsamps, nlines, ds_transform) (lr_lon, lr_lat, height) = coord_tf.TransformPoint(map_x, map_y) # find the min and max values accordingly, for initialization west_lon = min(ul_lon, lr_lon) east_lon = max(ul_lon, lr_lon) north_lat = max(ul_lat, lr_lat) south_lat = min(ul_lat, lr_lat) # traverse the boundaries of the image to determine the bounding # coords; traverse one extra line and sample to get the the outer # extents of the image vs. just the UL of the outer edge. # top and bottom edges for samp in range(0, nsamps_int + 1): # top edge (map_x, map_y) = convert_imageXY_to_mapXY(samp, 0.0, ds_transform) (top_lon, top_lat, height) = coord_tf.TransformPoint(map_x, map_y) # lower edge (map_x, map_y) = convert_imageXY_to_mapXY(samp, nlines, ds_transform) (low_lon, low_lat, height) = coord_tf.TransformPoint(map_x, map_y) # update the min and max values west_lon = min(top_lon, low_lon, west_lon) east_lon = max(top_lon, low_lon, east_lon) north_lat = max(top_lat, low_lat, north_lat) south_lat = min(top_lat, low_lat, south_lat) # left and right edges for line in range(0, nlines_int + 1): # left edge (map_x, map_y) = convert_imageXY_to_mapXY(0.0, line, ds_transform) (left_lon, left_lat, height) = coord_tf.TransformPoint(map_x, map_y) # right edge (map_x, map_y) = convert_imageXY_to_mapXY(nsamps, line, ds_transform) (right_lon, right_lat, height) = coord_tf.TransformPoint(map_x, map_y) # update the min and max values west_lon = min(left_lon, right_lon, west_lon) east_lon = max(left_lon, right_lon, east_lon) north_lat = max(left_lat, right_lat, north_lat) south_lat = min(left_lat, right_lat, south_lat) # update the XML bounding_coords = meta_global.get_bounding_coordinates() bounding_coords.set_west(west_lon) bounding_coords.set_east(east_lon) bounding_coords.set_north(north_lat) bounding_coords.set_south(south_lat) del (ds_transform) del (ds_srs) # clear some of the global information that doesn't apply for these # products del (meta_global.scene_center_time) meta_global.set_scene_center_time(None) del (meta_global.lpgs_metadata_file) meta_global.set_lpgs_metadata_file(None) del (meta_global.orientation_angle) meta_global.set_orientation_angle(None) del (meta_global.level1_production_date) meta_global.set_level1_production_date(None) # clear the solar angles del (meta_global.solar_angles) meta_global.set_solar_angles(None) # save the first band and then wipe the bands out so that new bands # can be added for the burned area bands myband_save = meta_bands.band[0] del (meta_bands.band) meta_bands.band = [] # create the band information; there are 4 output products per year # for the burned area dataset; add enough bands to cover the products # and years # 1. first date a burned area was observed (burned_area) # 2. number of times burn was observed (burn_count) # 3. number of good looks (good_looks_count) # 4. maximum probability for burned area (max_burn_prob) nproducts = 4 nyears = end_year - start_year + 1 nbands = nproducts * nyears for i in range(0, nbands): # add the new band myband = metadata_api.band() meta_bands.band.append(myband) # how many bands are there in the new XML file num_scene_bands = len(meta_bands.band) print "New XML file has %d bands" % num_scene_bands # loop through the products and years to create the band metadata band_count = 0 for product in range(1, nproducts + 1): for year in range(start_year, end_year + 1): myband = meta_bands.band[band_count] myband.set_product("burned_area") myband.set_short_name("LNDBA") myband.set_data_type("INT16") myband.set_pixel_size(myband_save.get_pixel_size()) myband.set_fill_value(fill_value) myband.set_nlines(nlines) myband.set_nsamps(nsamps) myband.set_app_version(self.burned_area_version) production_date = time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime()) myband.set_production_date ( \ datetime_.datetime.strptime(production_date, '%Y-%m-%dT%H:%M:%S')) # clear some of the band-specific fields that don't apply for # this product (see metadata_api.py for the full list of # band-related values under class band) del (myband.source) myband.set_source(None) del (myband.saturate_value) myband.set_saturate_value(None) del (myband.scale_factor) myband.set_scale_factor(None) del (myband.add_offset) myband.set_add_offset(None) del (myband.radiance) myband.set_radiance(None) del (myband.reflectance) myband.set_reflectance(None) del (myband.thermal_const) myband.set_thermal_const(None) del (myband.bitmap_description) myband.set_bitmap_description(None) del (myband.class_values) myband.set_class_values(None) del (myband.qa_description) myband.set_qa_description(None) del (myband.calibrated_nt) myband.set_calibrated_nt(None) # handle the band-specific differences valid_range = metadata_api.valid_range() if product == 1: name = "burned_area_%d" % year long_name = "first DOY a burn was observed" file_name = "burned_area_%d.img" % year category = "image" data_units = "day of year" valid_range.min = 0 valid_range.max = 366 qa_description = "0: no burn observed" elif product == 2: name = "burn_count_%d" % year long_name = "number of times a burn was observed" file_name = "burn_count_%d.img" % year category = "image" data_units = "count" valid_range.min = 0 valid_range.max = 366 qa_description = "0: no burn observed" elif product == 3: name = "good_looks_count_%d" % year long_name = "number of good looks (pixels with good QA)" file_name = "good_looks_count_%d.img" % year category = "qa" data_units = "count" valid_range.min = 0 valid_range.max = 366 qa_description = "0: no valid pixels (water, cloud, " \ "snow, etc.)" elif product == 4: name = "max_burn_prob_%d" % year long_name = "maximum probability for burned area" file_name = "max_burn_prob_%d.img" % year category = "image" data_units = "probability" valid_range.min = 0 valid_range.max = 100 qa_description = "-9998: bad QA (water, cloud, snow, etc.)" myband.set_name(name) myband.set_long_name(long_name) myband.set_file_name(file_name) myband.set_category(category) myband.set_data_units(data_units) myband.set_valid_range(valid_range) myband.set_qa_description(qa_description) # increment the band counter band_count += 1 # end for year # end for nproducts # write out a the XML file after validation # call the export with validation fd = open(output_xml_file, 'w') if fd == None: msg = "Unable to open the output XML file (%s) for writing." % \ output_xml_file logIt(msg, log_handler) return ERROR metadata_api.export(fd, xml) fd.flush() fd.close() return SUCCESS