-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsardem-sarsen.py
198 lines (162 loc) · 5.17 KB
/
sardem-sarsen.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
import argparse
import logging
import os
from dataclasses import dataclass
from functools import wraps
from time import time
import numpy as np
from osgeo import gdal # type: ignore
from sardem import cop_dem
import sarsen
import pystac
import json
__version__ = "1.0.0"
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(name)s] [%(levelname)s] %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
# Remove the sardem logging handler so we can control the output, because the
# cop_dem module adds its own handler (poor practice for a library to do).
logging.getLogger("sardem").handlers.clear()
logger = logging.getLogger("sardem-sarsen")
@dataclass(frozen=True, kw_only=True)
class Args:
catalog_path: str
bbox: tuple[float, float, float, float]
out_dir: str
def logtime(func):
"""Function decorator to log the time (seconds) a function takes to execute."""
@wraps(func)
def wrapper(*args, **kwargs):
start = time()
result = func(*args, **kwargs)
logger.info("%s: %.1f seconds", func.__name__, time() - start)
return result
return wrapper
@logtime
def get_s1_grd_path(json_file):
"""
Fetches the paths of S1 GRD products from the STAC catalog.
Parameters
----------
json_file : str
Path to the JSON file containing the STAC catalog.
Returns
-------
List[str]
List of paths of S1 GRD products.
"""
logger.info("Fetching S1 GRD product paths from the STAC catalog...")
with open(json_file, 'r') as file:
catalog_data = json.load(file)
catalog = pystac.Catalog.from_dict(catalog_data)
s1_grd_paths = []
if catalog.links :
for link in catalog.links:
if link.rel == 'item':
with open(link.absolute_href, 'r') as item_file:
item_data = json.load(item_file)
item = pystac.Item.from_dict(item_data)
if item.assets:
s1_grd_paths.append(item.assets['PRODUCT'].absolute_href)
return s1_grd_paths
@logtime
def get_dem(bbox: tuple[float, float, float, float], out_dir: str) -> str:
"""
Download DEM from Copernicus using Sardem.
Parameters
----------
bbox : str
Bounding box coordinates (min_lon, min_lat, max_lon, max_lat).
Example: '-156 18.8 -154.7 20.3'.
out_dir : str
Path to the output directory.
Returns
-------
str
Path to the downloaded DEM file
"""
logger.info("Downloading DEM from Copernicus...")
dem_file = os.path.join(out_dir, "dem.tif")
cop_dem.download_and_stitch(dem_file, bbox, output_format="GTiff")
return dem_file
@logtime
def run_sarsen(s1_file: str, dem_file: str, output_dir: str) -> str:
"""
Runs SARsen processing on a Sentinel-1 GRD product and a DEM.
Parameters
----------
s1_file : str
Path to the Sentinel-1 GRD product.
dem_file : str
Path to the DEM file.
output_dir : str
Path to the output directory.
Returns
-------
str
Path to the output of SARsen processing.
"""
logger.info("Running SARsen on the S1 GRD product and the DEM...")
output_file = os.path.join(output_dir, os.path.basename(s1_file).replace(".SAFE", "_sarsen_output.tif"))
product = sarsen.Sentinel1SarProduct(s1_file,measurement_group="IW/VV")
sarsen.terrain_correction(product, dem_file)
logger.info("SARsen process completed. Output file: %s", output_file)
return output_file
def parse_args() -> Args:
"""Parse the command-line arguments."""
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--version", action="version", version=__version__)
parser.add_argument(
"--catalog_path",
type=str,
help="URL of the S1 GRD product from the Copernicus STAC Catalog",
metavar="CATALOG_PATH",
required=False,
)
parser.add_argument(
"--bbox",
type=float,
help="lat/lon bounding box (example: --bbox '-118.068 34.222 -118.058 34.228')",
nargs=4,
metavar=("LEFT", "BOTTOM", "RIGHT", "TOP"),
required=True,
)
parser.add_argument(
"-o",
"--out_dir",
dest="out_dir",
metavar="PATH",
type=str,
help="output directory to write DEM GeoTIFF to",
required=True,
)
raw_args = parser.parse_args()
return Args(
catalog_path=raw_args.catalog_path,
bbox=raw_args.bbox,
out_dir=raw_args.out_dir,
)
@logtime
def main() -> None:
"""
Main function to execute the OGC application.
Steps:
1. Parse arguments
2. Get S1 GRD product paths
3. Download DEM
4. Run SARsen
"""
# Step 1: Parse arguments
args = parse_args()
# Step 2: Get S1 GRD product paths
s1_grd_paths = get_s1_grd_path(args.catalog_path)
# Step 3: Download DEM
dem_file = get_dem(args.bbox, args.out_dir)
# Step 4: Run SARsen for each S1 GRD product
for s1_grd_path in s1_grd_paths:
run_sarsen(s1_grd_path, dem_file, args.out_dir)
logger.info("SARSEN process completed for all S1 GRD products.")
if __name__ == "__main__":
main()