sarkit-convert 0.1.0__py3-none-any.whl → 0.2.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.
@@ -0,0 +1,201 @@
1
+ import enum
2
+ import logging
3
+
4
+ import numpy as np
5
+ import numpy.typing as npt
6
+ import pyproj
7
+
8
+ import sarkit_convert._utils
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ class _ModelTypeCodes(enum.IntEnum):
14
+ """GeoTIFF GTModelTypeGeoKey values"""
15
+
16
+ ModelTypeProjected = 1
17
+ ModelTypeGeographic = 2
18
+ ModelTypeGeocentric = 3
19
+
20
+
21
+ def _get_transformation_matrix(
22
+ model_transformation_tag: npt.ArrayLike | None,
23
+ model_tiepoint_tag: npt.ArrayLike | None,
24
+ model_pixel_scale_tag: npt.ArrayLike | None,
25
+ ):
26
+ """Matrix to convert from image coordinates to model coordinates
27
+
28
+ See: http://geotiff.maptools.org/spec/geotiff2.6.html
29
+ """
30
+
31
+ if model_transformation_tag is not None:
32
+ matrix = np.asarray(model_transformation_tag).reshape(4, 4)
33
+ if np.any(matrix[-1] != (0.0, 0.0, 0.0, 1.0)):
34
+ logger.warning(
35
+ "Last row of ModelTransformation matrix must be (0, 0, 0, 1)"
36
+ )
37
+ return matrix
38
+
39
+ if model_pixel_scale_tag is not None and model_tiepoint_tag is not None:
40
+ sx, sy, sz = np.asarray(model_pixel_scale_tag)
41
+ mtp = np.atleast_2d(model_tiepoint_tag)
42
+
43
+ if mtp.shape[0] != 1:
44
+ # Probably want to interpolate tiepoints if there is more than one
45
+ # Should only be one tiepoint when ModelPixelScale is present
46
+ return None
47
+
48
+ i, j, k, x, y, z = mtp[0]
49
+ tx = x - i * sx
50
+ ty = y + j * sy
51
+ tz = z - k * sz
52
+ matrix = np.asarray(
53
+ [
54
+ [sx, 0.0, 0.0, tx],
55
+ [0.0, -sy, 0.0, ty],
56
+ [0.0, 0.0, sz, tz],
57
+ [0.0, 0.0, 0.0, 1.0],
58
+ ]
59
+ )
60
+ return matrix
61
+
62
+ return None
63
+
64
+
65
+ def sidd_projection_from_geotiff(
66
+ shape: tuple[int, int],
67
+ gt_model_type_geo_key: int,
68
+ projected_cs_type_geo_key: int | None,
69
+ geographic_type_geo_key: int | None,
70
+ model_transformation_tag: npt.ArrayLike | None,
71
+ model_tiepoint_tag: npt.ArrayLike | None,
72
+ model_pixel_scale_tag: npt.ArrayLike | None,
73
+ grid_size: int = 11,
74
+ max_order: int = 5,
75
+ ) -> tuple[npt.NDArray, npt.NDArray, npt.NDArray, npt.NDArray]:
76
+ """
77
+ Generate SIDD PolynomialProjection polynomials for a GeoTIFF
78
+
79
+ Parameters
80
+ ----------
81
+ shape : tuple of int
82
+ Shape of the image
83
+ gt_model_type_geo_key : int
84
+ Value of the GTModelTypeGeoKey
85
+ projected_cs_type_geo_key : int, optional
86
+ Value of the ProjectedCSTypeGeoKey. Required if gt_model_type_geo_key == 1
87
+ geographic_type_geo_key : int, optional
88
+ Value of the GeographicTypeGeoKey. Required if gt_model_type_geo_key == 2
89
+ model_transformation_tag : array-like, optional
90
+ Value of the ModelTransformationTag
91
+ model_tiepoint_tag : array-like, optional
92
+ Value of the ModelTiepointTag
93
+ model_pixel_scale_tag : array-like, optional
94
+ Value of the ModelPixelScaleTag
95
+ grid_size : int, optional
96
+ Number of fit points in each dimension
97
+ max_order : int, optional
98
+ Maximum order of generated polynomials
99
+
100
+ Returns
101
+ -------
102
+ rowcol_to_lat : ndarray
103
+ 2D polynomial coefficients. (row, col) -> latitude degrees
104
+ rowcol_to_lon : ndarray
105
+ 2D polynomial coefficients. (row, col) -> longitude degrees
106
+ latlon_to_row : ndarray
107
+ 2D polynomial coefficients. (latitude degrees, longitude degrees) -> row
108
+ latlon_to_col : ndarray
109
+ 2D polynomial coefficients. (latitude degrees, longitude degrees) -> col
110
+
111
+ Notes
112
+ -----
113
+ See:
114
+ * http://geotiff.maptools.org/spec/geotiff2.5.html
115
+ * http://geotiff.maptools.org/spec/geotiff2.6.html
116
+
117
+ """
118
+ # Image Coordinates are [I J K 1] -> (column, row, vertical, 1)
119
+ image_coords = np.stack(
120
+ [
121
+ *np.meshgrid(
122
+ np.linspace(0, shape[1], grid_size),
123
+ np.linspace(0, shape[0], grid_size),
124
+ ),
125
+ np.zeros((grid_size, grid_size)), # no vertical component
126
+ np.ones((grid_size, grid_size)),
127
+ ],
128
+ axis=-1,
129
+ )
130
+
131
+ matrix = _get_transformation_matrix(
132
+ model_transformation_tag, model_tiepoint_tag, model_pixel_scale_tag
133
+ )
134
+
135
+ if matrix is None:
136
+ raise RuntimeError("Failed to get transformation matrix")
137
+
138
+ swap_axis = False
139
+ if gt_model_type_geo_key == _ModelTypeCodes.ModelTypeProjected:
140
+ if projected_cs_type_geo_key is None:
141
+ raise ValueError(
142
+ "projected_cs_type_geo_key must be provided for Projected model type"
143
+ )
144
+
145
+ crs = pyproj.CRS.from_epsg(int(projected_cs_type_geo_key))
146
+ elif gt_model_type_geo_key == _ModelTypeCodes.ModelTypeGeographic:
147
+ if geographic_type_geo_key is None:
148
+ raise ValueError(
149
+ "geographic_type_geo_key must be provided for Geographic model type"
150
+ )
151
+
152
+ crs = pyproj.CRS.from_epsg(int(geographic_type_geo_key))
153
+ # GeoTIFF axis order convention is reversed from CRS for Geographic coordinate systems
154
+ swap_axis = True
155
+ else:
156
+ raise RuntimeError(
157
+ f"GTModelTypeGeoKey == {gt_model_type_geo_key} not supported"
158
+ )
159
+
160
+ # model image
161
+ # coords = matrix * coords
162
+ # |- -| |- -| |- -|
163
+ # | X | | a b c d | | I |
164
+ # | | | | | |
165
+ # | Y | | e f g h | | J |
166
+ # | | = | | | |
167
+ # | Z | | i j k l | | K |
168
+ # | | | | | |
169
+ # | 1 | | m n o p | | 1 |
170
+ # |- -| |- -| |- -|
171
+ model_coords = np.inner(matrix, image_coords)
172
+ if swap_axis:
173
+ model_x = model_coords[1]
174
+ model_y = model_coords[0]
175
+ else:
176
+ model_x = model_coords[0]
177
+ model_y = model_coords[1]
178
+
179
+ transformer = pyproj.Transformer.from_crs(crs, 4326) # 4326 = WGS84 Lat/Lon
180
+ lats, lons = transformer.transform(model_x, model_y)
181
+ rows = image_coords[..., 1]
182
+ cols = image_coords[..., 0]
183
+
184
+ rc_span = max(np.ptp(rows), np.ptp(cols))
185
+ tol_px = 0.1
186
+ tol_lat = np.ptp(lats) / rc_span * tol_px
187
+ tol_lon = np.ptp(lons) / rc_span * tol_px
188
+
189
+ rowcol_to_lat = sarkit_convert._utils.polyfit2d_tol(
190
+ rows.flatten(), cols.flatten(), lats.flatten(), max_order, max_order, tol_lat
191
+ )
192
+ rowcol_to_lon = sarkit_convert._utils.polyfit2d_tol(
193
+ rows.flatten(), cols.flatten(), lons.flatten(), max_order, max_order, tol_lon
194
+ )
195
+ latlon_to_row = sarkit_convert._utils.polyfit2d_tol(
196
+ lats.flatten(), lons.flatten(), rows.flatten(), max_order, max_order, tol=tol_px
197
+ )
198
+ latlon_to_col = sarkit_convert._utils.polyfit2d_tol(
199
+ lats.flatten(), lons.flatten(), cols.flatten(), max_order, max_order, tol=tol_px
200
+ )
201
+ return rowcol_to_lat, rowcol_to_lon, latlon_to_row, latlon_to_col
@@ -15,6 +15,7 @@ metadata that would predict the complex data characteristics
15
15
  """
16
16
 
17
17
  import argparse
18
+ import datetime
18
19
  import pathlib
19
20
 
20
21
  import dateutil.parser
@@ -28,6 +29,7 @@ import sarkit.wgs84
28
29
  import scipy.constants
29
30
  from lxml import etree
30
31
 
32
+ from sarkit_convert import __version__
31
33
  from sarkit_convert import _utils as utils
32
34
 
33
35
  NSMAP = {
@@ -154,7 +156,6 @@ def cosar_to_sicd(
154
156
  cosar_file,
155
157
  sicd_file,
156
158
  classification,
157
- ostaid,
158
159
  chan_index,
159
160
  tx_polarizations,
160
161
  tx_rcv_pols,
@@ -196,7 +197,7 @@ def cosar_to_sicd(
196
197
  application = generation_system.text
197
198
  version = generation_system.attrib["version"]
198
199
  creation_application = f"{application} version {version}"
199
- creation_site = tsx_xml.findtext(
200
+ originator_facility = tsx_xml.findtext(
200
201
  "./productInfo/generationInfo/level1ProcessingFacility"
201
202
  )
202
203
 
@@ -491,23 +492,135 @@ def cosar_to_sicd(
491
492
  uspz = spz / npl.norm(spz)
492
493
  u_col = np.cross(uspz, u_row)
493
494
 
495
+ # Antenna
496
+ attitude_elem = tsx_xml.find("./platform/attitude")
497
+ attitude_utcs = []
498
+ attitude_quaternions = []
499
+ for attitude_data in attitude_elem.findall("./attitudeData"):
500
+ attitude_utcs.append(_parse_to_naive(attitude_data.findtext("./timeUTC")))
501
+ quat = [
502
+ float(attitude_data.findtext("./q0")),
503
+ float(attitude_data.findtext("./q1")),
504
+ float(attitude_data.findtext("./q2")),
505
+ float(attitude_data.findtext("./q3")),
506
+ ]
507
+ attitude_quaternions.append(quat)
508
+ attitude_quaternions = np.array(attitude_quaternions)
509
+
510
+ def get_mech_frame_from_quat(att_quat):
511
+ scipy_quat = np.roll(att_quat, -1)
512
+ return scipy.spatial.transform.Rotation.from_quat(scipy_quat).inv().as_matrix()
513
+
514
+ rel_att_times = np.array(
515
+ [(att_utc - collection_start_time).total_seconds() for att_utc in attitude_utcs]
516
+ )
517
+ good_indices = np.where(
518
+ np.logical_and(
519
+ np.less(-60, rel_att_times),
520
+ np.less(rel_att_times, 60 + collection_duration),
521
+ )
522
+ )
523
+ good_times = rel_att_times[good_indices]
524
+ good_att_quat = attitude_quaternions[good_indices]
525
+ mech_frame = np.array(
526
+ [get_mech_frame_from_quat(att_quat) for att_quat in good_att_quat]
527
+ )
528
+
529
+ ux_rot = mech_frame[:, 0, :]
530
+ uy_rot = mech_frame[:, 1, :]
531
+
532
+ ant_x_dir_poly = utils.fit_state_vectors(
533
+ (0, (collection_stop_time - collection_start_time).total_seconds()),
534
+ good_times,
535
+ ux_rot,
536
+ None,
537
+ None,
538
+ order=4,
539
+ )
540
+ ant_y_dir_poly = utils.fit_state_vectors(
541
+ (0, (collection_stop_time - collection_start_time).total_seconds()),
542
+ good_times,
543
+ uy_rot,
544
+ None,
545
+ None,
546
+ order=4,
547
+ )
548
+
549
+ freq_zero = center_frequency
550
+ antenna_pattern_elem = tsx_xml.xpath(
551
+ f'./calibration/calibrationData/antennaPattern[polLayer[text()="{pol_layer}"]]'
552
+ )[0]
553
+ antenna_beam_elevation = np.arctan2(
554
+ float(antenna_pattern_elem.findtext("./beamPointingVector/y")),
555
+ float(antenna_pattern_elem.findtext("./beamPointingVector/z")),
556
+ )
557
+ spotlight_elem = tsx_xml.find(
558
+ "./productInfo/acquisitionInfo/imagingModeSpecificInfo/spotLight"
559
+ )
560
+ if spotlight_elem is not None:
561
+ azimuth_steering = np.array(
562
+ [
563
+ float(spotlight_elem.findtext("./azimuthSteeringAngleFirst")),
564
+ float(spotlight_elem.findtext("./azimuthSteeringAngleLast")),
565
+ ]
566
+ )
567
+ eb_dcx_poly = npp.polyfit(
568
+ [0, collection_duration], np.sin(np.deg2rad(azimuth_steering)), 1
569
+ )
570
+ else:
571
+ eb_dcx_poly = [0.0]
572
+ eb_dcy_poly = [-look * np.sin(antenna_beam_elevation)]
573
+
574
+ def get_angles_gains(pattern_elem):
575
+ antenna_angles = []
576
+ antenna_gains = []
577
+ for gain_ext in pattern_elem.findall("./gainExt"):
578
+ antenna_angles.append(float(gain_ext.attrib["angle"]))
579
+ antenna_gains.append(float(gain_ext.text))
580
+ return np.array(antenna_angles), np.array(antenna_gains)
581
+
582
+ antenna_rg_angles, antenna_rg_gains = get_angles_gains(
583
+ antenna_pattern_elem.find("./elevationPattern")
584
+ )
585
+ antenna_az_angles, antenna_az_gains = get_angles_gains(
586
+ antenna_pattern_elem.find("./azimuthPattern")
587
+ )
588
+
589
+ fit_order = 4
590
+
591
+ def fit_gains(angles, gains):
592
+ fit_limit = -9
593
+ array_mask = gains > fit_limit
594
+ dcs = np.sin(np.deg2rad(angles))
595
+ return npp.polyfit(dcs[array_mask], gains[array_mask], fit_order)
596
+
597
+ antenna_array_gain = np.zeros((fit_order + 1, fit_order + 1), dtype=float)
598
+ antenna_array_gain[0, :] = fit_gains(
599
+ antenna_rg_angles + look * np.rad2deg(antenna_beam_elevation),
600
+ antenna_rg_gains,
601
+ )
602
+ antenna_array_gain[:, 0] = fit_gains(antenna_az_angles, antenna_az_gains)
603
+ antenna_array_gain[0, 0] = 0.0
604
+
494
605
  # Build XML
495
606
  sicd = lxml.builder.ElementMaker(
496
607
  namespace=NSMAP["sicd"], nsmap={None: NSMAP["sicd"]}
497
608
  )
498
- collection_info = sicd.CollectionInfo(
609
+ sicd_xml_obj = sicd.SICD()
610
+ sicd_ew = sksicd.ElementWrapper(sicd_xml_obj)
611
+
612
+ sicd_ew["CollectionInfo"] = sicd.CollectionInfo(
499
613
  sicd.CollectorName(collector_name),
500
614
  sicd.CoreName(core_name),
501
615
  sicd.CollectType("MONOSTATIC"),
502
616
  sicd.RadarMode(sicd.ModeType(radar_mode_type), sicd.ModeID(radar_mode_id)),
503
617
  sicd.Classification(classification),
504
618
  )
505
- image_creation = sicd.ImageCreation(
619
+ sicd_ew["ImageCreation"] = sicd.ImageCreation(
506
620
  sicd.Application(creation_application),
507
621
  sicd.DateTime(_naive_to_sicd_str(creation_time)),
508
- sicd.Site(creation_site),
509
622
  )
510
- image_data = sicd.ImageData(
623
+ sicd_ew["ImageData"] = sicd.ImageData(
511
624
  sicd.PixelType(COSAR_PIXEL_TYPE),
512
625
  sicd.NumRows(str(num_rows)),
513
626
  sicd.NumCols(str(num_cols)),
@@ -524,7 +637,7 @@ def cosar_to_sicd(
524
637
  return [sicd.Lat(str(arr[0])), sicd.Lon(str(arr[1])), sicd.HAE(str(arr[2]))]
525
638
 
526
639
  # Will add ImageCorners later
527
- geo_data = sicd.GeoData(
640
+ sicd_ew["GeoData"] = sicd.GeoData(
528
641
  sicd.EarthModel("WGS_84"),
529
642
  sicd.SCP(sicd.ECF(*make_xyz(scp_ecf)), sicd.LLH(*make_llh(scp_llh))),
530
643
  )
@@ -552,7 +665,7 @@ def cosar_to_sicd(
552
665
  col_window_name = proc_param.findtext("./azimuthWindowID")
553
666
  col_window_coeff = float(proc_param.findtext("./azimuthWindowCoefficient"))
554
667
 
555
- grid = sicd.Grid(
668
+ sicd_ew["Grid"] = sicd.Grid(
556
669
  sicd.ImagePlane("SLANT"),
557
670
  sicd.Type("RGZERO"),
558
671
  sicd.TimeCOAPoly(),
@@ -587,33 +700,27 @@ def cosar_to_sicd(
587
700
  ),
588
701
  ),
589
702
  )
590
- sksicd.Poly2dType().set_elem(grid.find("./{*}TimeCOAPoly"), time_coa_poly)
591
- sksicd.Poly2dType().set_elem(grid.find("./{*}Row/{*}DeltaKCOAPoly"), [[0]])
592
- sksicd.Poly2dType().set_elem(
593
- grid.find("./{*}Col/{*}DeltaKCOAPoly"), col_deltakcoa_poly
594
- )
703
+ sicd_ew["Grid"]["TimeCOAPoly"] = time_coa_poly
704
+ sicd_ew["Grid"]["Row"]["DeltaKCOAPoly"] = [[0]]
705
+ sicd_ew["Grid"]["Col"]["DeltaKCOAPoly"] = col_deltakcoa_poly
595
706
  rcs_row_sf = None
596
707
  rcs_col_sf = None
597
708
  if row_window_name == "Hamming":
598
709
  wgts = scipy.signal.windows.general_hamming(512, row_window_coeff, sym=True)
599
- wgtfunc = sicd.WgtFunct()
600
- sksicd.TRANSCODERS["Grid/Row/WgtFunct"].set_elem(wgtfunc, wgts)
601
- grid.find("./{*}Row").append(wgtfunc)
710
+ sicd_ew["Grid"]["Row"]["WgtFunct"] = wgts
602
711
  row_broadening_factor = utils.broadening_from_amp(wgts)
603
712
  row_wid = row_broadening_factor / row_bw
604
- sksicd.DblType().set_elem(grid.find("./{*}Row/{*}ImpRespWid"), row_wid)
713
+ sicd_ew["Grid"]["Row"]["ImpRespWid"] = row_wid
605
714
  rcs_row_sf = 1 + np.var(wgts) / np.mean(wgts) ** 2
606
715
  if col_window_name == "Hamming":
607
716
  wgts = scipy.signal.windows.general_hamming(512, col_window_coeff, sym=True)
608
- wgtfunc = sicd.WgtFunct()
609
- sksicd.TRANSCODERS["Grid/Col/WgtFunct"].set_elem(wgtfunc, wgts)
610
- grid.find("./{*}Col").append(wgtfunc)
717
+ sicd_ew["Grid"]["Col"]["WgtFunct"] = wgts
611
718
  col_broadening_factor = utils.broadening_from_amp(wgts)
612
719
  col_wid = col_broadening_factor / col_bw
613
- sksicd.DblType().set_elem(grid.find("./{*}Col/{*}ImpRespWid"), col_wid)
720
+ sicd_ew["Grid"]["Col"]["ImpRespWid"] = col_wid
614
721
  rcs_col_sf = 1 + np.var(wgts) / np.mean(wgts) ** 2
615
722
 
616
- timeline = sicd.Timeline(
723
+ sicd_ew["Timeline"] = sicd.Timeline(
617
724
  sicd.CollectStart(_naive_to_sicd_str(collection_start_time)),
618
725
  sicd.CollectDuration(str(collection_duration)),
619
726
  sicd.IPP(
@@ -628,10 +735,9 @@ def cosar_to_sicd(
628
735
  ),
629
736
  ),
630
737
  )
631
- sksicd.PolyType().set_elem(timeline.find("./{*}IPP/{*}Set/{*}IPPPoly"), [0, prf])
738
+ sicd_ew["Timeline"]["IPP"]["Set"][0]["IPPPoly"] = [0, prf]
632
739
 
633
- position = sicd.Position(sicd.ARPPoly())
634
- sksicd.XyzPolyType().set_elem(position.find("./{*}ARPPoly"), apc_poly)
740
+ sicd_ew["Position"]["ARPPoly"] = apc_poly
635
741
 
636
742
  rcv_channels = sicd.RcvChannels(
637
743
  {"size": str(len(tx_rcv_pols))},
@@ -643,7 +749,7 @@ def cosar_to_sicd(
643
749
  )
644
750
  )
645
751
 
646
- radar_collection = sicd.RadarCollection(
752
+ sicd_ew["RadarCollection"] = sicd.RadarCollection(
647
753
  sicd.TxFrequency(sicd.Min(str(tx_freq_min)), sicd.Max(str(tx_freq_max))),
648
754
  sicd.Waveform(
649
755
  {"size": "1"},
@@ -661,7 +767,7 @@ def cosar_to_sicd(
661
767
  rcv_channels,
662
768
  )
663
769
  if len(tx_polarizations) > 1:
664
- radar_collection.find("./{*}TxPolarization").text = "SEQUENCE"
770
+ sicd_ew["RadarCollection"]["TxPolarization"] = "SEQUENCE"
665
771
  tx_sequence = sicd.TxSequence({"size": str(len(tx_polarizations))})
666
772
  for ndx, tx_pol in enumerate(tx_polarizations):
667
773
  tx_sequence.append(
@@ -669,7 +775,12 @@ def cosar_to_sicd(
669
775
  )
670
776
  rcv_channels.addprevious(tx_sequence)
671
777
 
672
- image_formation = sicd.ImageFormation(
778
+ now = (
779
+ datetime.datetime.now(datetime.timezone.utc)
780
+ .isoformat(timespec="microseconds")
781
+ .replace("+00:00", "Z")
782
+ )
783
+ sicd_ew["ImageFormation"] = sicd.ImageFormation(
673
784
  sicd.RcvChanProc(sicd.NumChanProc("1"), sicd.ChanIndex(str(chan_index))),
674
785
  sicd.TxRcvPolarizationProc(tx_rcv_polarization),
675
786
  sicd.TStartProc(str(0)),
@@ -682,9 +793,23 @@ def cosar_to_sicd(
682
793
  sicd.ImageBeamComp("SV"),
683
794
  sicd.AzAutofocus("NO"),
684
795
  sicd.RgAutofocus("NO"),
796
+ sicd.Processing(
797
+ sicd.Type(f"sarkit-convert {__version__} @ {now}"),
798
+ sicd.Applied("true"),
799
+ ),
685
800
  )
686
801
 
687
- rma = sicd.RMA(
802
+ sicd_ew["Antenna"]["TwoWay"]["XAxisPoly"] = ant_x_dir_poly
803
+ sicd_ew["Antenna"]["TwoWay"]["YAxisPoly"] = ant_y_dir_poly
804
+ sicd_ew["Antenna"]["TwoWay"]["FreqZero"] = freq_zero
805
+ sicd_ew["Antenna"]["TwoWay"]["EB"]["DCXPoly"] = eb_dcx_poly
806
+ sicd_ew["Antenna"]["TwoWay"]["EB"]["DCYPoly"] = eb_dcy_poly
807
+ sicd_ew["Antenna"]["TwoWay"]["Array"]["GainPoly"] = antenna_array_gain
808
+ sicd_ew["Antenna"]["TwoWay"]["Array"]["PhasePoly"] = np.zeros(
809
+ dtype=float, shape=(1, 1)
810
+ )
811
+
812
+ sicd_ew["RMA"] = sicd.RMA(
688
813
  sicd.RMAlgoType("OMEGA_K"),
689
814
  sicd.ImageType("INCA"),
690
815
  sicd.INCA(
@@ -695,25 +820,11 @@ def cosar_to_sicd(
695
820
  sicd.DopCentroidPoly(),
696
821
  ),
697
822
  )
698
- sksicd.PolyType().set_elem(rma.find("./{*}INCA/{*}TimeCAPoly"), time_ca_poly)
699
- sksicd.Poly2dType().set_elem(rma.find("./{*}INCA/{*}DRateSFPoly"), drsf_poly)
700
- sksicd.Poly2dType().set_elem(
701
- rma.find("./{*}INCA/{*}DopCentroidPoly"), doppler_centroid_poly
702
- )
703
- sicd_xml_obj = sicd.SICD(
704
- collection_info,
705
- image_creation,
706
- image_data,
707
- geo_data,
708
- grid,
709
- timeline,
710
- position,
711
- radar_collection,
712
- image_formation,
713
- rma,
714
- )
823
+ sicd_ew["RMA"]["INCA"]["TimeCAPoly"] = time_ca_poly
824
+ sicd_ew["RMA"]["INCA"]["DRateSFPoly"] = drsf_poly
825
+ sicd_ew["RMA"]["INCA"]["DopCentroidPoly"] = doppler_centroid_poly
715
826
 
716
- image_formation.addnext(sksicd.compute_scp_coa(sicd_xml_obj.getroottree()))
827
+ sicd_ew["SCPCOA"] = sksicd.compute_scp_coa(sicd_xml_obj.getroottree())
717
828
 
718
829
  # Add Radiometric
719
830
  cal_constant = float(cal_const_elem.findtext("./calFactor"))
@@ -759,7 +870,7 @@ def cosar_to_sicd(
759
870
  radiometric.find("./{*}SigmaZeroSFPoly").addprevious(sicd.RCSSFPoly())
760
871
  sksicd.Poly2dType().set_elem(radiometric.find("./{*}RCSSFPoly"), rcssf_poly)
761
872
 
762
- sicd_xml_obj.find("./{*}RMA").addprevious(radiometric)
873
+ sicd_xml_obj.find("./{*}Antenna").addprevious(radiometric)
763
874
 
764
875
  # Add Geodata Corners
765
876
  sicd_xmltree = sicd_xml_obj.getroottree()
@@ -776,18 +887,16 @@ def cosar_to_sicd(
776
887
  sarkit.wgs84.up(sarkit.wgs84.cartesian_to_geodetic(scp_ecf)),
777
888
  )
778
889
  icp_llh = sarkit.wgs84.cartesian_to_geodetic(icp_ecef)
779
- image_corners = sicd.ImageCorners()
780
- sksicd.ImageCornersType().set_elem(image_corners, icp_llh[:, :2])
781
- geo_data.append(image_corners)
890
+ sicd_ew["GeoData"]["ImageCorners"] = icp_llh[:, :2]
782
891
 
783
892
  # Add RNIIRS
784
893
  xml_helper = sksicd.XmlHelper(sicd_xmltree)
785
894
  inf_density, pred_rniirs = utils.get_rniirs_estimate(xml_helper)
786
- collection_info.append(
787
- sicd.Parameter({"name": "INFORMATION_DENSITY"}, f"{inf_density:.2g}")
895
+ sicd_ew["CollectionInfo"].add(
896
+ "Parameter", ("INFORMATION_DENSITY", f"{inf_density:.2g}")
788
897
  )
789
- collection_info.append(
790
- sicd.Parameter({"name": "PREDICTED_RNIIRS"}, f"{pred_rniirs:.2g}")
898
+ sicd_ew["CollectionInfo"].add(
899
+ "Parameter", ("PREDICTED_RNIIRS", f"{pred_rniirs:.2g}")
791
900
  )
792
901
 
793
902
  # Validate XML
@@ -803,7 +912,7 @@ def cosar_to_sicd(
803
912
  metadata = sksicd.NitfMetadata(
804
913
  xmltree=sicd_xmltree,
805
914
  file_header_part={
806
- "ostaid": ostaid,
915
+ "ostaid": originator_facility,
807
916
  "ftitle": core_name,
808
917
  "security": {
809
918
  "clas": classification[0].upper(),
@@ -847,11 +956,6 @@ def main(args=None):
847
956
  type=pathlib.Path,
848
957
  help='path of the output SICD file. The string "{pol}" will be replaced with polarization for multiple images',
849
958
  )
850
- parser.add_argument(
851
- "--ostaid",
852
- help="content of the originating station ID (OSTAID) field of the NITF header",
853
- default="Unknown",
854
- )
855
959
  config = parser.parse_args(args)
856
960
 
857
961
  if not config.input_xml_file.is_file():
@@ -895,7 +999,6 @@ def main(args=None):
895
999
  cosar_file=img_info["cosar_filename"],
896
1000
  sicd_file=img_info["sicd_filename"],
897
1001
  classification=config.classification,
898
- ostaid=config.ostaid,
899
1002
  chan_index=img_info["chan_index"],
900
1003
  tx_polarizations=tx_polarizations,
901
1004
  tx_rcv_pols=tx_rcv_pols,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sarkit-convert
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Python library for converting SAR data to standard formats.
5
5
  Author-Email: Valkyrie Systems Corporation <info@govsco.com>
6
6
  License: MIT
@@ -16,37 +16,27 @@ Classifier: Programming Language :: Python :: 3.13
16
16
  Requires-Python: >=3.11
17
17
  Requires-Dist: lxml>=5.1.0
18
18
  Requires-Dist: numpy>=1.26.3
19
- Requires-Dist: sarkit[verification]>=0.6.0
19
+ Requires-Dist: pyproj>=3.7.2
20
+ Requires-Dist: sarkit>=1.3.0
21
+ Requires-Dist: scipy>=1.15.1
20
22
  Provides-Extra: iceye
21
23
  Requires-Dist: h5py>=3.12.1; extra == "iceye"
22
24
  Requires-Dist: python-dateutil>=2.9.0; extra == "iceye"
23
25
  Provides-Extra: cosmo
26
+ Requires-Dist: astropy>=6.0.0; extra == "cosmo"
24
27
  Requires-Dist: h5py>=3.12.1; extra == "cosmo"
25
28
  Requires-Dist: python-dateutil>=2.9.0; extra == "cosmo"
26
- Requires-Dist: scipy>=1.15.1; extra == "cosmo"
27
29
  Requires-Dist: shapely>=2.0.2; extra == "cosmo"
28
- Provides-Extra: tsx
29
- Requires-Dist: lxml>=5.1.0; extra == "tsx"
30
- Requires-Dist: python-dateutil>=2.9.0; extra == "tsx"
31
- Requires-Dist: sarkit[verification]>=0.5.0; extra == "tsx"
32
- Requires-Dist: scipy>=1.15.1; extra == "tsx"
30
+ Provides-Extra: terrasar
31
+ Requires-Dist: python-dateutil>=2.9.0; extra == "terrasar"
33
32
  Provides-Extra: sentinel
34
33
  Requires-Dist: python-dateutil>=2.9.0; extra == "sentinel"
35
- Requires-Dist: scipy>=1.15.1; extra == "sentinel"
36
34
  Requires-Dist: tifffile>=2025.5.10; extra == "sentinel"
37
35
  Provides-Extra: all
38
36
  Requires-Dist: sarkit-convert[iceye]; extra == "all"
39
37
  Requires-Dist: sarkit-convert[cosmo]; extra == "all"
40
- Requires-Dist: sarkit-convert[tsx]; extra == "all"
38
+ Requires-Dist: sarkit-convert[terrasar]; extra == "all"
41
39
  Requires-Dist: sarkit-convert[sentinel]; extra == "all"
42
- Provides-Extra: dev-lint
43
- Requires-Dist: ruff>=0.3.0; extra == "dev-lint"
44
- Requires-Dist: mypy>=1.8.0; extra == "dev-lint"
45
- Requires-Dist: types-python-dateutil>=2.9.0; extra == "dev-lint"
46
- Provides-Extra: dev-test
47
- Requires-Dist: pytest>=7.4.4; extra == "dev-test"
48
- Provides-Extra: dev
49
- Requires-Dist: sarkit-convert[dev-lint,dev-test]; extra == "dev"
50
40
  Description-Content-Type: text/markdown
51
41
 
52
42
  <div align="center">
@@ -0,0 +1,14 @@
1
+ sarkit_convert-0.2.0.dist-info/METADATA,sha256=SiqX7I3vnXIuaZMPA7yXtV8PeKo2X1li5k98TozI0jM,2185
2
+ sarkit_convert-0.2.0.dist-info/WHEEL,sha256=tsUv_t7BDeJeRHaSrczbGeuK-TtDpGsWi_JfpzD255I,90
3
+ sarkit_convert-0.2.0.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
4
+ sarkit_convert-0.2.0.dist-info/licenses/LICENSE,sha256=H0HxqjXuxJwqg17L2F5jjbrWm5IV-R_FNLbzu-eJEc0,1086
5
+ sarkit_convert/__init__.py,sha256=l_h1XRjbcNc4Hg66LvcjKzjk4lVlwaFLbNizvQtysgo,295
6
+ sarkit_convert/_utils.py,sha256=bzFGIBE870QAMfEhm5CYAP4PUlY0KM6V3paTng_rr_8,9407
7
+ sarkit_convert/_version.py,sha256=clN3TnyYyt5T_wUJLVBmutS2kYLLBET4JFB2QXnRm2Q,21
8
+ sarkit_convert/cosmo.py,sha256=ttrN0f_pU2PmH3LL0DojWCxxVDAcCm8j6kWB2w82BIU,36374
9
+ sarkit_convert/create_arp_poly.py,sha256=dgbKYYKUarC7W1edbEeozy6O4Gg-u62LsV8v1mrC_hs,5983
10
+ sarkit_convert/iceye.py,sha256=LmOqEo2_BW7noo2I9QJbPMLn51c1tAaItUBomA62gy0,31108
11
+ sarkit_convert/sentinel.py,sha256=s7rrssfrJhLsV90HzKAzoxeYNXNKTru0qNvRNkEfoU8,63124
12
+ sarkit_convert/sidd_metadata.py,sha256=lVffYbkvGWZ6hQl_PcQfR8FEMcneCGsp5n6xR5ZvHIk,6927
13
+ sarkit_convert/terrasar.py,sha256=Rl7BfBt2ZTQ9sVlMWY-rgLp0RBoWwBT1PWymSCW8Xvk,36351
14
+ sarkit_convert-0.2.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: pdm-backend (2.4.5)
2
+ Generator: pdm-backend (2.4.6)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,12 +0,0 @@
1
- sarkit_convert-0.1.0.dist-info/METADATA,sha256=2-lG0dGfU-M99LtveDx4ypQ9A_qvDwmsCRvMgv-Mre8,2668
2
- sarkit_convert-0.1.0.dist-info/WHEEL,sha256=9P2ygRxDrTJz3gsagc0Z96ukrxjr-LFBGOgv3AuKlCA,90
3
- sarkit_convert-0.1.0.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
4
- sarkit_convert-0.1.0.dist-info/licenses/LICENSE,sha256=H0HxqjXuxJwqg17L2F5jjbrWm5IV-R_FNLbzu-eJEc0,1086
5
- sarkit_convert/__init__.py,sha256=l_h1XRjbcNc4Hg66LvcjKzjk4lVlwaFLbNizvQtysgo,295
6
- sarkit_convert/_utils.py,sha256=lNjo85sgJ9aMvnSWIABu57QCa_QXOyIGLWdviJLt6wQ,8566
7
- sarkit_convert/_version.py,sha256=L6zbQIZKsAP-Knhm6fBcQFPoVdIDuejxze60qX23jiw,21
8
- sarkit_convert/csk.py,sha256=Q6f49HZxul7KsGRgN4sStHkoNOft7RFnV-9leFNod5o,30364
9
- sarkit_convert/iceye.py,sha256=7mrH9wAM6MqLodoCgOG2SegaqcA32dRcnyjkXWZ0egA,30796
10
- sarkit_convert/sentinel.py,sha256=XKeLs8QbpPj42_uCOb6N3m3Iuj3I5VxFBRRmdDTXqY4,60935
11
- sarkit_convert/tsx.py,sha256=kQCGfIBOjazvlXz_FY-ZuVMDrKkMaTdAkYRtKRsftys,32564
12
- sarkit_convert-0.1.0.dist-info/RECORD,,