sarkit-convert 0.2.0__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,12 +8,13 @@ Convert a complex image(s) from the Sentinel SAFE to SICD(s).
8
8
  """
9
9
 
10
10
  import argparse
11
+ import contextlib
11
12
  import datetime
13
+ import functools
12
14
  import pathlib
13
15
 
14
16
  import dateutil.parser
15
- import lxml.builder
16
- import lxml.etree as et
17
+ import lxml.etree
17
18
  import numpy as np
18
19
  import numpy.linalg as npl
19
20
  import numpy.polynomial.polynomial as npp
@@ -176,7 +177,29 @@ def _collect_base_info(root_node):
176
177
  return base_info
177
178
 
178
179
 
179
- def _collect_swath_info(product_root_node, base_info):
180
+ @functools.lru_cache
181
+ def _window_params(window_name, coefficient):
182
+ window_name = window_name.upper()
183
+ if window_name == "HAMMING":
184
+ wgts = scipy.signal.windows.general_hamming(512, float(coefficient), sym=True)
185
+ elif window_name == "KAISER":
186
+ wgts = scipy.signal.windows.kaiser(512, float(coefficient), sym=True)
187
+ else: # Default to UNIFORM
188
+ window_name = "UNIFORM"
189
+ coefficient = None
190
+ wgts = np.ones(256)
191
+
192
+ broadening_factor = utils.broadening_from_amp(wgts)
193
+
194
+ return {
195
+ "window_name": window_name,
196
+ "coefficient": coefficient,
197
+ "wgts": wgts,
198
+ "broadening_factor": broadening_factor,
199
+ }
200
+
201
+
202
+ def _collect_swath_info(product_root_node):
180
203
  swath_info = dict()
181
204
  burst_list = product_root_node.findall("./swathTiming/burstList/burst")
182
205
 
@@ -334,20 +357,15 @@ def _collect_swath_info(product_root_node, base_info):
334
357
  range_proc = product_root_node.find(
335
358
  "./imageAnnotation/processingInformation/swathProcParamsList/swathProcParams/rangeProcessing"
336
359
  )
337
- swath_info["row_window_name"] = range_proc.find("./windowType").text.upper()
338
- swath_info["row_params"] = range_proc.find("./windowCoefficient").text
339
- if swath_info["row_window_name"] == "HAMMING":
340
- swath_info["row_wgts"] = scipy.signal.windows.general_hamming(
341
- 512, float(swath_info["row_params"]), sym=True
342
- )
343
- elif swath_info["row_window_name"] == "KAISER":
344
- swath_info["row_wgts"] = scipy.signal.windows.kaiser(
345
- 512, float(swath_info["row_params"]), sym=True
346
- )
347
- else: # Default to UNIFORM
348
- swath_info["row_window_name"] = "UNIFORM"
349
- swath_info["row_params"] = None
350
- swath_info["row_wgts"] = np.ones(256)
360
+
361
+ window_info = _window_params(
362
+ range_proc.find("./windowType").text,
363
+ range_proc.find("./windowCoefficient").text,
364
+ )
365
+ swath_info["row_window_name"] = window_info["window_name"]
366
+ swath_info["row_params"] = window_info["coefficient"]
367
+ swath_info["row_wgts"] = window_info["wgts"]
368
+ swath_info["row_broadening_factor"] = window_info["broadening_factor"]
351
369
 
352
370
  swath_info["row_ss"] = (_constants.speed_of_light / 2) * delta_tau_s
353
371
  swath_info["row_sgn"] = -1
@@ -374,29 +392,24 @@ def _collect_swath_info(product_root_node, base_info):
374
392
  ).text
375
393
  )
376
394
 
377
- swath_info["col_window_name"] = az_proc.find("./windowType").text.upper()
378
- swath_info["col_params"] = az_proc.find("./windowCoefficient").text
379
- if swath_info["col_window_name"] == "HAMMING":
380
- swath_info["col_wgts"] = scipy.signal.windows.general_hamming(
381
- 512, float(swath_info["col_params"]), sym=True
382
- )
383
- elif swath_info["col_window_name"] == "KAISER":
384
- swath_info["col_wgts"] = scipy.signal.windows.kaiser(
385
- 512, float(swath_info["col_params"]), sym=True
386
- )
387
- else: # Default to UNIFORM
388
- swath_info["col_window_name"] = "UNIFORM"
389
- swath_info["col_params"] = None
390
- swath_info["col_wgts"] = np.ones(256)
395
+ window_info = _window_params(
396
+ az_proc.find("./windowType").text, az_proc.find("./windowCoefficient").text
397
+ )
398
+ swath_info["col_window_name"] = window_info["window_name"]
399
+ swath_info["col_params"] = window_info["coefficient"]
400
+ swath_info["col_wgts"] = window_info["wgts"]
401
+ swath_info["col_broadening_factor"] = window_info["broadening_factor"]
391
402
 
392
403
  swath_info["col_sgn"] = -1
393
404
  swath_info["col_kctr"] = 0.0
394
405
  swath_info["col_imp_res_bw"] = dop_bw * swath_info["ss_zd_s"] / swath_info["col_ss"]
395
406
 
396
- row_broadening_factor = utils.broadening_from_amp(swath_info["row_wgts"])
397
- col_broadening_factor = utils.broadening_from_amp(swath_info["col_wgts"])
398
- swath_info["row_imp_res_wid"] = row_broadening_factor / swath_info["row_imp_res_bw"]
399
- swath_info["col_imp_res_wid"] = col_broadening_factor / swath_info["col_imp_res_bw"]
407
+ swath_info["row_imp_res_wid"] = (
408
+ swath_info["row_broadening_factor"] / swath_info["row_imp_res_bw"]
409
+ )
410
+ swath_info["col_imp_res_wid"] = (
411
+ swath_info["col_broadening_factor"] / swath_info["col_imp_res_bw"]
412
+ )
400
413
 
401
414
  return swath_info
402
415
 
@@ -907,7 +920,7 @@ def _collect_burst_info(product_root_node, base_info, swath_info):
907
920
 
908
921
  def _calc_radiometric_info(cal_file_name, swath_info, burst_info_list):
909
922
  """Compute radiometric polys"""
910
- cal_root_node = et.parse(cal_file_name).getroot()
923
+ cal_root_node = lxml.etree.parse(cal_file_name).getroot()
911
924
  cal_vector_list = cal_root_node.findall(
912
925
  "./{*}calibrationVectorList/{*}calibrationVector"
913
926
  )
@@ -1023,7 +1036,7 @@ def _calc_radiometric_info(cal_file_name, swath_info, burst_info_list):
1023
1036
 
1024
1037
  def _calc_noise_level_info(noise_file_name, swath_info, burst_info_list):
1025
1038
  """Compute noise poly"""
1026
- noise_root_node = et.parse(noise_file_name).getroot()
1039
+ noise_root_node = lxml.etree.parse(noise_file_name).getroot()
1027
1040
  mode_id = swath_info["mode_id"]
1028
1041
  lines_per_burst = swath_info["num_cols"]
1029
1042
  range_size_pixels = swath_info["num_rows"]
@@ -1169,232 +1182,165 @@ def _complete_filename(swath_info, burst_info, filename_template):
1169
1182
 
1170
1183
 
1171
1184
  def _create_sicd_xml(base_info, swath_info, burst_info, classification):
1172
- em = lxml.builder.ElementMaker(namespace=NSMAP["sicd"], nsmap={None: NSMAP["sicd"]})
1173
-
1174
- sicd_xml_obj = em.SICD()
1185
+ sicd_xml_obj = lxml.etree.Element(
1186
+ f"{{{NSMAP['sicd']}}}SICD", nsmap={None: NSMAP["sicd"]}
1187
+ )
1175
1188
  sicd_ew = sksicd.ElementWrapper(sicd_xml_obj)
1176
1189
 
1177
1190
  # Collection Info
1178
- sicd_ew["CollectionInfo"] = em.CollectionInfo(
1179
- em.CollectorName(swath_info["collector_name"]),
1180
- em.CoreName(burst_info["core_name"]),
1181
- em.CollectType(base_info["collect_type"]),
1182
- em.RadarMode(
1183
- em.ModeType(base_info["mode_type"]),
1184
- em.ModeID(swath_info["mode_id"]),
1185
- ),
1186
- em.Classification(classification),
1187
- em.Parameter({"name": "SLICE"}, swath_info["parameters"]["SLICE"]),
1188
- em.Parameter({"name": "BURST"}, burst_info["parameters"]["BURST"]),
1189
- em.Parameter({"name": "SWATH"}, swath_info["parameters"]["SWATH"]),
1190
- em.Parameter(
1191
- {"name": "ORBIT_SOURCE"}, swath_info["parameters"]["ORBIT_SOURCE"]
1192
- ),
1193
- *(
1194
- [
1195
- em.Parameter(
1196
- {"name": "BURST_ID"}, str(burst_info["parameters"]["BURST_ID"])
1197
- )
1198
- ]
1199
- if burst_info["parameters"].get("BURST_ID") is not None
1200
- else []
1201
- ),
1202
- em.Parameter(
1203
- {"name": "MISSION_DATA_TAKE_ID"},
1204
- swath_info["parameters"]["MISSION_DATA_TAKE_ID"],
1205
- ),
1206
- em.Parameter(
1207
- {"name": "RELATIVE_ORBIT_NUM"},
1208
- str(base_info["parameters"]["RELATIVE_ORBIT_NUM"]),
1209
- ),
1210
- )
1211
-
1212
- # Image Creation
1213
- sicd_ew["ImageCreation"] = em.ImageCreation(
1214
- em.Application(base_info["creation_application"]),
1215
- em.DateTime(base_info["creation_date_time"].strftime("%Y-%m-%dT%H:%M:%SZ")),
1216
- )
1217
-
1218
- # Image Data
1219
- sicd_ew["ImageData"] = em.ImageData(
1220
- em.PixelType(swath_info["pixel_type"]),
1221
- em.NumRows(str(swath_info["num_rows"])),
1222
- em.NumCols(str(swath_info["num_cols"])),
1223
- em.FirstRow(str(swath_info["first_row"])),
1224
- em.FirstCol(str(swath_info["first_col"])),
1225
- em.FullImage(
1226
- em.NumRows(str(swath_info["num_rows"])),
1227
- em.NumCols(str(swath_info["num_cols"])),
1228
- ),
1229
- em.SCPPixel(
1230
- em.Row(str(swath_info["scp_pixel"][0])),
1231
- em.Col(str(swath_info["scp_pixel"][1])),
1232
- ),
1233
- em.ValidData(
1234
- {"size": "4"},
1235
- em.Vertex(
1236
- {"index": "1"},
1237
- em.Row(str(burst_info["valid_data"][0][0])),
1238
- em.Col(str(burst_info["valid_data"][0][1])),
1239
- ),
1240
- em.Vertex(
1241
- {"index": "2"},
1242
- em.Row(str(burst_info["valid_data"][1][0])),
1243
- em.Col(str(burst_info["valid_data"][1][1])),
1244
- ),
1245
- em.Vertex(
1246
- {"index": "3"},
1247
- em.Row(str(burst_info["valid_data"][2][0])),
1248
- em.Col(str(burst_info["valid_data"][2][1])),
1249
- ),
1250
- em.Vertex(
1251
- {"index": "4"},
1252
- em.Row(str(burst_info["valid_data"][3][0])),
1253
- em.Col(str(burst_info["valid_data"][3][1])),
1254
- ),
1255
- ),
1191
+ parameter = [
1192
+ ("SLICE", swath_info["parameters"]["SLICE"]),
1193
+ ("BURST", burst_info["parameters"]["BURST"]),
1194
+ ("SWATH", swath_info["parameters"]["SWATH"]),
1195
+ ("ORBIT_SOURCE", swath_info["parameters"]["ORBIT_SOURCE"]),
1196
+ ]
1197
+ if burst_info["parameters"].get("BURST_ID") is not None:
1198
+ parameter.append(("BURST_ID", str(burst_info["parameters"]["BURST_ID"])))
1199
+ parameter.extend(
1200
+ [
1201
+ ("MISSION_DATA_TAKE_ID", swath_info["parameters"]["MISSION_DATA_TAKE_ID"]),
1202
+ ("RELATIVE_ORBIT_NUM", str(base_info["parameters"]["RELATIVE_ORBIT_NUM"])),
1203
+ ]
1256
1204
  )
1205
+ sicd_ew["CollectionInfo"] = {
1206
+ "CollectorName": swath_info["collector_name"],
1207
+ "CoreName": burst_info["core_name"],
1208
+ "CollectType": base_info["collect_type"],
1209
+ "RadarMode": {
1210
+ "ModeType": base_info["mode_type"],
1211
+ "ModeID": swath_info["mode_id"],
1212
+ },
1213
+ "Classification": classification,
1214
+ "Parameter": parameter,
1215
+ }
1216
+ sicd_ew["ImageCreation"] = {
1217
+ "Application": base_info["creation_application"],
1218
+ "DateTime": base_info["creation_date_time"],
1219
+ }
1220
+ sicd_ew["ImageData"] = {
1221
+ "PixelType": swath_info["pixel_type"],
1222
+ "NumRows": swath_info["num_rows"],
1223
+ "NumCols": swath_info["num_cols"],
1224
+ "FirstRow": swath_info["first_row"],
1225
+ "FirstCol": swath_info["first_col"],
1226
+ "FullImage": {
1227
+ "NumRows": swath_info["num_rows"],
1228
+ "NumCols": swath_info["num_cols"],
1229
+ },
1230
+ "SCPPixel": swath_info["scp_pixel"],
1231
+ "ValidData": burst_info["valid_data"],
1232
+ }
1257
1233
 
1258
- def _make_xyz(arr):
1259
- return [em.X(str(arr[0])), em.Y(str(arr[1])), em.Z(str(arr[2]))]
1260
-
1261
- def __make_llh(arr):
1262
- return [em.Lat(str(arr[0])), em.Lon(str(arr[1])), em.HAE(str(arr[2]))]
1263
-
1264
- # Geo Data
1265
- sicd_ew["GeoData"] = em.GeoData(
1266
- em.EarthModel("WGS_84"),
1267
- em.SCP(
1268
- em.ECF(*_make_xyz(burst_info["scp_ecf"])),
1269
- em.LLH(*__make_llh(burst_info["scp_llh"])),
1270
- ),
1271
- em.ImageCorners(),
1272
- em.ValidData(),
1273
- )
1234
+ sicd_ew["GeoData"] = {
1235
+ "EarthModel": "WGS_84",
1236
+ "SCP": {
1237
+ "ECF": burst_info["scp_ecf"],
1238
+ "LLH": burst_info["scp_llh"],
1239
+ },
1240
+ }
1274
1241
 
1275
- # Grid
1276
- sicd_ew["Grid"] = em.Grid(
1277
- em.ImagePlane(swath_info["image_plane"]),
1278
- em.Type(swath_info["grid_type"]),
1279
- em.TimeCOAPoly(),
1280
- em.Row(
1281
- em.UVectECF(*_make_xyz(burst_info["row_uvect_ecf"])),
1282
- em.SS(str(swath_info["row_ss"])),
1283
- em.ImpRespWid(str(swath_info["row_imp_res_wid"])),
1284
- em.Sgn(str(swath_info["row_sgn"])),
1285
- em.ImpRespBW(str(swath_info["row_imp_res_bw"])),
1286
- em.KCtr(str(swath_info["row_kctr"])),
1287
- em.DeltaK1(str(burst_info["row_delta_k1"])),
1288
- em.DeltaK2(str(burst_info["row_delta_k2"])),
1289
- em.DeltaKCOAPoly(),
1290
- em.WgtType(
1291
- em.WindowName(
1292
- str(swath_info["row_window_name"]),
1293
- ),
1294
- *(
1295
- [
1296
- em.Parameter(
1297
- {"name": "COEFFICIENT"},
1298
- str(swath_info["row_params"]),
1299
- )
1300
- ]
1301
- if swath_info["row_params"] is not None
1302
- else []
1303
- ),
1304
- ),
1305
- ),
1306
- em.Col(
1307
- em.UVectECF(*_make_xyz(burst_info["col_uvect_ecf"])),
1308
- em.SS(str(swath_info["col_ss"])),
1309
- em.ImpRespWid(str(swath_info["col_imp_res_wid"])),
1310
- em.Sgn(str(swath_info["col_sgn"])),
1311
- em.ImpRespBW(str(swath_info["col_imp_res_bw"])),
1312
- em.KCtr(str(swath_info["col_kctr"])),
1313
- em.DeltaK1(str(burst_info["col_delta_k1"])),
1314
- em.DeltaK2(str(burst_info["col_delta_k2"])),
1315
- em.DeltaKCOAPoly(),
1316
- em.WgtType(
1317
- em.WindowName(
1318
- str(swath_info["col_window_name"]),
1319
- ),
1320
- *(
1321
- [
1322
- em.Parameter(
1323
- {"name": "COEFFICIENT"},
1324
- str(swath_info["col_params"]),
1325
- )
1326
- ]
1327
- if swath_info["col_params"] is not None
1328
- else []
1329
- ),
1330
- ),
1331
- ),
1332
- )
1333
- sicd_ew["Grid"]["TimeCOAPoly"] = burst_info["time_coa_poly_coefs"]
1334
- sicd_ew["Grid"]["Row"]["DeltaKCOAPoly"] = swath_info["row_deltak_coa_poly"]
1335
- sicd_ew["Grid"]["Col"]["DeltaKCOAPoly"] = burst_info["col_deltak_coa_poly"]
1336
- sicd_ew["Grid"]["Row"]["WgtFunct"] = swath_info["row_wgts"]
1337
- sicd_ew["Grid"]["Col"]["WgtFunct"] = swath_info["col_wgts"]
1242
+ sicd_ew["Grid"] = {
1243
+ "ImagePlane": swath_info["image_plane"],
1244
+ "Type": swath_info["grid_type"],
1245
+ "TimeCOAPoly": burst_info["time_coa_poly_coefs"],
1246
+ "Row": {
1247
+ "UVectECF": burst_info["row_uvect_ecf"],
1248
+ "SS": swath_info["row_ss"],
1249
+ "ImpRespWid": swath_info["row_imp_res_wid"],
1250
+ "Sgn": swath_info["row_sgn"],
1251
+ "ImpRespBW": swath_info["row_imp_res_bw"],
1252
+ "KCtr": swath_info["row_kctr"],
1253
+ "DeltaK1": burst_info["row_delta_k1"],
1254
+ "DeltaK2": burst_info["row_delta_k2"],
1255
+ "DeltaKCOAPoly": swath_info["row_deltak_coa_poly"],
1256
+ "WgtType": {
1257
+ "WindowName": swath_info["row_window_name"],
1258
+ },
1259
+ "WgtFunct": swath_info["row_wgts"],
1260
+ },
1261
+ "Col": {
1262
+ "UVectECF": burst_info["col_uvect_ecf"],
1263
+ "SS": swath_info["col_ss"],
1264
+ "ImpRespWid": swath_info["col_imp_res_wid"],
1265
+ "Sgn": swath_info["col_sgn"],
1266
+ "ImpRespBW": swath_info["col_imp_res_bw"],
1267
+ "KCtr": swath_info["col_kctr"],
1268
+ "DeltaK1": burst_info["col_delta_k1"],
1269
+ "DeltaK2": burst_info["col_delta_k2"],
1270
+ "DeltaKCOAPoly": burst_info["col_deltak_coa_poly"],
1271
+ "WgtType": {
1272
+ "WindowName": swath_info["col_window_name"],
1273
+ },
1274
+ "WgtFunct": swath_info["col_wgts"],
1275
+ },
1276
+ }
1277
+ if swath_info["row_params"] is not None:
1278
+ sicd_ew["Grid"]["Row"]["WgtType"]["Parameter"] = [
1279
+ ("COEFFICIENT", str(swath_info["row_params"]))
1280
+ ]
1281
+ if swath_info["col_params"] is not None:
1282
+ sicd_ew["Grid"]["Col"]["WgtType"]["Parameter"] = [
1283
+ ("COEFFICIENT", str(swath_info["col_params"]))
1284
+ ]
1338
1285
 
1339
- # Timeline
1340
- sicd_ew["Timeline"] = em.Timeline(
1341
- em.CollectStart(burst_info["collect_start"].strftime("%Y-%m-%dT%H:%M:%S.%fZ")),
1342
- em.CollectDuration(str(burst_info["collect_duration"])),
1343
- em.IPP(
1344
- {"size": "1"},
1345
- em.Set(
1346
- {"index": "1"},
1347
- em.TStart(str(burst_info["ipp_set_tstart"])),
1348
- em.TEnd(str(burst_info["ipp_set_tend"])),
1349
- em.IPPStart(str(burst_info["ipp_set_ippstart"])),
1350
- em.IPPEnd(str(burst_info["ipp_set_ippend"])),
1351
- em.IPPPoly(),
1352
- ),
1353
- ),
1354
- )
1355
- sicd_ew["Timeline"]["IPP"]["Set"][0]["IPPPoly"] = swath_info["ipp_poly"]
1286
+ sicd_ew["Timeline"] = {
1287
+ "CollectStart": burst_info["collect_start"],
1288
+ "CollectDuration": burst_info["collect_duration"],
1289
+ "IPP": {
1290
+ "@size": 1,
1291
+ "Set": [
1292
+ {
1293
+ "@index": 1,
1294
+ "TStart": burst_info["ipp_set_tstart"],
1295
+ "TEnd": burst_info["ipp_set_tend"],
1296
+ "IPPStart": burst_info["ipp_set_ippstart"],
1297
+ "IPPEnd": burst_info["ipp_set_ippend"],
1298
+ "IPPPoly": swath_info["ipp_poly"],
1299
+ }
1300
+ ],
1301
+ },
1302
+ }
1356
1303
 
1357
- # Position
1358
1304
  sicd_ew["Position"]["ARPPoly"] = burst_info["arp_poly_coefs"]
1359
1305
 
1360
1306
  # Radar Collection
1361
- sicd_ew["RadarCollection"] = em.RadarCollection(
1362
- em.TxFrequency(
1363
- em.Min(str(swath_info["tx_freq"][0])),
1364
- em.Max(str(swath_info["tx_freq"][1])),
1365
- ),
1366
- em.Waveform(
1367
- {"size": f"{len(swath_info['rcv_window_length'])}"},
1368
- *[
1369
- em.WFParameters(
1370
- {"index": str(i)},
1371
- em.TxPulseLength(str(swath_info["tx_pulse_length"])),
1372
- em.TxRFBandwidth(str(swath_info["tx_rf_bw"])),
1373
- em.TxFreqStart(str(swath_info["tx_freq_start"])),
1374
- em.TxFMRate(str(swath_info["tx_fm_rate"])),
1375
- em.RcvWindowLength(str(swl)),
1376
- em.ADCSampleRate(str(swath_info["adc_sample_rate"])),
1377
- )
1307
+ sicd_ew["RadarCollection"] = {
1308
+ "TxFrequency": {
1309
+ "Min": swath_info["tx_freq"][0],
1310
+ "Max": swath_info["tx_freq"][1],
1311
+ },
1312
+ "Waveform": {
1313
+ "@size": len(swath_info["rcv_window_length"]),
1314
+ "WFParameters": [
1315
+ {
1316
+ "@index": i,
1317
+ "TxPulseLength": swath_info["tx_pulse_length"],
1318
+ "TxRFBandwidth": swath_info["tx_rf_bw"],
1319
+ "TxFreqStart": swath_info["tx_freq_start"],
1320
+ "TxFMRate": swath_info["tx_fm_rate"],
1321
+ "RcvWindowLength": swl,
1322
+ "ADCSampleRate": swath_info["adc_sample_rate"],
1323
+ }
1378
1324
  for i, swl in enumerate(swath_info["rcv_window_length"], start=1)
1379
1325
  ],
1380
- ),
1381
- em.TxPolarization(swath_info["tx_polarization"]),
1382
- em.RcvChannels(
1383
- {"size": f"{len(base_info['tx_rcv_polarization'])}"},
1384
- *[
1385
- em.ChanParameters(
1386
- {"index": str(i)},
1387
- em.TxRcvPolarization(entry),
1388
- )
1326
+ },
1327
+ "TxPolarization": swath_info["tx_polarization"],
1328
+ "RcvChannels": {
1329
+ "@size": len(base_info["tx_rcv_polarization"]),
1330
+ "ChanParameters": [
1331
+ {
1332
+ "@index": i,
1333
+ "TxRcvPolarization": entry,
1334
+ }
1389
1335
  for i, entry in enumerate(base_info["tx_rcv_polarization"], start=1)
1390
1336
  ],
1391
- ),
1392
- )
1337
+ },
1338
+ }
1393
1339
 
1394
- chan_indices = None
1340
+ chan_index = None
1395
1341
  for i, pol in enumerate(base_info["tx_rcv_polarization"], start=1):
1396
1342
  if pol == swath_info["tx_rcv_polarization_proc"]:
1397
- chan_indices = str(i)
1343
+ chan_index = str(i)
1398
1344
 
1399
1345
  # Image Formation
1400
1346
  now = (
@@ -1402,48 +1348,44 @@ def _create_sicd_xml(base_info, swath_info, burst_info, classification):
1402
1348
  .isoformat(timespec="microseconds")
1403
1349
  .replace("+00:00", "Z")
1404
1350
  )
1405
- sicd_ew["ImageFormation"] = em.ImageFormation(
1406
- em.RcvChanProc(
1407
- em.NumChanProc("1"),
1408
- em.PRFScaleFactor("1"),
1409
- em.ChanIndex(chan_indices),
1410
- ),
1411
- em.TxRcvPolarizationProc(swath_info["tx_rcv_polarization_proc"]),
1412
- em.TStartProc(str(burst_info["tstart_proc"])),
1413
- em.TEndProc(str(burst_info["tend_proc"])),
1414
- em.TxFrequencyProc(
1415
- em.MinProc(str(swath_info["tx_freq_proc"][0])),
1416
- em.MaxProc(str(swath_info["tx_freq_proc"][1])),
1417
- ),
1418
- em.ImageFormAlgo(swath_info["image_form_algo"]),
1419
- em.STBeamComp(swath_info["st_beam_comp"]),
1420
- em.ImageBeamComp(swath_info["image_beam_comp"]),
1421
- em.AzAutofocus(swath_info["az_autofocus"]),
1422
- em.RgAutofocus(swath_info["rg_autofocus"]),
1423
- em.Processing(
1424
- em.Type(f"sarkit-convert {__version__} @ {now}"),
1425
- em.Applied("true"),
1426
- ),
1427
- )
1351
+ sicd_ew["ImageFormation"] = {
1352
+ "RcvChanProc": {
1353
+ "NumChanProc": 1,
1354
+ "PRFScaleFactor": 1,
1355
+ "ChanIndex": [chan_index],
1356
+ },
1357
+ "TxRcvPolarizationProc": swath_info["tx_rcv_polarization_proc"],
1358
+ "TStartProc": burst_info["tstart_proc"],
1359
+ "TEndProc": burst_info["tend_proc"],
1360
+ "TxFrequencyProc": {
1361
+ "MinProc": swath_info["tx_freq_proc"][0],
1362
+ "MaxProc": swath_info["tx_freq_proc"][1],
1363
+ },
1364
+ "ImageFormAlgo": swath_info["image_form_algo"],
1365
+ "STBeamComp": swath_info["st_beam_comp"],
1366
+ "ImageBeamComp": swath_info["image_beam_comp"],
1367
+ "AzAutofocus": swath_info["az_autofocus"],
1368
+ "RgAutofocus": swath_info["rg_autofocus"],
1369
+ "Processing": [
1370
+ {
1371
+ "Type": f"sarkit-convert {__version__} @ {now}",
1372
+ "Applied": True,
1373
+ },
1374
+ ],
1375
+ }
1428
1376
 
1429
- # RMA
1430
- sicd_ew["RMA"] = em.RMA(
1431
- em.RMAlgoType(swath_info["rm_algo_type"]),
1432
- em.ImageType(swath_info["image_type"]),
1433
- em.INCA(
1434
- em.TimeCAPoly(),
1435
- em.R_CA_SCP(str(swath_info["r_ca_scp"])),
1436
- em.FreqZero(str(swath_info["freq_zero"])),
1437
- em.DRateSFPoly(),
1438
- em.DopCentroidPoly(),
1439
- em.DopCentroidCOA(swath_info["dop_centroid_coa"]),
1440
- ),
1441
- )
1442
- sicd_ew["RMA"]["INCA"]["TimeCAPoly"] = burst_info["time_ca_poly_coefs"]
1443
- sicd_ew["RMA"]["INCA"]["DRateSFPoly"] = burst_info["drsf_poly_coefs"]
1444
- sicd_ew["RMA"]["INCA"]["DopCentroidPoly"] = burst_info[
1445
- "doppler_centroid_poly_coefs"
1446
- ]
1377
+ sicd_ew["RMA"] = {
1378
+ "RMAlgoType": swath_info["rm_algo_type"],
1379
+ "ImageType": swath_info["image_type"],
1380
+ "INCA": {
1381
+ "TimeCAPoly": burst_info["time_ca_poly_coefs"],
1382
+ "R_CA_SCP": swath_info["r_ca_scp"],
1383
+ "FreqZero": swath_info["freq_zero"],
1384
+ "DRateSFPoly": burst_info["drsf_poly_coefs"],
1385
+ "DopCentroidPoly": burst_info["doppler_centroid_poly_coefs"],
1386
+ "DopCentroidCOA": swath_info["dop_centroid_coa"],
1387
+ },
1388
+ }
1447
1389
 
1448
1390
  # Add Radiometric after Sentinel baseline processing calibration update on 25 Nov 2015.
1449
1391
  if "radiometric" in burst_info:
@@ -1470,14 +1412,14 @@ def _create_sicd_xml(base_info, swath_info, burst_info, classification):
1470
1412
  return sicd_xml_obj
1471
1413
 
1472
1414
 
1473
- def _update_geo_data(xml_helper):
1415
+ def _update_geo_data(sicd_ew):
1474
1416
  # Update ImageCorners
1475
- num_rows = xml_helper.load("./{*}ImageData/{*}NumRows")
1476
- num_cols = xml_helper.load("./{*}ImageData/{*}NumCols")
1477
- row_ss = xml_helper.load("./{*}Grid/{*}Row/{*}SS")
1478
- col_ss = xml_helper.load("./{*}Grid/{*}Col/{*}SS")
1479
- scp_pixel = xml_helper.load("./{*}ImageData/{*}SCPPixel")
1480
- scp_ecf = xml_helper.load("./{*}GeoData/{*}SCP/{*}ECF")
1417
+ num_rows = sicd_ew["ImageData"]["NumRows"]
1418
+ num_cols = sicd_ew["ImageData"]["NumCols"]
1419
+ row_ss = sicd_ew["Grid"]["Row"]["SS"]
1420
+ col_ss = sicd_ew["Grid"]["Col"]["SS"]
1421
+ scp_pixel = sicd_ew["ImageData"]["SCPPixel"]
1422
+ scp_ecf = sicd_ew["GeoData"]["SCP"]["ECF"]
1481
1423
  image_grid_locations = (
1482
1424
  np.array(
1483
1425
  [
@@ -1491,27 +1433,39 @@ def _update_geo_data(xml_helper):
1491
1433
  ) * [row_ss, col_ss]
1492
1434
 
1493
1435
  icp_ecef, _, _ = sksicd.image_to_ground_plane(
1494
- xml_helper.element_tree,
1436
+ sicd_ew.elem.getroottree(),
1495
1437
  image_grid_locations,
1496
1438
  scp_ecf,
1497
1439
  sarkit.wgs84.up(sarkit.wgs84.cartesian_to_geodetic(scp_ecf)),
1498
1440
  )
1499
1441
  icp_llh = sarkit.wgs84.cartesian_to_geodetic(icp_ecef)
1500
- xml_helper.set("./{*}GeoData/{*}ImageCorners", icp_llh[:, :2])
1501
- xml_helper.set("./{*}GeoData/{*}ValidData", icp_llh[:, :2])
1442
+ sicd_ew["GeoData"]["ImageCorners"] = icp_llh[:, :2]
1443
+ sicd_ew["GeoData"]["ValidData"] = icp_llh[:, :2]
1502
1444
 
1503
1445
 
1504
- def _update_rniirs_info(xml_helper):
1505
- em = lxml.builder.ElementMaker(namespace=NSMAP["sicd"], nsmap={None: NSMAP["sicd"]})
1506
- info_density, predicted_rniirs = utils.get_rniirs_estimate(xml_helper)
1507
- collection_info_node = xml_helper.element_tree.find("./{*}CollectionInfo")
1446
+ def _update_rniirs_info(sicd_ew):
1447
+ info_density, predicted_rniirs = utils.get_rniirs_estimate(sicd_ew)
1448
+ sicd_ew["CollectionInfo"].add(
1449
+ "Parameter", ("INFORMATION_DENSITY", f"{info_density:.2G}")
1450
+ )
1451
+ sicd_ew["CollectionInfo"].add(
1452
+ "Parameter", ("PREDICTED_RNIIRS", f"{predicted_rniirs:.1f}")
1453
+ )
1454
+
1508
1455
 
1509
- param_node = em.Parameter({"name": "INFORMATION_DENSITY"}, f"{info_density:0.2G}")
1510
- collection_info_node.append(param_node)
1511
- param_node = em.Parameter({"name": "PREDICTED_RNIIRS"}, f"{predicted_rniirs:0.1f}")
1512
- collection_info_node.append(param_node)
1456
+ @contextlib.contextmanager
1457
+ def uint_tiff():
1458
+ """Overwrite SAMPLEFORMAT to force TIFFILE not to upcast from complex int16 to complex64
1513
1459
 
