def test_6s_example1(self): # Implements Example_In_1.txt from the 6S distribution in Py6S # Tests against manual run of that file s = SixS() s.geometry = Geometry.User() s.geometry.solar_z = 40 s.geometry.solar_a = 100 s.geometry.view_z = 45 s.geometry.view_a = 50 s.geometry.month = 7 s.geometry.day = 23 s.atmos_profile = AtmosProfile.UserWaterAndOzone(3.0, 3.5) s.aero_profile = AeroProfile.User(dust=0.25, water=0.25, oceanic=0.25, soot=0.25) s.aot550 = 0.5 s.altitudes.set_target_custom_altitude(0.2) s.altitudes.set_sensor_custom_altitude(3.3, aot=0.25) s.wavelength = Wavelength(PredefinedWavelengths.AVHRR_NOAA9_B1) s.ground_reflectance = GroundReflectance.HeterogeneousLambertian( 0.5, GroundReflectance.ClearWater, GroundReflectance.GreenVegetation) s.atmos_corr = AtmosCorr.AtmosCorrBRDFFromReflectance(0.1) s.run() self.assertAlmostEqual(s.outputs.apparent_radiance, 12.749, delta=0.002)
def __init__(self, bandnums, wavelengths, geometry, date_time, sensor=None): """ Run SixS atmospheric model using Py6S """ start = datetime.datetime.now() VerboseOut('Running atmospheric model (6S)', 2) s = SixS() # Geometry s.geometry = Geometry.User() s.geometry.from_time_and_location(geometry['lat'], geometry['lon'], str(date_time), geometry['zenith'], geometry['azimuth']) s.altitudes = Altitudes() s.altitudes.set_target_sea_level() s.altitudes.set_sensor_satellite_level() doy = (date_time - datetime.datetime(date_time.year, 1, 1)).days + 1 # Atmospheric profile s.atmos_profile = atmospheric_model(doy, geometry['lat']) # Aerosols # TODO - dynamically adjust AeroProfile? s.aero_profile = AeroProfile.PredefinedType(AeroProfile.Continental) self.aod = aodData.get_aod(geometry['lat'], geometry['lon'], date_time.date()) s.aot550 = self.aod[1] # Other settings s.ground_reflectance = GroundReflectance.HomogeneousLambertian( GroundReflectance.GreenVegetation) s.atmos_corr = AtmosCorr.AtmosCorrLambertianFromRadiance(1.0) # Used for testing try: stdout = sys.stdout funcs = { 'LT5': SixSHelpers.Wavelengths.run_landsat_tm, 'LT7': SixSHelpers.Wavelengths.run_landsat_etm, # LC8 doesn't seem to work #'LC8': SixSHelpers.Wavelengths.run_landsat_oli } if sensor in funcs.keys(): sys.stdout = open(os.devnull, 'w') wvlens, outputs = funcs[sensor](s) sys.stdout = stdout else: # Use wavelengths outputs = [] for wv in wavelengths: s.wavelength = Wavelength(wv[0], wv[1]) s.run() outputs.append(s.outputs) except Exception, e: sys.stdout = stdout raise AtmCorrException("Error running 6S: %s" % e)
def __init__(self, bandnums, wavelengths, geometry, date_time, sensor=None): """ Run SixS atmospheric model using Py6S """ start = datetime.datetime.now() verbose_out('Running atmospheric model (6S)', 2) s = SixS() # Geometry s.geometry = Geometry.User() s.geometry.from_time_and_location(geometry['lat'], geometry['lon'], str(date_time), geometry['zenith'], geometry['azimuth']) s.altitudes = Altitudes() s.altitudes.set_target_sea_level() s.altitudes.set_sensor_satellite_level() doy = (date_time - datetime.datetime(date_time.year, 1, 1)).days + 1 # Atmospheric profile s.atmos_profile = atmospheric_model(doy, geometry['lat']) # Aerosols # TODO - dynamically adjust AeroProfile? s.aero_profile = AeroProfile.PredefinedType(AeroProfile.Continental) self.aod = aodData.get_aod( geometry['lat'], geometry['lon'], date_time.date() ) # sixs throws IEEE_UNDERFLOW_FLAG IEEE_DENORMAL for small aod. # and if using a predefined AeroProfile, visible or aot550 must be # specified. Small here was determined emprically on my laptop, and # visible = 1000 km is essentially setting the visibility to infinite. if self.aod[1] < 0.0103: s.visible = 1000 else: s.aot550 = self.aod[1] # Other settings s.ground_reflectance = GroundReflectance.HomogeneousLambertian(GroundReflectance.GreenVegetation) s.atmos_corr = AtmosCorr.AtmosCorrLambertianFromRadiance(1.0) # Used for testing funcs = { 'LT5': SixSHelpers.Wavelengths.run_landsat_tm, 'LT7': SixSHelpers.Wavelengths.run_landsat_etm, # LC8 doesn't seem to work #'LC8': SixSHelpers.Wavelengths.run_landsat_oli } if sensor in funcs.keys(): saved_stdout = sys.stdout try: sys.stdout = open(os.devnull, 'w') wvlens, outputs = funcs[sensor](s) finally: sys.stdout = saved_stdout else: # Use wavelengths outputs = [] for wv in wavelengths: s.wavelength = Wavelength(wv[0], wv[1]) s.run() outputs.append(s.outputs) self.results = {} verbose_out("{:>6} {:>8}{:>8}{:>8}".format('Band', 'T', 'Lu', 'Ld'), 4) for b, out in enumerate(outputs): t = out.trans['global_gas'].upward Lu = out.atmospheric_intrinsic_radiance Ld = (out.direct_solar_irradiance + out.diffuse_solar_irradiance + out.environmental_irradiance) / numpy.pi self.results[bandnums[b]] = [t, Lu, Ld] verbose_out("{:>6}: {:>8.3f}{:>8.2f}{:>8.2f}".format(bandnums[b], t, Lu, Ld), 4) verbose_out('Ran atmospheric model in %s' % str(datetime.datetime.now() - start), 2)
def __init__(self, bandnums, wavelengths, geometry, date_time, sensor=None): """ Run SixS atmospheric model using Py6S """ start = datetime.datetime.now() VerboseOut('Running atmospheric model (6S)', 2) s = SixS() # Geometry s.geometry = Geometry.User() s.geometry.from_time_and_location(geometry['lat'], geometry['lon'], str(date_time), geometry['zenith'], geometry['azimuth']) s.altitudes = Altitudes() s.altitudes.set_target_sea_level() s.altitudes.set_sensor_satellite_level() doy = (date_time - datetime.datetime(date_time.year, 1, 1)).days + 1 # Atmospheric profile s.atmos_profile = atmospheric_model(doy, geometry['lat']) # Aerosols # TODO - dynamically adjust AeroProfile? s.aero_profile = AeroProfile.PredefinedType(AeroProfile.Continental) self.aod = aodData.get_aod(geometry['lat'], geometry['lon'], date_time.date()) s.aot550 = self.aod[1] # Other settings s.ground_reflectance = GroundReflectance.HomogeneousLambertian(GroundReflectance.GreenVegetation) s.atmos_corr = AtmosCorr.AtmosCorrLambertianFromRadiance(1.0) # Used for testing try: stdout = sys.stdout funcs = { 'LT5': SixSHelpers.Wavelengths.run_landsat_tm, 'LT7': SixSHelpers.Wavelengths.run_landsat_etm, # LC8 doesn't seem to work #'LC8': SixSHelpers.Wavelengths.run_landsat_oli } if sensor in funcs.keys(): sys.stdout = open(os.devnull, 'w') wvlens, outputs = funcs[sensor](s) sys.stdout = stdout else: # Use wavelengths outputs = [] for wv in wavelengths: s.wavelength = Wavelength(wv[0], wv[1]) s.run() outputs.append(s.outputs) except Exception, e: sys.stdout = stdout raise AtmCorrException("Error running 6S: %s" % e)
def test_simple_radiosonde_import(self): s = SixS() s.altitudes.set_sensor_satellite_level() s.altitudes.set_target_sea_level() try: s.atmos_profile = SixSHelpers.Radiosonde.import_uow_radiosonde_data( "http://weather.uwyo.edu/cgi-bin/sounding?region=europe&TYPE=TEXT%3ALIST&YEAR=2012&MONTH=02&FROM=2712&TO=2712&STNM=03808", AtmosProfile.MidlatitudeWinter, ) s.run() except urllib.error.HTTPError: print("HTTP error in test, skipping") pytest.skip() self.assertAlmostEqual(s.outputs.apparent_radiance, 164.482, delta=0.02)
def test_atmos_profile(self): aps = [ AtmosProfile.Tropical, AtmosProfile.NoGaseousAbsorption, AtmosProfile.UserWaterAndOzone(0.9, 3), ] results = [0.2723143, 0.2747224, 0.2476101] for i in range(len(aps)): s = SixS() s.atmos_profile = aps[i] s.run() self.assertAlmostEqual( s.outputs.apparent_reflectance, results[i], msg="Error in atmos profile with ID %s. Got %f, expected %f." % (str(aps[i]), s.outputs.apparent_reflectance, results[i]), delta=0.002, )
def test_6s_example2(self): # Implements Example_In_2.txt from the 6S distribution in Py6S # Tests against manual run of that file s = SixS() s.geometry = Geometry.User() s.geometry.solar_z = 0 s.geometry.solar_a = 20 s.geometry.view_z = 30 s.geometry.view_a = 0 s.geometry.month = 8 s.geometry.day = 22 s.atmos_profile = AtmosProfile.PredefinedType( AtmosProfile.NoGaseousAbsorption) s.aero_profile = AeroProfile.SunPhotometerDistribution( [ 0.050000001, 0.065604001, 0.086076997, 0.112939, 0.148184001, 0.194428995, 0.255104989, 0.334715992, 0.439173013, 0.576227009, 0.756052017, 0.99199599, 1.30157101, 1.707757, 2.24070191, 2.93996596, 3.85745192, 5.06126022, 6.64074516, 8.71314526, 11.4322901, 15, ], [ 0.001338098, 0.007492487, 0.026454749, 0.058904506, 0.082712278, 0.073251031, 0.040950641, 0.014576218, 0.003672085, 0.001576356, 0.002422644, 0.004472982, 0.007452302, 0.011037065, 0.014523974, 0.016981738, 0.017641816, 0.016284294, 0.01335547, 0.009732267, 0.006301342, 0.003625077, ], [1.47] * 20, [0.0093] * 20, ) s.aot550 = 1.0 s.altitudes.set_target_sea_level() s.altitudes.set_sensor_satellite_level() s.wavelength = Wavelength(0.550) s.ground_reflectance = GroundReflectance.HomogeneousRoujean( 0.037, 0.0, 0.133) s.run() self.assertAlmostEqual(s.outputs.apparent_radiance, 76.999, delta=0.002) self.assertAlmostEqual(s.outputs.background_radiance, 10.017, delta=0.002)
def test_6s_example4(self): s = SixS() s.geometry = Geometry.User() s.geometry.solar_z = 61.23 s.geometry.solar_a = 18.78 s.geometry.solar_a = 0 s.geometry.view_z = 18.78 s.geometry.view_a = 159.2 s.geometry.month = 4 s.geometry.day = 30 s.atmos_profile = AtmosProfile.UserWaterAndOzone(0.29, 0.41) s.aero_profile = AeroProfile.PredefinedType(AeroProfile.Continental) s.aot550 = 0.14 s.altitudes.set_target_sea_level() s.altitudes.set_sensor_satellite_level() s.wavelength = Wavelength(PredefinedWavelengths.AVHRR_NOAA11_B1) s.ground_reflectance = GroundReflectance.HomogeneousLambertian([ 0.827, 0.828, 0.828, 0.827, 0.827, 0.827, 0.827, 0.826, 0.826, 0.826, 0.826, 0.825, 0.826, 0.826, 0.827, 0.827, 0.827, 0.827, 0.828, 0.828, 0.828, 0.829, 0.829, 0.828, 0.826, 0.826, 0.825, 0.826, 0.826, 0.826, 0.827, 0.827, 0.827, 0.826, 0.825, 0.826, 0.828, 0.829, 0.830, 0.831, 0.833, 0.834, 0.835, 0.836, 0.836, 0.837, 0.838, 0.838, 0.837, 0.836, 0.837, 0.837, 0.837, 0.840, 0.839, 0.840, 0.840, 0.841, 0.841, 0.841, 0.841, 0.842, 0.842, 0.842, 0.842, 0.843, 0.842, 0.843, 0.843, 0.843, 0.843, 0.843, 0.843, 0.841, 0.841, 0.842, 0.842, 0.842, 0.842, 0.842, 0.841, 0.840, 0.841, 0.838, 0.839, 0.837, 0.837, 0.836, 0.832, 0.832, 0.830, 0.829, 0.826, 0.826, 0.824, 0.821, 0.821, 0.818, 0.815, 0.813, 0.812, 0.811, 0.810, 0.808, 0.807, 0.807, 0.812, 0.808, 0.806, 0.807, 0.807, 0.807, 0.807, ]) s.run() self.assertAlmostEqual(s.outputs.apparent_radiance, 170.771, delta=0.002)
def test_6s_example3(self): s = SixS() s.geometry = Geometry.Landsat_TM() s.geometry.month = 5 s.geometry.day = 9 s.geometry.gmt_decimal_hour = 11.222 s.geometry.latitude = 40 s.geometry.longitude = 30 s.atmos_profile = AtmosProfile.PredefinedType( AtmosProfile.MidlatitudeSummer) s.aero_profile = AeroProfile.MultimodalLogNormalDistribution(0.001, 20) s.aero_profile.add_component( 0.05, 2.03, 0.538, [ 1.508, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.495, 1.490, 1.490, 1.490, 1.486, 1.480, 1.470, 1.460, 1.456, 1.443, 1.430, 1.470, ], [ 3.24e-07, 3.0e-08, 2.86e-08, 2.51e-08, 2.2e-08, 2.0e-08, 1.0e-08, 1.0e-08, 1.48e-08, 2.0e-08, 6.85e-08, 1.0e-07, 1.25e-06, 3.0e-06, 3.5e-04, 6.0e-04, 6.86e-04, 1.7e-03, 4.0e-03, 1.4e-03, ], ) s.aero_profile.add_component( 0.0695, 2.03, 0.457, [ 1.452, 1.440, 1.438, 1.433, 1.432, 1.431, 1.431, 1.430, 1.429, 1.429, 1.429, 1.428, 1.427, 1.425, 1.411, 1.401, 1.395, 1.385, 1.364, 1.396, ], [ 1.0e-08, 1.0e-08, 1.0e-08, 1.0e-08, 1.0e-08, 1.0e-08, 1.0e-08, 1.0e-08, 1.38e-08, 1.47e-08, 1.68e-08, 1.93e-08, 4.91e-08, 1.75e-07, 9.66e-06, 1.94e-04, 3.84e-04, 1.12e-03, 2.51e-03, 1.31e-01, ], ) s.aero_profile.add_component( 0.4, 2.03, 0.005, [ 1.508, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.495, 1.490, 1.490, 1.490, 1.486, 1.480, 1.470, 1.460, 1.456, 1.443, 1.430, 1.470, ], [ 3.24e-07, 3.0e-08, 2.86e-08, 2.51e-08, 2.2e-08, 2.0e-08, 1.0e-08, 1.0e-08, 1.48e-08, 2.0e-08, 6.85e-08, 1.0e-07, 1.25e-06, 3.0e-06, 3.5e-04, 6.0e-04, 6.86e-04, 1.7e-03, 4.0e-03, 1.4e-03, ], ) s.aot550 = 0.8 s.altitudes.set_target_custom_altitude(2) s.altitudes.set_sensor_satellite_level() s.wavelength = Wavelength(PredefinedWavelengths.MODIS_B4) s.ground_reflectance = GroundReflectance.HomogeneousOcean( 11, 30, 35, 3) s.run() self.assertAlmostEqual(s.outputs.apparent_reflectance, 0.1234623, delta=0.002)
def run_py6s(self, geom, acqui_date, option=1): """ runs the 6S algorithm for atmospheric correction on a user-defined geometry and date on Sentinel-2 imagery Requires Google Earth-Engine Python API client Parameters ---------- geom : EE-Geometry Google EE geometry specify the geographic extent to be processed acqui_date : String date (YYYY-MM-dd) of the desired scene to be processed option : Integer for computing the cloud mask the user can decide whether the ESA delivered quality layer should be used for cloud masking (option=1) or an alternative approach originally developed for Landsat TM (option=2) should be used (Def=1) Returns ------- s2_surf : ee.image.Image Google EE image instance with surface reflectance values and two additional bands containing Clouds ('CloudMask') and detected cloud shadows ('CloudShadows') """ date = ee.Date(acqui_date) # get the Sentinel-2 image at or immediately after the specified date self.S2 = ee.Image( ee.ImageCollection('COPERNICUS/S2') .filterBounds(geom) .filterDate(date,date.advance(3,'month')) .sort('system:time_start') .first() ) # extract the relevant metadata for carrying out the atmospheric correction self.info = self.S2.getInfo()['properties'] # get the solar zenith angle an the scene data self.scene_date = datetime.datetime.utcfromtimestamp( self.info['system:time_start']/1000) self.solar_z = self.info['MEAN_SOLAR_ZENITH_ANGLE'] # log the identifier of the processed scene scene_id = self.info.get('DATASTRIP_ID') self.__logger.info("Starting Processing scene '{}' using GEE and Py6S".format( scene_id)) # get the atmospheric constituents # i.e water vapor (h2o), ozone (o3), aerosol optical thickness (aot) h2o = Atmospheric.water(geom, date).getInfo() o3 = Atmospheric.ozone(geom, date).getInfo() aot = Atmospheric.aerosol(geom, date).getInfo() # add this information to the info dictionary as this metadata could # be of interest afterwards self.info['WATER_VAPOUR'] = h2o self.info['OZONE'] = o3 self.info['AOT'] = aot # get the average altitude of the region to be processed # for the Digital Elevation Model (DEM) the Shuttle Radar Topography # Mission (SRTM) is used (Version 4) as it covers most parts of the # Earth (90 arc-sec data is used) SRTM = ee.Image('CGIAR/SRTM90_V4') alt = SRTM.reduceRegion(reducer = ee.Reducer.mean(), geometry = geom.centroid()).get('elevation').getInfo() message = "Atcorr-Metadata: Water-Vapor = {0}, Ozone = {1}, AOT = {2}, "\ " Average Altitude (m) = {3}".format( h2o, o3, aot, alt) self.__logger.info(message) # Py6S uses units of kilometers km = alt/1000 # mask out clouds and cirrus from the imagery using the cloud score # optionally # algorithm provided by Sam Murhpy under Apache 2.0 licence # see: https://github.com/samsammurphy/cloud-masking-sentinel2/blob/master/cloud-masking-sentinel2.ipynb # also converts the image values to top-of-atmosphere reflectance self.__logger.info('Calculating cloud and shadow mask') self.S2 = mask_clouds(self.S2, option=1) self.__logger.info('Finished calculating cloud and shadow mask') # create a 6S object from the Py6S class # Instantiate (use the explizit path to installation directory of the # 6S binary as otherwise there might be an error) s = SixS() # Atmospheric constituents s.atmos_profile = AtmosProfile.UserWaterAndOzone(h2o,o3) s.aero_profile = AeroProfile.Continental s.aot550 = aot # Earth-Sun-satellite geometry s.geometry = Geometry.User() s.geometry.view_z = 0 # always NADIR (simplification!) s.geometry.solar_z = self.solar_z # solar zenith angle s.geometry.month = self.scene_date.month # month and day used for Earth-Sun distance s.geometry.day = self.scene_date.day # month and day used for Earth-Sun distance s.altitudes.set_sensor_satellite_level() s.altitudes.set_target_custom_altitude(km) self.__logger.info('6S: Starting processing of Sentinel-2 scene!') # now iterate over the nine relevant Sentinel-2 bands to perform the # atmospheric correction and get the surface reflectance # go through the spectral bands B2_surf = self.surface_reflectance(s, 'B2') self.__logger.info('6S: Finished processing Sentinel-2 Band 2!') B3_surf = self.surface_reflectance(s, 'B3') self.__logger.info('6S: Finished processing Sentinel-2 Band 3!') B4_surf = self.surface_reflectance(s, 'B4') self.__logger.info('6S: Finished processing Sentinel-2 Band 4!') B5_surf = self.surface_reflectance(s, 'B5') self.__logger.info('6S: Finished processing Sentinel-2 Band 5!') B6_surf = self.surface_reflectance(s, 'B6') self.__logger.info('6S: Finished processing Sentinel-2 Band 6!') B7_surf = self.surface_reflectance(s, 'B7') self.__logger.info('6S: Finished processing Sentinel-2 Band 7!') B8A_surf = self.surface_reflectance(s, 'B8A') self.__logger.info('6S: Finished processing Sentinel-2 Band 8A!') B11_surf = self.surface_reflectance(s, 'B11') self.__logger.info('6S: Finished processing Sentinel-2 Band 11!') B12_surf = self.surface_reflectance(s, 'B12') self.__logger.info('6S: Finished processing Sentinel-2 Band 12!') self.__logger.info('6S: Finished processing of Sentinel-2 scene!') # make a stack of the spectral bands # also add the computed cloud and shadow mask cm = self.S2.select('CloudMask') sm = self.S2.select('ShadowMask') S2_surf = B2_surf.addBands(B3_surf).addBands(B4_surf).addBands(B5_surf).addBands(B6_surf).addBands(B7_surf).addBands(B8A_surf).addBands(B11_surf).addBands(B12_surf).addBands(cm).addBands(sm) # return the surface reflectance image close_logger(self.__logger) return S2_surf