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.
- sarkit_convert/_utils.py +32 -0
- sarkit_convert/_version.py +1 -1
- sarkit_convert/{csk.py → cosmo.py} +216 -59
- sarkit_convert/create_arp_poly.py +166 -0
- sarkit_convert/iceye.py +25 -14
- sarkit_convert/sentinel.py +224 -178
- sarkit_convert/sidd_metadata.py +201 -0
- sarkit_convert/{tsx.py → terrasar.py} +165 -62
- {sarkit_convert-0.1.0.dist-info → sarkit_convert-0.2.0.dist-info}/METADATA +8 -18
- sarkit_convert-0.2.0.dist-info/RECORD +14 -0
- {sarkit_convert-0.1.0.dist-info → sarkit_convert-0.2.0.dist-info}/WHEEL +1 -1
- sarkit_convert-0.1.0.dist-info/RECORD +0 -12
- {sarkit_convert-0.1.0.dist-info → sarkit_convert-0.2.0.dist-info}/entry_points.txt +0 -0
- {sarkit_convert-0.1.0.dist-info → sarkit_convert-0.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
591
|
-
|
|
592
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
720
|
+
sicd_ew["Grid"]["Col"]["ImpRespWid"] = col_wid
|
|
614
721
|
rcs_col_sf = 1 + np.var(wgts) / np.mean(wgts) ** 2
|
|
615
722
|
|
|
616
|
-
|
|
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
|
-
|
|
738
|
+
sicd_ew["Timeline"]["IPP"]["Set"][0]["IPPPoly"] = [0, prf]
|
|
632
739
|
|
|
633
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
699
|
-
|
|
700
|
-
|
|
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
|
-
|
|
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("./{*}
|
|
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
|
-
|
|
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
|
-
|
|
787
|
-
|
|
895
|
+
sicd_ew["CollectionInfo"].add(
|
|
896
|
+
"Parameter", ("INFORMATION_DENSITY", f"{inf_density:.2g}")
|
|
788
897
|
)
|
|
789
|
-
|
|
790
|
-
|
|
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":
|
|
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.
|
|
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:
|
|
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:
|
|
29
|
-
Requires-Dist:
|
|
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[
|
|
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,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,,
|
|
File without changes
|
|
File without changes
|