Use statistical API to get mean values of sentinel 1 bands from geodataframe

Hello,

I have dataframe with polygons (wgs84) and date range (in two columns-start and end).
The tbale is ismilar to this:

id      start                    end                  polygon
0      2019-10-19        2020-02-14       POLYGON ((-56.76952 -13.18003, -56.77289 -13.1...
1      2019-10-10        2020-02-25      POLYGON ((-54.23731 -13.23412, -54.21453 -13.2...
....

I want to get for each polygon the mean value of bands vv and vh from sentinel-1 . The mean value should be of pixels inside the polygons and not of the whole bounding box.

As the date range is changing all the time, I was trying to run the statistic per row and not for the while geodataframe, as described in the secod option in the documentation (using the gdf for this,taking into account the polygon shape) :

evalscript = """
//VERSION=3

function setup() {
  return {
    input: ["VV","VH","dataMask"],
    output: [{ id:"bands",bands: 2,sampleType:"FLOAT32"},{id :"dataMask",bands: 1}]
  };
}

function evaluatePixel(samples) {
  return {
  bands: [10 * Math.log((samples.VV)+0.0001) / Math.LN10, 10 * Math.log((samples.VH)+0.0001) / Math.LN10],
  dataMask:[samples.dataMask]
  };
}
"""


for index, row in gdf.iterrows():
    
    
    tmp=gpd.GeoDataFrame(gdf.loc[index]).T
    tmp=tmp.set_geometry("polygon")
    bbox_size,bbox,bbox_coords_wgs84=get_bbox_from_shape(tmp,10)    
    tmp.set_crs(epsg=4326, inplace=True)


    start_date=tmp['start'].values[0]
    end_date=tmp['Hend'].values[0]
    
    time_interval=(start_date,end_date)

    aggregation = SentinelHubStatistical.aggregation(
        evalscript=evalscript, time_interval=time_interval, aggregation_interval="P1D", resolution=(10, 10)
    )
    
    input_data=[SentinelHubStatistical.input_data(DataCollection.SENTINEL1_IW)]

    
    for geo_shape in tmp.geometry.values:
        request = SentinelHubStatistical(
            aggregation=aggregation,
            input_data=[input_data],
            geometry=Geometry(geo_shape, crs=CRS(tmp.crs)),
            config=config,
        )



The problem is that I get error for this:

ypeError Traceback (most recent call last)
/tmp/ipykernel_17356/1793327201.py in
39
40 for geo_shape in tmp.geometry.values:
—> 41 request = SentinelHubStatistical(
42 aggregation=aggregation,
43 input_data=[input_data],

/opt/conda/envs/rs/lib/python3.8/site-packages/sentinelhub/sentinelhub_statistical.py in init(self, aggregation, input_data, bbox, geometry, calculations, **kwargs)
40 self.mime_type = MimeType.JSON
41
—> 42 self.payload = self.body(
43 request_bounds=self.bounds(bbox=bbox, geometry=geometry),
44 request_data=input_data,

/opt/conda/envs/rs/lib/python3.8/site-packages/sentinelhub/sentinelhub_statistical.py in body(request_bounds, request_data, aggregation, calculations, other_args)
70 for input_data_payload in request_data:
71 if ‘dataFilter’ not in input_data_payload:
—> 72 input_data_payload[‘dataFilter’] = {}
73
74 if calculations is None:

TypeError: list indices must be integers or slices, not str
tmp

When I run the statistics as shown in the documentation at the beginning, with only the bounding box, it worked , but seems like I got results per bounding box and not per polygon.

My questions are:

  1. Why do I get this error? I’m not sure I understand what is the list of indices mentioned in the error.

  2. Is there any way to run this as shown in the first option of the documentation, with the one bbox, but instead of bbox, use one polygon? (similar to masking the sat image to get only pixels inside the polygon)

  3. Is there any way I can apply the processing of sentinel 1 inside the statistics? for example. select geometric correction and speckle filter (lee 7X7)

  4. do I need to change to another projection in order to get correct results? I can check for this case the espg in utm, but I want this part to be automatic. I saw some function in sentinel hub that seem to be able to convert from wgs to utm,but seems like is specific coordinates but not polygon, and I don’t want to search every time the specific utm. I saw something similar to this in the documentation of large area (osm splitter?)
    Edit for question 4: I found I can use geopandas function .estimate_utm_crs() , if it helps to anyone who has gotten here :slight_smile:

Hi @reutkeller ,

  1. The error message is indicating that List is expecting an indices but a string as you accidentally add another layer of [] to input_data. In the example the input of input_data is [SentinelHubStatistical.input_data()] but you are parsing [[SentinelHubStatistical.input_data()]]. By removing the square brackets from input_data=[input_data] and parsing input_data=input_data instead the script will work.

  2. Yes. The geometry parameter of SentinelHubStatistical can take polygon or multipolygon. Parse your geometry to Geometry as Geometry(<wkt>, crs=CRS.WGS84) will do the job.

  3. Yes. Those can be added to the request using the parameter other_args of SentinelHubStatistical.input_data. The input should be a dictionary and the format can be found in the API reference. An example code snippet looks like:

input_data=[SentinelHubStatistical.input_data(
    DataCollection.SENTINEL1_IW, 
    other_args={
        "processing": {
            "backCoeff": "GAMMA0_TERRAIN",
            "orthorectify": True,
            "demInstance": "MAPZEN",
            "speckleFilter": {
                "type": "LEE",
                "windowSizeX": 7,
                "windowSizeY": 7
            }
        }
    }
)]
  1. You can either make a request in wgs84 or utm. Notice that the unit of resolution is the same as the unit of your input crs. In your case the input crs is wgs84 so the resolution should be 0.00009 instead of 10. Your request is currently asking for a resolution of 10 degree.

Best reagards

1 Like

Hi,

Thank you for your answer.

when I create the bounding box after setting to UTM crs, to be in meters , I get error when creating the bbox

   #get crs for statistics 
    estimated_crs=int(str(tmp.estimate_utm_crs()).split(':')[1])
#32721
    tmp.to_crs(epsg=estimated_crs, inplace=True)
    bbox_size,bbox,bbox_coords_wgs84=get_bbox_from_shape(tmp,10)

OutOfRangeError: latitude out of range (must be between 80 deg S and 84 deg N)

that is weird becaue when I do the same with 10 meters to WGS84 I get this bbox:

BBox(((-56.77835, -13.18206), (-56.76329, -13.15846)), crs=CRS(‘4326’)). That seems to me reasonable,also when I check it in QGIS:

I think the bbox function is ok with wgs and there is no need to change it to 10 meters.

Beside that, seems like it works :slight_smile: thank you !

Hi @reutkeller ,

I’m not quite sure about the get_bbox_from_shape function you’re using, but it seems to me that the function is expecting lat and lon instead of utm coordinates.

Maybe you can try to print tmp.crs before parsing it to get_bbox_from_shape so you can make sure the input crs is in utm as expected.

Best Regards