def search_sentinels(platform_name, df, aoi, dt=2, user=None, pwd=None, proj_string='+init=EPSG:3995', product_type=None, min_cloud_cover=0, max_cloud_cover=100, swath_type=None, f_out=None): """ Search Sentinel-1/2 images overlapping ICESat-2 data within +- dt Parameters: ----------- platform_name : str ['Sentinel-1 | Sentinel-2'] name of the platform for which images will be searched df : panda dataframe ICESat-2 data aoi: str, list area of interest as WKT string or bounding box[lllon, lllat, urlon, urlat] dt: int, float difference in hours between CS2 and S2 user : str username to connect to the Copernicus Scientific Hub pwd : str password to connect to the Copernicus Scientific Hub proj_string: str projection string to be used with the pyproj module product_type : str name of the type of product to be searched (more info at https://scihub.copernicus.eu/userguide/) swath_type : str name of the type of swath to be searched (Sentinel-1 only, more info at https://scihub.copernicus.eu/userguide/) min_cloud_cover: int, float Minimum cloud coverage in percentage (Sentinel-2 only) max_cloud_cover: int, float Maximum cloud coverage in percentage (Sentinel-2 only) f_out : str path to file where to write results Returns: (to be finished!) -------- """ #========================================================================== # Pre-processing #========================================================================== ### Imports from sentinelsat import SentinelAPI # import wkt import pyproj import numpy as np import shapely.geometry as sg from shapely.wkt import dumps, loads from astropy.time import Time, TimeDelta from tqdm import tqdm ### Convert aoi to shapely polygon in projected CRS # define projection print("Creating AOI polygon...") proj = pyproj.Proj(proj_string) # read aoi polygon if type(aoi) == str: aoi_temp = loads(aoi) elif type(aoi) in (list, tuple): aoi_temp = sg.box(aoi[0], aoi[1], aoi[2], aoi[3]) aoi = aoi_temp.wkt else: print("ERROR: 'aoi' should be provided as a WKT string or bounding box (list)") sys.exit(1) ### Check input parameters if product_type == None: if platform_name == 'Sentinel-1': product_type = 'GRD' print("product_type set to: ", product_type) if platform_name == 'Sentinel-2': product_type = 'S2MSI1C' print("product_type set to: ", product_type) if swath_type == None and platform_name == 'Sentinel-1': swath_type = 'EW' print("swath_type set to: ", swath_type) # project coordinates and convert to shapely polygon x, y = proj(aoi_temp.exterior.xy[0], aoi_temp.exterior.xy[1]) aoi_poly = sg.Polygon(list(zip(x, y))) ### Convert dt to astropy time object dtt = TimeDelta(3600 * dt, format='sec') #========================================================================== # Processing #========================================================================== ### Project IS2 data to desired CRS print("Selecting orbit data inside AOI...") lon, lat = np.array(df['lons']), np.array(df['lats']) x, y = proj(lon, lat) ### Extract IS2 orbit number is2_orbits = np.unique(df['orbit_number']) print("N. of orbits/points inside AOI: {}/{}".format(len(is2_orbits), len(df))) ### Extract time period from IS2 data to query the server t_is2 = Time(df['time'], scale='utc') t_is2_start = min(t_is2) - dtt t_is2_stop = max(t_is2) + dtt ### Read metadata print("Query for metadata...") api = SentinelAPI(user, pwd,'https://scihub.copernicus.eu/dhus', timeout=600) if platform_name == 'Sentinel-1': md = api.query(area=aoi, date=(t_is2_start.datetime, t_is2_stop.datetime), platformname='Sentinel-1', area_relation='Intersects', producttype=product_type, sensoroperationalmode=swath_type) elif platform_name == 'Sentinel-2': md = api.query(area=aoi, date=(t_is2_start.datetime, t_is2_stop.datetime), platformname='Sentinel-2', area_relation='Intersects', cloudcoverpercentage=(min_cloud_cover, max_cloud_cover), producttype=product_type) print("N. of total images: {}".format(len(md))) if len(md) == 0: return [], [], [], [], [], [] ### Convert Sentinel-2 time strings to astropy time objects t_sen = {} print("Converting time to astropy objects...") for el in md: t_sen[el] = Time(md[el]['beginposition'], format='datetime', scale='utc') ### Loop over orbits to find images that satisfy time costraints TimeDict = {} t_is2 = [] print("Looping over orbits to find intersections within {}h...".format(dt)) for c, o in tqdm(enumerate(is2_orbits)): ### select CS2 data d_is2 = df[df['orbit_number'] == o] ### compute CS2 track central time t_temp = Time(d_is2['time'], scale='utc') t_start_is2 = min(t_temp) t_stop_is2 = max(t_temp) t_is2_o = t_start_is2 + (t_stop_is2 - t_start_is2) / 2 t_is2.append(t_is2_o) ### save dict keys of images within +-dt from CS2 i_t = np.array( [el for el in md if np.abs((t_sen[el] - t_is2_o).sec) <= dtt.sec]) TimeDict[o] = i_t # get unique images within +-dt from all orbit data i_sen_t_int = set(np.concatenate(list(TimeDict.values())).ravel()) print("N. of images within {}h: {}".format(dt, len(i_sen_t_int))) if len(i_sen_t_int) == 0: return [], [], [], [], [], [] ### Project images corner coordinates and convert to shapely polygons print("Creating images footprint polygons...") # loop over them, project corner coords and create polygons SenPolygonsDict = {} for i in i_sen_t_int: # load S2 footprint aoi_sen = loads(md[i]['footprint']) # check if multipolygon has more than 1 polygon defined if len(aoi_sen) > 1: print("WARNING: footprint for product {}".format(i), "is defined by more than 1 polygon!!!") aoi_sen = aoi_sen[0] # project corner coords x_sen, y_sen = proj(aoi_sen.exterior.xy[0], aoi_sen.exterior.xy[1]) # add polygon to dictionary SenPolygonsDict[i] = sg.Polygon(list(zip(x_sen, y_sen))) ### Loop over orbits to find spatial intersections print("Looping over orbits to find intersections...") orbit_number = [] product_name = [] browse_url = [] download_url = [] t_diff = [] md_out = {} for c, o in tqdm(enumerate(is2_orbits)): ### select CS2 data i = df['orbit_number'] == o # check if track has at least 2 points if sum(i) < 2: continue d_is2 = df[i] x_is2 = x[i] y_is2 = y[i] ### create shapely line from CS track is2_line = sg.LineString(list(zip(x_is2, y_is2))) ### collect LS8 polygon indices i_sen = TimeDict[o] ### Loop over S2 polygons for i_poly in i_sen: ls_poly = SenPolygonsDict[i_poly] if is2_line.intersects(ls_poly): orbit_number.append(o) t_diff.append((t_sen[i_poly] - t_is2[c]).sec / 3600) product_name.append(md[i_poly]['filename']) download_url.append(md[i_poly]['link']) browse_url.append(md[i_poly]['link_icon']) md_out[i_poly] = md[i_poly] print("N. of total intersections: {}".format(len(orbit_number))) ### Print to file if f_out != None: print("Printing results to {}...".format(f_out)) with open(f_out, 'w') as fp: fp.write("orbit_number,t_diff_(h),product_id,dowload_url,browse_url\n") for i in range(len(orbit_number)): fp.write("{},{:.2f},{},{},{}\n".format( orbit_number[i], t_diff[i], product_name[i], download_url[i], browse_url[i])) return orbit_number, product_name, browse_url, download_url, t_diff, md_out