1514
- return
1460
+ Per S1-RS-MDA-52-7441 (Sentinel-1 Product Specification) Table 6-13:
1461
+ "Interpretation of pixel format. Set to 5 (complex signed integer, 'int16') for SLC products"
1462
+ """
1463
+ oldformat = tifffile.SAMPLEFORMAT
1464
+ tifffile.SAMPLEFORMAT = lambda _: oldformat.UINT # treat as UINT
1465
+ try:
1466
+ yield
1467
+ finally:
1468
+ tifffile.SAMPLEFORMAT = oldformat
1515
1469
 
1516
1470
 
1517
1471
  def main(args=None):
@@ -1538,14 +1492,14 @@ def main(args=None):
1538
1492
 
1539
1493
  manifest_filename = config.safe_product_folder / "manifest.safe"
1540
1494
 
1541
- manifest_root = et.parse(manifest_filename).getroot()
1495
+ manifest_root = lxml.etree.parse(manifest_filename).getroot()
1542
1496
  base_info = _collect_base_info(manifest_root)
1543
1497
  files = _get_file_sets(config.safe_product_folder, manifest_root)
1544
1498
 
1545
1499
  used_filenames = set()
1546
1500
  for entry in files:
1547
- product_root_node = et.parse(entry["product"]).getroot()
1548
- swath_info = _collect_swath_info(product_root_node, base_info)
1501
+ product_root_node = lxml.etree.parse(entry["product"]).getroot()
1502
+ swath_info = _collect_swath_info(product_root_node)
1549
1503
  burst_info_list = _collect_burst_info(product_root_node, base_info, swath_info)
1550
1504
  if base_info["creation_date_time"].date() >= np.datetime64("2015-11-25"):
1551
1505
  [burst_info.update({"radiometric": {}}) for burst_info in burst_info_list]
@@ -1553,57 +1507,61 @@ def main(args=None):
1553
1507
  _calc_noise_level_info(entry["noise"], swath_info, burst_info_list)
1554
1508
 
1555
1509
  # Grab the data and write the files
1556
- with tifffile.TiffFile(entry["data"]) as tif:
1510
+ # Overwrite SAMPLEFORMAT to force TIFFILE not to upcast from complex int16 to complex64
1511
+ # Treat as uint32 and handle the conversion later
1512
+ assert swath_info["pixel_type"] != "RE16I_IM16I", (
1513
+ "pixel handling assumes 'RE16I_IM16I'"
1514
+ )
1515
+ with uint_tiff(), tifffile.TiffFile(entry["data"]) as tif:
1557
1516
  image = tif.asarray().T
1558
1517
  image_width = tif.pages[0].tags.values()[0].value
1559
1518
  begin_col = 0
1560
1519
  for burst_info in burst_info_list:
1561
- sicd = _create_sicd_xml(
1520
+ sicd_xml_obj = _create_sicd_xml(
1562
1521
  base_info, swath_info, burst_info, config.classification.upper()
1563
1522
  )
1523
+ sicd_ew = sksicd.ElementWrapper(sicd_xml_obj)
1564
1524
  # Add SCPCOA node
1565
- scp_coa = sksicd.compute_scp_coa(sicd.getroottree())
1566
- sicd.find("./{*}ImageFormation").addnext(scp_coa)
1567
- xml_helper = sksicd.XmlHelper(et.ElementTree(sicd))
1525
+ sicd_ew["SCPCOA"] = sksicd.compute_scp_coa(sicd_xml_obj.getroottree())
1568
1526
  # Update ImageCorners and ValidData
1569
- _update_geo_data(xml_helper)
1527
+ _update_geo_data(sicd_ew)
1570
1528
 
1571
1529
  # RNIIRS calcs require radiometric info
1572
1530
  if "radiometric" in burst_info:
1573
- _update_rniirs_info(xml_helper)
1531
+ _update_rniirs_info(sicd_ew)
1574
1532
 
1575
1533
  # Check for XML consistency
1576
- sicd_con = sarkit.verification.SicdConsistency(sicd)
1534
+ sicd_con = sarkit.verification.SicdConsistency(sicd_ew.elem)
1577
1535
  sicd_con.check()
1578
1536
  sicd_con.print_result(fail_detail=True)
1579
1537
 
1580
- end_col = begin_col + xml_helper.load("{*}ImageData/{*}NumCols")
1538
+ end_col = begin_col + sicd_ew["ImageData"]["NumCols"]
1581
1539
  subset = (slice(0, image_width, 1), slice(begin_col, end_col, 1))
1582
1540
  begin_col = end_col
1583
- image_subset = image[subset]
1541
+ image_subset = np.ascontiguousarray(image[subset])
1584
1542
  pixel_type = swath_info["pixel_type"]
1585
- view_dtype = sksicd.PIXEL_TYPES[pixel_type]["dtype"]
1586
- complex_data = np.empty(image_subset.shape, dtype=view_dtype)
1587
- complex_data["real"] = image_subset.real.astype(np.int16)
1588
- complex_data["imag"] = image_subset.imag.astype(np.int16)
1543
+ view_dtype = sksicd.PIXEL_TYPES[pixel_type]["dtype"].newbyteorder(
1544
+ image_subset.dtype.byteorder
1545
+ )
1546
+ complex_data = image_subset.view(view_dtype)
1589
1547
 
1590
1548
  metadata = sksicd.NitfMetadata(
1591
- xmltree=sicd.getroottree(),
1549
+ xmltree=sicd_ew.elem.getroottree(),
1592
1550
  file_header_part={
1593
1551
  "ostaid": "ESA",
1594
- "ftitle": xml_helper.load("{*}CollectionInfo/{*}CoreName"),
1552
+ "ftitle": sicd_ew["CollectionInfo"]["CoreName"],
1595
1553
  "security": {
1596
1554
  "clas": config.classification[0].upper(),
1597
1555
  "clsy": "US",
1598
1556
  },
1599
1557
  },
1600
1558
  im_subheader_part={
1601
- "iid2": xml_helper.load("{*}CollectionInfo/{*}CoreName"),
1559
+ "iid2": sicd_ew["CollectionInfo"]["CoreName"],
1602
1560
  "security": {
1603
1561
  "clas": config.classification[0].upper(),
1604
1562
  "clsy": "US",
1605
1563
  },
1606
- "isorce": xml_helper.load("{*}CollectionInfo/{*}CollectorName"),
1564
+ "isorce": sicd_ew["CollectionInfo"]["CollectorName"],
1607
1565
  },
1608
1566
  de_subheader_part={
1609
1567
  "security": {