Skip to main content

Image pixels from adjacent (but overlapping) AOIs do not align when the data is downloaded, and I would like to know how I can ensure two requests for image pixels share the same alignment?
image


I am currently using the Python SDK, but to recreate the issue I have used the request builder, and here is the generated code for 2 adjacent AOIs, for S2 data on the same date.


AOI 1:


# This is script may only work with sentinelhub.__version__ >= '3.4.0'from sentinelhub import SentinelHubRequest, DataCollection, MimeType, CRS, BBox, SHConfig, Geometry# Credentialsconfig = SHConfig()config.sh_client_id = '<your client id here>'config.sh_client_secret = '<your client secret here>'evalscript = """//VERSION=3function setup() {  return {    input: ["B02", "B03", "B04"],    output: { bands: 3 }  };}function evaluatePixel(sample) {  return [2.5 * sample.B04, 2.5 * sample.B03, 2.5 * sample.B02];}"""bbox = BBox(bbox=[30.55303014474393, -29.444193007640393, 30.554029904282658, -29.44325944105369], crs=CRS.WGS84)request = SentinelHubRequest(    evalscript=evalscript,    input_data=[        SentinelHubRequest.input_data(            data_collection=DataCollection.SENTINEL2_L2A,                      time_interval=('2022-07-02', '2022-07-02'),                  ),    ],    responses=[        SentinelHubRequest.output_response('default', MimeType.TIFF),    ],    bbox=bbox,    resolution=(0.00017699, 0.00017699),    config=config)response = request.get_data()

AOI 2:


# This is script may only work with sentinelhub.__version__ >= '3.4.0'from sentinelhub import SentinelHubRequest, DataCollection, MimeType, CRS, BBox, SHConfig, Geometry# Credentialsconfig = SHConfig()config.sh_client_id = '<your client id here>'config.sh_client_secret = '<your client secret here>'evalscript = """//VERSION=3function setup() {  return {    input: ["B02", "B03", "B04"],    output: { bands: 3 }  };}function evaluatePixel(sample) {  return [2.5 * sample.B04, 2.5 * sample.B03, 2.5 * sample.B02];}"""bbox = BBox(bbox=[30.55374081718668, -29.443881819731814, 30.554732546528953, -29.442930767626954], crs=CRS.WGS84)request = SentinelHubRequest(    evalscript=evalscript,    input_data=[        SentinelHubRequest.input_data(            data_collection=DataCollection.SENTINEL2_L2A,                      time_interval=('2022-07-02', '2022-07-02'),                  ),    ],    responses=[        SentinelHubRequest.output_response('default', MimeType.TIFF),    ],    bbox=bbox,    resolution=(0.00017699, 0.00017699),    config=config)response = request.get_data()

e27fea03-e3e7-4f17-bacd-113202777076.tiff (466 Bytes)
09585002-b6cb-4a23-9634-c4a6af560851.tiff (462 Bytes)

Hi,

To make pixels aligned perfectly, you should align your bounding box coordinates to 10m using UTM.

In your case, you can convert your AOI to EPSG:32736 and round it to 10 meters.

aoi_1 = BBox(bbox=[30.55303014474393, -29.444193007640393, 30.554029904282658, -29.44325944105369], crs=CRS.WGS84)
aoi_2 = BBox(bbox=[30.55374081718668, -29.443881819731814, 30.554732546528953, -29.442930767626954], crs=CRS.WGS84)

new_aoi_1 = np.round(list(aoi_1.transform(CRS(32736))), -1)
new_aoi_2 = np.round(list(aoi_2.transform(CRS(32736))), -1)

After aligning bounding box to 10 meters, the image pixels from the adjacent AOI should be perfectly aligned.

Below are the example requests for your AOIs.
AOI 1:

curl -X POST https://services.sentinel-hub.com/api/v1/process \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ' \
-d '{
"input": {
"bounds": {
"bbox": [
262650,
6740310,
262750,
6740410
],
"properties": {
"crs": "http://www.opengis.net/def/crs/EPSG/0/32736"
}
},
"data": [
{
"dataFilter": {
"timeRange": {
"from": "2022-09-01T00:00:00Z",
"to": "2022-11-10T23:59:59Z"
},
"mosaickingOrder": "leastCC"
},
"type": "sentinel-2-l2a"
}
]
},
"output": {
"width": 10,
"height": 10,
"responses": [
{
"identifier": "default",
"format": {
"type": "image/tiff"
}
}
]
},
"evalscript": "//VERSION=3\n\nfunction setup() {\n return {\n input: [\"B02\", \"B03\", \"B04\"],\n output: { bands: 3 }\n };\n}\n\nfunction evaluatePixel(sample) {\n return [2.5 * sample.B04, 2.5 * sample.B03, 2.5 * sample.B02];\n}"
}'

AOI 2:

curl -X POST https://services.sentinel-hub.com/api/v1/process \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ' \
-d '{
"input": {
"bounds": {
"bbox": [
262720,
6740340,
262820,
6740450
],
"properties": {
"crs": "http://www.opengis.net/def/crs/EPSG/0/32736"
}
},
"data": [
{
"dataFilter": {
"timeRange": {
"from": "2022-09-01T00:00:00Z",
"to": "2022-11-10T23:59:59Z"
},
"mosaickingOrder": "leastCC"
},
"type": "sentinel-2-l2a"
}
]
},
"output": {
"width": 10,
"height": 11,
"responses": [
{
"identifier": "default",
"format": {
"type": "image/tiff"
}
}
]
},
"evalscript": "//VERSION=3\n\nfunction setup() {\n return {\n input: [\"B02\", \"B03\", \"B04\"],\n output: { bands: 3 }\n };\n}\n\nfunction evaluatePixel(sample) {\n return [2.5 * sample.B04, 2.5 * sample.B03, 2.5 * sample.B02];\n}"
}'

Thank you for the response 

Follow-on comments, after implementing Chung’s solution:

  1. Below I adapted the ‘to_utm_bbox’ function in the geo_utils library (included in the sentinel hub python sdk) to include the ability to align pixels given a resolution.
# A function to round to a multiple
def round_to_multiple(number, multiple):
return multiple * round(number / multiple)

def to_utm_bbox(bbox: BBox, resolution: int = 10, align_pixel: bool = False) -> BBox:
"""Transform bbox into UTM CRS

:param bbox: bounding box
:param align_pixel: do pixels need to be aligned to a similar upper limits (default: False)
:return: bounding box in UTM CRS
"""
if not CRS.is_utm(bbox.crs):
lng, lat = bbox.middle
utm_crs = get_utm_crs(lng, lat, source_crs=bbox.crs)
bbox = bbox.transform(CRS(utm_crs))
if align_pixel:
bbox_bounds = tuple( round_to_multiple(coord, resolution)for coord in list(bbox)])
bbox = BBox(bbox=bbox_bounds, crs=CRS(utm_crs))
return bbox
bbox = BBox(bbox=bounds, crs=CRS.WGS84)
# Convert the bounding box to UTM so that pixels are snapped and aligned.
bbox_utm = to_utm_bbox(bbox=bbox,resolution=resolution, align_pixel=True)
  1. It is also valuable to note that you can use ‘get_utm_crs’ to locate the correct utm epsg code for the projection.

Reply