sarpyx 0.1.5__py3-none-any.whl → 0.1.6__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.
- docs/examples/advanced/batch_processing.py +1 -1
- docs/examples/advanced/custom_processing_chains.py +1 -1
- docs/examples/advanced/performance_optimization.py +1 -1
- docs/examples/basic/snap_integration.py +1 -1
- docs/examples/intermediate/quality_assessment.py +1 -1
- outputs/baseline/20260205-234828/__init__.py +33 -0
- outputs/baseline/20260205-234828/main.py +493 -0
- outputs/final/20260205-234851/__init__.py +33 -0
- outputs/final/20260205-234851/main.py +493 -0
- sarpyx/__init__.py +2 -2
- sarpyx/algorithms/__init__.py +2 -2
- sarpyx/cli/__init__.py +1 -1
- sarpyx/cli/focus.py +3 -5
- sarpyx/cli/main.py +106 -7
- sarpyx/cli/shipdet.py +1 -1
- sarpyx/cli/worldsar.py +549 -0
- sarpyx/processor/__init__.py +1 -1
- sarpyx/processor/core/decode.py +43 -8
- sarpyx/processor/core/focus.py +104 -57
- sarpyx/science/__init__.py +1 -1
- sarpyx/sla/__init__.py +8 -0
- sarpyx/sla/metrics.py +101 -0
- sarpyx/{snap → snapflow}/__init__.py +1 -1
- sarpyx/snapflow/engine.py +6165 -0
- sarpyx/{snap → snapflow}/op.py +0 -1
- sarpyx/utils/__init__.py +1 -1
- sarpyx/utils/geos.py +652 -0
- sarpyx/utils/grid.py +285 -0
- sarpyx/utils/io.py +77 -9
- sarpyx/utils/meta.py +55 -0
- sarpyx/utils/nisar_utils.py +652 -0
- sarpyx/utils/rfigen.py +108 -0
- sarpyx/utils/wkt_utils.py +109 -0
- sarpyx/utils/zarr_utils.py +55 -37
- {sarpyx-0.1.5.dist-info → sarpyx-0.1.6.dist-info}/METADATA +9 -5
- {sarpyx-0.1.5.dist-info → sarpyx-0.1.6.dist-info}/RECORD +41 -32
- {sarpyx-0.1.5.dist-info → sarpyx-0.1.6.dist-info}/WHEEL +1 -1
- sarpyx-0.1.6.dist-info/licenses/LICENSE +201 -0
- sarpyx-0.1.6.dist-info/top_level.txt +4 -0
- tests/test_zarr_compat.py +35 -0
- sarpyx/processor/core/decode_v0.py +0 -0
- sarpyx/processor/core/decode_v1.py +0 -849
- sarpyx/processor/core/focus_old.py +0 -1550
- sarpyx/processor/core/focus_v1.py +0 -1566
- sarpyx/processor/core/focus_v2.py +0 -1625
- sarpyx/snap/engine.py +0 -633
- sarpyx-0.1.5.dist-info/top_level.txt +0 -2
- {sarpyx-0.1.5.dist-info → sarpyx-0.1.6.dist-info}/entry_points.txt +0 -0
sarpyx/utils/rfigen.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from scipy.constants import c
|
|
3
|
+
|
|
4
|
+
def freq2wavelen(f_hz):
|
|
5
|
+
return c / f_hz
|
|
6
|
+
|
|
7
|
+
def fspl(r_m, wavelen_m):
|
|
8
|
+
return (4 * np.pi * r_m / wavelen_m) ** 2
|
|
9
|
+
|
|
10
|
+
def GenerateRFISignal(sqd, RadPar=None, verbose=False):
|
|
11
|
+
"""Generate Radio Frequency Interference (RFI) signal for SAR data.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
sqd (np.ndarray): Input SAR single look complex data with shape (rows, cols).
|
|
15
|
+
RadPar (dict): Radar parameters containing:
|
|
16
|
+
- 'bw': Bandwidth in Hz
|
|
17
|
+
- 'fo': Center frequency in Hz
|
|
18
|
+
- 'fs': Sampling frequency in Hz
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
tuple: A tuple containing:
|
|
22
|
+
- sInterf (np.ndarray): Generated interference signal with same shape as sqd
|
|
23
|
+
- IR (dict): Dictionary containing RFI parameters used in generation
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
if sqd is None:
|
|
28
|
+
raise ValueError('The parameter "sqd" must be a valid numpy ndarray.')
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
RadPar = {
|
|
32
|
+
'fo': 5405000454.33435,
|
|
33
|
+
'fs': 64345238.12571428,
|
|
34
|
+
'bw': 56504455.48389234,
|
|
35
|
+
'ts': 1.554116558005821e-08
|
|
36
|
+
} if RadPar is None else RadPar
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
IR = {}
|
|
40
|
+
|
|
41
|
+
# === RFI Physical Parameters ===
|
|
42
|
+
IR['freqShift'] = (np.random.rand() - 0.5) * RadPar['bw'] # shift within radar BW
|
|
43
|
+
IR['fc'] = RadPar['fo'] + IR['freqShift']
|
|
44
|
+
IR['SIR_dB'] = np.random.uniform(-25, -5) # control strength
|
|
45
|
+
IR['bw'] = np.random.uniform(0.005, 0.05) * RadPar['bw'] # narrower than radar BW
|
|
46
|
+
IR['PRF'] = np.random.randint(1000, 2000)
|
|
47
|
+
IR['duty'] = np.random.uniform(0.1, 0.5) # sparse duty
|
|
48
|
+
IR['Gain'] = 1.0
|
|
49
|
+
IR['Lambda'] = freq2wavelen(IR['fc'])
|
|
50
|
+
IR['T'] = IR['duty'] / IR['PRF']
|
|
51
|
+
IR['K'] = IR['bw'] / IR['T']
|
|
52
|
+
fs = RadPar['fs']
|
|
53
|
+
IR['t'] = np.arange(-IR['T']/2, IR['T']/2, 1/fs)
|
|
54
|
+
|
|
55
|
+
# === Chirp Pulse Generation ===
|
|
56
|
+
chirp = np.exp(1j * np.pi * IR['K'] * IR['t']**2)
|
|
57
|
+
chirp *= np.exp(1j * 2 * np.pi * IR['freqShift'] * IR['t']) # center-shift
|
|
58
|
+
chirp *= np.hanning(len(chirp)) # envelope
|
|
59
|
+
chirp /= np.max(np.abs(chirp))
|
|
60
|
+
|
|
61
|
+
# === Pulse Train (1D) ===
|
|
62
|
+
rows, cols = sqd.shape
|
|
63
|
+
burst_duration = rows / IR['PRF']
|
|
64
|
+
IRstream_len = int(burst_duration * fs)
|
|
65
|
+
pulse_spacing = int(fs / IR['PRF'])
|
|
66
|
+
pulse_len = len(chirp)
|
|
67
|
+
IRstream = np.zeros(IRstream_len, dtype=np.complex64)
|
|
68
|
+
|
|
69
|
+
pulse_indices = np.arange(0, IRstream_len - pulse_len, pulse_spacing)
|
|
70
|
+
N_total = len(pulse_indices)
|
|
71
|
+
N_active = int(IR['duty'] * N_total)
|
|
72
|
+
active_idx = np.random.choice(pulse_indices, N_active, replace=False)
|
|
73
|
+
for idx in active_idx:
|
|
74
|
+
IRstream[idx:idx+pulse_len] += chirp
|
|
75
|
+
|
|
76
|
+
# === Map to Burst Grid (Spatial Submask) ===
|
|
77
|
+
IRmap = np.zeros((rows, cols), dtype=np.complex64)
|
|
78
|
+
# Random RFI spatial extent (5-95% of image dimensions)
|
|
79
|
+
az_fraction = np.random.uniform(0.05, 0.95)
|
|
80
|
+
rg_fraction = np.random.uniform(0.05, 0.95)
|
|
81
|
+
sub_rows = int(rows * az_fraction)
|
|
82
|
+
sub_cols = int(cols * rg_fraction)
|
|
83
|
+
|
|
84
|
+
# Random position ensuring no border overflow
|
|
85
|
+
az_start = np.random.randint(0, max(1, rows - sub_rows + 1))
|
|
86
|
+
az_stop = az_start + sub_rows
|
|
87
|
+
rg_start = np.random.randint(0, max(1, cols - sub_cols + 1))
|
|
88
|
+
rg_stop = rg_start + sub_cols
|
|
89
|
+
|
|
90
|
+
# reshape with tiling
|
|
91
|
+
IRstream_crop = IRstream[:sub_rows * sub_cols]
|
|
92
|
+
IRmap[az_start:az_stop, rg_start:rg_stop] = IRstream_crop.reshape((sub_rows, sub_cols))
|
|
93
|
+
|
|
94
|
+
# === Power Calibration ===
|
|
95
|
+
signal_power_lin = np.mean(np.abs(sqd)**2)
|
|
96
|
+
signal_power_dB = 10 * np.log10(signal_power_lin)
|
|
97
|
+
PrRFI_dB = signal_power_dB - IR['SIR_dB']
|
|
98
|
+
PrRFI = 10 ** (PrRFI_dB / 10)
|
|
99
|
+
sInterf = IRmap * np.sqrt(PrRFI)
|
|
100
|
+
|
|
101
|
+
# === Metadata & Logging ===
|
|
102
|
+
IR['AffectedAz'] = (az_start, az_stop)
|
|
103
|
+
IR['AffectedRg'] = (rg_start, rg_stop)
|
|
104
|
+
if verbose:
|
|
105
|
+
print(f"[INFO] Injected RFI: freq offset = {IR['freqShift']/1e6:.2f} MHz, bw = {IR['bw']/1e6:.2f} MHz")
|
|
106
|
+
print(f"[INFO] SIR = {IR['SIR_dB']} dB, Affected area: Az[{az_start}:{az_stop}], Rg[{rg_start}:{rg_stop}]")
|
|
107
|
+
|
|
108
|
+
return sInterf, IR
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""This script provides utility functions for extracting Well-Known Text (WKT) representations of geographic footprints
|
|
2
|
+
from satellite product metadata. It includes functions for extracting WKT polygons from Sentinel-1 products using
|
|
3
|
+
the Copernicus Data Search API and from Terrasar-X product XML files.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from phidown.search import CopernicusDataSearcher
|
|
8
|
+
from shapely.geometry import shape
|
|
9
|
+
import xml.etree.ElementTree as ET
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def sentinel1_wkt_extractor_cdse(product_name: str, display_results: bool = False, verbose: bool = False) -> str | None:
|
|
16
|
+
"""
|
|
17
|
+
Extract WKT footprint from Sentinel-1 product using Copernicus Data Search.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
product_name (str): Name of the Sentinel-1 product to search for.
|
|
21
|
+
display_results (bool): Whether to display search results. Defaults to False.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
str | None: WKT representation of the product footprint polygon, or None if not found.
|
|
25
|
+
"""
|
|
26
|
+
searcher = CopernicusDataSearcher()
|
|
27
|
+
if verbose:
|
|
28
|
+
print(f"Searching for product with exact name: {product_name}\n")
|
|
29
|
+
df_exact = searcher.query_by_name(product_name=product_name)
|
|
30
|
+
|
|
31
|
+
if not df_exact.empty:
|
|
32
|
+
if display_results:
|
|
33
|
+
searcher.display_results(top_n=1)
|
|
34
|
+
if verbose:
|
|
35
|
+
print(df_exact)
|
|
36
|
+
print("\nProduct found successfully.")
|
|
37
|
+
|
|
38
|
+
geofootprint = df_exact['GeoFootprint'].values[0]
|
|
39
|
+
if verbose:
|
|
40
|
+
print(f"Product with GeoFootprint: '{geofootprint}' found.")
|
|
41
|
+
|
|
42
|
+
polygon = shape(geofootprint)
|
|
43
|
+
wkt_polygon = polygon.wkt
|
|
44
|
+
|
|
45
|
+
if verbose:
|
|
46
|
+
print(f"\nWKT Polygon:\n{wkt_polygon}")
|
|
47
|
+
return wkt_polygon
|
|
48
|
+
else:
|
|
49
|
+
if verbose:
|
|
50
|
+
print("No product found with the specified name.")
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def terrasar_wkt_extractor(product_path: Path) -> str:
|
|
55
|
+
"""
|
|
56
|
+
Extract WKT footprint from Terrasar-X product XML file.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
product_path (Path): Path to the input Terrasar-X product XML file.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
str: WKT representation of the product footprint polygon.
|
|
63
|
+
"""
|
|
64
|
+
if isinstance(product_path, str):
|
|
65
|
+
product_path = Path(product_path)
|
|
66
|
+
assert product_path.exists(), f"Product path {product_path} does not exist."
|
|
67
|
+
assert product_path.suffix.lower() == '.xml', f"Product path {product_path} is not an XML file."
|
|
68
|
+
|
|
69
|
+
tree = ET.parse(product_path)
|
|
70
|
+
root = tree.getroot()
|
|
71
|
+
|
|
72
|
+
# Find all sceneCornerCoord elements and extract coordinates
|
|
73
|
+
corners = [(float(corner.find('lon').text), float(corner.find('lat').text))
|
|
74
|
+
for corner in root.findall('.//sceneCornerCoord')]
|
|
75
|
+
|
|
76
|
+
# Close the polygon by repeating the first point at the end
|
|
77
|
+
if corners:
|
|
78
|
+
corners.append(corners[0])
|
|
79
|
+
|
|
80
|
+
# Create WKT polygon string
|
|
81
|
+
return f"POLYGON(({', '.join(f'{lon} {lat}' for lon, lat in corners)}))"
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
if __name__ == "__main__":
|
|
87
|
+
import argparse
|
|
88
|
+
|
|
89
|
+
parser = argparse.ArgumentParser(description="Extract WKT footprint from satellite product metadata.")
|
|
90
|
+
parser.add_argument('--mode', type=str, required=True, help="Mode of operation: 'S1TOPS', 'S1STRIP', 'BM', 'TSX', etc.")
|
|
91
|
+
parser.add_argument('--product-path', type=str, required=True, help="Name of the satellite product.")
|
|
92
|
+
args = parser.parse_args()
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
MODE = args.mode # Example mode, can be 'S1TOPS', 'S1STRIP', 'BM', 'TSX', etc.
|
|
96
|
+
PRODUCT_PATH = Path(args.product_path)
|
|
97
|
+
PRODUCT_NAME = PRODUCT_PATH.name
|
|
98
|
+
|
|
99
|
+
if MODE == 'S1TOPS' or MODE == 'S1STRIP':
|
|
100
|
+
# Example usage for Sentinel-1 product
|
|
101
|
+
sentinel1_product_name = PRODUCT_NAME
|
|
102
|
+
wkt_sentinel1 = sentinel1_wkt_extractor_cdse(sentinel1_product_name, display_results=False)
|
|
103
|
+
print(f"\nSentinel-1 WKT Polygon:\n{wkt_sentinel1}")
|
|
104
|
+
|
|
105
|
+
elif MODE == 'TSX':
|
|
106
|
+
# Example usage for Terrasar-X product
|
|
107
|
+
terrasar_product_path = PRODUCT_PATH
|
|
108
|
+
wkt_terrasar = terrasar_wkt_extractor(terrasar_product_path)
|
|
109
|
+
print(f"\nTerrasar-X WKT Polygon:\n{wkt_terrasar}")
|
sarpyx/utils/zarr_utils.py
CHANGED
|
@@ -39,45 +39,42 @@ def save_array_to_zarr(array: 'np.ndarray',
|
|
|
39
39
|
chunk_size = max(64, chunk_size) # Ensure minimum chunk size
|
|
40
40
|
chunks = (chunk_size, chunk_size)
|
|
41
41
|
|
|
42
|
-
# Use maximum compression with zstd and byte shuffle
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
zarr_array = zarr.open(
|
|
51
|
-
file_path,
|
|
52
|
-
mode='w',
|
|
53
|
-
shape=array.shape,
|
|
42
|
+
# Use maximum compression with zstd and byte shuffle (Zarr 3.x format)
|
|
43
|
+
compressors = [{'name': 'blosc', 'configuration': {'cname': 'zstd', 'clevel': compressor_level, 'shuffle': 'bitshuffle'}}]
|
|
44
|
+
|
|
45
|
+
# Create Zarr array using group + create_array pattern for Zarr 3.x compatibility
|
|
46
|
+
store = zarr.open_group(file_path, mode='w')
|
|
47
|
+
zarr_array = store.create_array(
|
|
48
|
+
name='data',
|
|
49
|
+
shape=array.shape,
|
|
54
50
|
dtype=array.dtype,
|
|
55
|
-
zarr_format=2,
|
|
56
|
-
compressor=codec,
|
|
57
51
|
chunks=chunks,
|
|
52
|
+
compressors=compressors,
|
|
53
|
+
overwrite=True
|
|
58
54
|
)
|
|
59
55
|
zarr_array[:] = array
|
|
60
56
|
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
# Store attributes on the group level for easier access
|
|
58
|
+
store.attrs['parent_product'] = parent_product if parent_product else 'unknown'
|
|
59
|
+
store.attrs['creation_date'] = pd.Timestamp.now().isoformat()
|
|
63
60
|
# Add metadata as attributes if provided
|
|
64
61
|
if metadata_df is not None:
|
|
65
62
|
# Handle NaN values by filling them with None or converting to string
|
|
66
63
|
metadata_clean = metadata_df.fillna('null')
|
|
67
64
|
|
|
68
65
|
# Convert DataFrame to dictionary for zarr attributes
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
store.attrs['metadata'] = metadata_clean.to_dict('records')
|
|
67
|
+
store.attrs['metadata_columns'] = list(metadata_df.columns)
|
|
68
|
+
store.attrs['metadata_dtypes'] = metadata_df.dtypes.astype(str).to_dict()
|
|
72
69
|
print(f'Added metadata with {len(metadata_df)} records as zarr attributes')
|
|
73
70
|
|
|
74
71
|
# Add ephemeris data as attributes if provided
|
|
75
72
|
if ephemeris_df is not None:
|
|
76
73
|
ephemeris_clean = ephemeris_df.fillna('null')
|
|
77
74
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
75
|
+
store.attrs['ephemeris'] = ephemeris_clean.to_dict('records')
|
|
76
|
+
store.attrs['ephemeris_columns'] = list(ephemeris_df.columns)
|
|
77
|
+
store.attrs['ephemeris_dtypes'] = ephemeris_df.dtypes.astype(str).to_dict()
|
|
81
78
|
print(f'Added ephemeris with {len(ephemeris_df)} records as zarr attributes')
|
|
82
79
|
|
|
83
80
|
print(f'Saved array to {file_path} with maximum compression (zstd-9, chunks={chunks})')
|
|
@@ -212,14 +209,10 @@ def dask_slice_saver(
|
|
|
212
209
|
zarr_path = Path(zarr_path)
|
|
213
210
|
zarr_path.parent.mkdir(parents=True, exist_ok=True)
|
|
214
211
|
|
|
215
|
-
store = zarr.open_group(str(zarr_path), mode='w'
|
|
212
|
+
store = zarr.open_group(str(zarr_path), mode='w')
|
|
216
213
|
|
|
217
214
|
# Configure compression codec
|
|
218
|
-
|
|
219
|
-
cname='zstd',
|
|
220
|
-
clevel=clevel,
|
|
221
|
-
shuffle=numcodecs.Blosc.BITSHUFFLE
|
|
222
|
-
)
|
|
215
|
+
compressors = [{'name': 'blosc', 'configuration': {'cname': 'zstd', 'clevel': clevel, 'shuffle': 'bitshuffle'}}]
|
|
223
216
|
|
|
224
217
|
# Save arrays with optimized compression
|
|
225
218
|
for array_name in required_arrays:
|
|
@@ -240,7 +233,7 @@ def dask_slice_saver(
|
|
|
240
233
|
shape=array_data.shape,
|
|
241
234
|
dtype=array_data.dtype,
|
|
242
235
|
chunks=chunk_shape,
|
|
243
|
-
|
|
236
|
+
compressors=compressors,
|
|
244
237
|
overwrite=True
|
|
245
238
|
)
|
|
246
239
|
zarr_array[:] = array_data
|
|
@@ -497,7 +490,7 @@ def concatenate_slices_efficient(
|
|
|
497
490
|
shutil.rmtree(output_path)
|
|
498
491
|
|
|
499
492
|
# Create output Zarr store and initialize arrays
|
|
500
|
-
print(f'🏗️ Creating output Zarr store at: {output_path}')
|
|
493
|
+
print(f'🏗️ Creating output Zarr store (Using Zarr v{zarr.__version__}) at: {output_path}')
|
|
501
494
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
502
495
|
output_store = zarr.open(str(output_path), mode='w')
|
|
503
496
|
|
|
@@ -588,6 +581,7 @@ class ZarrManager:
|
|
|
588
581
|
self.file_path = file_path
|
|
589
582
|
self.filename = Path(file_path).stem
|
|
590
583
|
self._zarr_array = None
|
|
584
|
+
self._zarr_store = None # Cache for the group/store (for attributes)
|
|
591
585
|
self._metadata = None
|
|
592
586
|
self._ephemeris = None
|
|
593
587
|
self.echoes_shape = self.load().shape if self.load() is not None else None
|
|
@@ -596,11 +590,34 @@ class ZarrManager:
|
|
|
596
590
|
"""
|
|
597
591
|
Load the zarr array and cache it.
|
|
598
592
|
|
|
593
|
+
Handles both Zarr 3.x format (group with 'data' array) and legacy format (direct array).
|
|
594
|
+
|
|
599
595
|
Returns:
|
|
600
596
|
zarr.Array: The loaded zarr array
|
|
601
597
|
"""
|
|
602
598
|
if self._zarr_array is None:
|
|
603
|
-
self.
|
|
599
|
+
self._zarr_store = zarr.open(self.file_path, mode='r')
|
|
600
|
+
# Check if it's a group with a 'data' array (Zarr 3.x format)
|
|
601
|
+
if isinstance(self._zarr_store, zarr.Group) and 'data' in self._zarr_store:
|
|
602
|
+
self._zarr_array = self._zarr_store['data']
|
|
603
|
+
else:
|
|
604
|
+
# Legacy format: direct array
|
|
605
|
+
self._zarr_array = self._zarr_store
|
|
606
|
+
return self._zarr_array
|
|
607
|
+
|
|
608
|
+
def _get_attrs_source(self):
|
|
609
|
+
"""
|
|
610
|
+
Get the object that contains attributes (group for Zarr 3.x, array for legacy).
|
|
611
|
+
|
|
612
|
+
Returns:
|
|
613
|
+
zarr.Group or zarr.Array: The object containing metadata attributes
|
|
614
|
+
"""
|
|
615
|
+
if self._zarr_store is None:
|
|
616
|
+
self.load()
|
|
617
|
+
# For Zarr 3.x format, attributes are on the group
|
|
618
|
+
if isinstance(self._zarr_store, zarr.Group) and 'data' in self._zarr_store:
|
|
619
|
+
return self._zarr_store
|
|
620
|
+
# For legacy format, attributes are on the array
|
|
604
621
|
return self._zarr_array
|
|
605
622
|
|
|
606
623
|
def _create_output_dir(self, output_path: str) -> None:
|
|
@@ -858,10 +875,10 @@ class ZarrManager:
|
|
|
858
875
|
pandas DataFrame containing metadata if available, None otherwise
|
|
859
876
|
"""
|
|
860
877
|
if self._metadata is None:
|
|
861
|
-
|
|
862
|
-
if 'metadata' in
|
|
878
|
+
attrs_source = self._get_attrs_source()
|
|
879
|
+
if 'metadata' in attrs_source.attrs:
|
|
863
880
|
import pandas as pd
|
|
864
|
-
self._metadata = pd.DataFrame(
|
|
881
|
+
self._metadata = pd.DataFrame(attrs_source.attrs['metadata'])
|
|
865
882
|
return self._metadata
|
|
866
883
|
|
|
867
884
|
def get_ephemeris(self) -> Optional['pd.DataFrame']:
|
|
@@ -872,10 +889,11 @@ class ZarrManager:
|
|
|
872
889
|
pandas DataFrame containing ephemeris data if available, None otherwise
|
|
873
890
|
"""
|
|
874
891
|
if self._ephemeris is None:
|
|
875
|
-
|
|
876
|
-
if 'ephemeris' in
|
|
892
|
+
attrs_source = self._get_attrs_source()
|
|
893
|
+
if 'ephemeris' in attrs_source.attrs:
|
|
877
894
|
import pandas as pd
|
|
878
|
-
self._ephemeris = pd.DataFrame(
|
|
895
|
+
self._ephemeris = pd.DataFrame(attrs_source.attrs['ephemeris'])
|
|
896
|
+
return self._ephemeris
|
|
879
897
|
return self._ephemeris
|
|
880
898
|
|
|
881
899
|
@gc_collect
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sarpyx
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.6
|
|
4
4
|
Summary: A swissknife for SAR processing.
|
|
5
5
|
Author-email: Roberto Del Prete <roberto.delprete@esa.int>
|
|
6
|
-
License:
|
|
6
|
+
License: Apache-2.0
|
|
7
7
|
Requires-Python: <3.13,>=3.11
|
|
8
|
+
License-File: LICENSE
|
|
8
9
|
Requires-Dist: matplotlib
|
|
9
10
|
Requires-Dist: scipy
|
|
10
|
-
Requires-Dist: s1isp
|
|
11
11
|
Requires-Dist: geopandas>=1.1.1
|
|
12
12
|
Requires-Dist: shapely
|
|
13
13
|
Requires-Dist: numpy
|
|
@@ -15,9 +15,9 @@ Requires-Dist: openpyxl
|
|
|
15
15
|
Requires-Dist: h5py
|
|
16
16
|
Requires-Dist: numba
|
|
17
17
|
Requires-Dist: jupyter
|
|
18
|
-
Requires-Dist: phidown>=0.1.
|
|
18
|
+
Requires-Dist: phidown>=0.1.24
|
|
19
19
|
Requires-Dist: rasterio>=1.4.3
|
|
20
|
-
Requires-Dist: zarr>=3.0.
|
|
20
|
+
Requires-Dist: zarr>=3.0.0
|
|
21
21
|
Requires-Dist: joblib>=1.5.1
|
|
22
22
|
Requires-Dist: dask>=2025.5.1
|
|
23
23
|
Requires-Dist: lxml>=5.4.0
|
|
@@ -36,3 +36,7 @@ Requires-Dist: tensorboardX>=2.6.4
|
|
|
36
36
|
Requires-Dist: wandb>=0.21.3
|
|
37
37
|
Requires-Dist: scikit-learn>=1.7.2
|
|
38
38
|
Requires-Dist: geographiclib>=2.1
|
|
39
|
+
Requires-Dist: dotenv>=0.9.9
|
|
40
|
+
Requires-Dist: pyarrow>=23.0.0
|
|
41
|
+
Requires-Dist: fastparquet>=2025.12.0
|
|
42
|
+
Dynamic: license-file
|
|
@@ -1,27 +1,32 @@
|
|
|
1
1
|
docs/examples/basic_sublook_analysis.py,sha256=1qUlX5D18F6vFRV0DdzKWvTywZ3Ri94F-th1xqWoz0w,11529
|
|
2
|
-
docs/examples/advanced/batch_processing.py,sha256=
|
|
3
|
-
docs/examples/advanced/custom_processing_chains.py,sha256=
|
|
2
|
+
docs/examples/advanced/batch_processing.py,sha256=LTf1SULZTcWL83Cqhe0e1MSY6IrLsNr-nYozAriVCIk,41432
|
|
3
|
+
docs/examples/advanced/custom_processing_chains.py,sha256=mh4pldE-cz-EFSaQeTZI5XWe5lURKzlBmw7QyXl-f5c,37479
|
|
4
4
|
docs/examples/advanced/insar_time_series.py,sha256=FMQVs9CdgHynnxM_lKRJ_GpJydJALC9uXQK19AvzrmI,39716
|
|
5
|
-
docs/examples/advanced/performance_optimization.py,sha256=
|
|
5
|
+
docs/examples/advanced/performance_optimization.py,sha256=hORCN98gJILBQKiZoxWxHvkNDyCO4N0c3bLl5BcyC_E,41737
|
|
6
6
|
docs/examples/basic/data_io_examples.py,sha256=CHXlZ-rQ-bajP4UikHQN3r4Q_b69WY0clELJ-taiC8w,21499
|
|
7
|
-
docs/examples/basic/snap_integration.py,sha256=
|
|
7
|
+
docs/examples/basic/snap_integration.py,sha256=poDpmdnoQalMI1Mfr8j2KeU66uqluBTR6E6P2AkGkjY,11603
|
|
8
8
|
docs/examples/basic/visualization_gallery.py,sha256=tdo5GxI3o9KgJOj9eOZxjhYaP8FhOj6yz1Ja8K_OuBk,22920
|
|
9
9
|
docs/examples/intermediate/polarimetric_analysis.py,sha256=kQx7i9Wfm_ykxyzUti95f-UuokTL1CLlYZa1Vj4A_dg,31490
|
|
10
|
-
docs/examples/intermediate/quality_assessment.py,sha256=
|
|
10
|
+
docs/examples/intermediate/quality_assessment.py,sha256=UlvdYTvodnWvMxUdSa8AzGL5qLWEueV0lAgB65tMoRs,34450
|
|
11
11
|
docs/examples/intermediate/ship_detection_cfar.py,sha256=W-2duOHp3h_PdHmUaLJcUDTj4lxmXYJGXsG6rxvrLRY,38964
|
|
12
12
|
docs/examples/intermediate/vegetation_monitoring.py,sha256=ENnCyMMzuw04C34Faxoorj5jVtXqF4im9VNXRBlFwws,27686
|
|
13
|
-
|
|
13
|
+
outputs/baseline/20260205-234828/__init__.py,sha256=i4Gs34Wk0L5fVBafr-7Kj1tuV3xsguRxmG4KzKAVZuY,833
|
|
14
|
+
outputs/baseline/20260205-234828/main.py,sha256=f5CkwOd4qEkzy72amOzGZ_t3JR30bycEoXsvr9av9YU,13703
|
|
15
|
+
outputs/final/20260205-234851/__init__.py,sha256=B_bGwcpY66FgpyjZNGR9PvzRFZif0jnvfS6qmBIUos4,833
|
|
16
|
+
outputs/final/20260205-234851/main.py,sha256=uilWF1JZOh_HkvZrXsTSh_T0JXDTzWxTfSD2ZwLWcqc,13703
|
|
17
|
+
sarpyx/__init__.py,sha256=Gsu_bvdPPpnEA10qcDUJrM4fvNUprmmHGZLqg3ZeA3k,964
|
|
14
18
|
sarpyx/algorithms/AdaptiveThresholding.py,sha256=7ycVmHp2WeXqq1Wb2huDmky_Z1FvbtJ_OLvExzcEGK0,5691
|
|
15
|
-
sarpyx/algorithms/__init__.py,sha256=
|
|
16
|
-
sarpyx/cli/__init__.py,sha256=
|
|
19
|
+
sarpyx/algorithms/__init__.py,sha256=C43iB7s5GAN8EqyJ_7yXt96_Bl4x2QD_xygJ6xFNj5M,453
|
|
20
|
+
sarpyx/cli/__init__.py,sha256=Pd4AUeEVoAI-YuWutb-J5mon66TC9iCnuwW8gPQQS2M,369
|
|
17
21
|
sarpyx/cli/decode.py,sha256=vQqOifdcexed3tAPL1Qw2xErGLBNHOnNhUPMoAD__NY,5815
|
|
18
|
-
sarpyx/cli/focus.py,sha256=
|
|
19
|
-
sarpyx/cli/main.py,sha256=
|
|
20
|
-
sarpyx/cli/shipdet.py,sha256=
|
|
22
|
+
sarpyx/cli/focus.py,sha256=gYhVhLSUn55pWKP5g9Pe5cQLeAzjicyRgwKiEcD9LOY,11927
|
|
23
|
+
sarpyx/cli/main.py,sha256=uilWF1JZOh_HkvZrXsTSh_T0JXDTzWxTfSD2ZwLWcqc,13703
|
|
24
|
+
sarpyx/cli/shipdet.py,sha256=RjJ4j4aXSvbthQ5tDuLE6HM6Q2bgjtqp1CjcWVuFX7w,11076
|
|
21
25
|
sarpyx/cli/unzip.py,sha256=fG2WpJPDZ2AoQk1qrpuYP7hLj1mKnxhUNieZKktkVmk,5340
|
|
22
26
|
sarpyx/cli/upload.py,sha256=teXpohily8IYeLVYmWbS9ealBXdJwLH83c1KZU-HVQY,4348
|
|
23
27
|
sarpyx/cli/utils.py,sha256=ewArHsKMB4JEi32fu2gxzouQVwa0afwou0WG6y35o2U,7553
|
|
24
|
-
sarpyx/
|
|
28
|
+
sarpyx/cli/worldsar.py,sha256=Yb8mLwpjKuTdWzgsKsqhqG9aRwWA2tRAMdnXNH5nPMk,18445
|
|
29
|
+
sarpyx/processor/__init__.py,sha256=B_bGwcpY66FgpyjZNGR9PvzRFZif0jnvfS6qmBIUos4,833
|
|
25
30
|
sarpyx/processor/algorithms/__init__.py,sha256=UdXRpIWS_WWFfqGTJIf-YC1cjUKVYHoESeGKxV2SqeQ,410
|
|
26
31
|
sarpyx/processor/algorithms/backprojection.py,sha256=BSz37I0zrbBcbK8XSnHs1DBzo-jQhfzD9-ZQK1XSFLA,526
|
|
27
32
|
sarpyx/processor/algorithms/constants.py,sha256=2G3gOaC7qAhbs_g9RkaGnP__wCW5J5azeoa45pJcRRs,12445
|
|
@@ -31,13 +36,8 @@ sarpyx/processor/core/__init__.py,sha256=K_RdQ87RPU3duTH1jK_gp8VeOV6thqhqDZucRCG
|
|
|
31
36
|
sarpyx/processor/core/aux.py,sha256=9u1uVQpDfXwH6S8hY0aDtrwYa744dr3-kC8ikO8S0Gc,12352
|
|
32
37
|
sarpyx/processor/core/code2physical.py,sha256=dD0niPGjvvhkkKwGswOOWU8mvZiRj1RyjGILfPj8z08,27044
|
|
33
38
|
sarpyx/processor/core/constants.py,sha256=2IglEMxVFQqvROm67sXl54X8nzODfXjqTTmnQtEsfJY,3026
|
|
34
|
-
sarpyx/processor/core/decode.py,sha256=
|
|
35
|
-
sarpyx/processor/core/
|
|
36
|
-
sarpyx/processor/core/decode_v1.py,sha256=bvg5t1ZKlWuI5f2VEe4mJJnuNiXXJoKA0XC4qQqzJLk,36094
|
|
37
|
-
sarpyx/processor/core/focus.py,sha256=Gf53hitsnNSXLv5KbnQSb-u66pnZK7i8nBcs73TY10A,68998
|
|
38
|
-
sarpyx/processor/core/focus_old.py,sha256=76FuP9YUq30cXW46fkCF3qr3th_I1CB253hex9D3wd4,64372
|
|
39
|
-
sarpyx/processor/core/focus_v1.py,sha256=0Eww7syNzuYzjOHfxjOzg-HxiiRgYiCsv0ynozTnYz4,65024
|
|
40
|
-
sarpyx/processor/core/focus_v2.py,sha256=HBC1RJ2UEyGGAMfBA769YlVtsghgovf1BvkiVMrSTsk,67402
|
|
39
|
+
sarpyx/processor/core/decode.py,sha256=o4oEbUCSrXCVEW1uQ1_Nm73Bgd_u0miWpZ03bGqZMSU,40741
|
|
40
|
+
sarpyx/processor/core/focus.py,sha256=vH022rXX4gg5EOwvGaQfP5MIgCoN0lhoUjly9xqnhNA,71943
|
|
41
41
|
sarpyx/processor/core/signal.py,sha256=aVlMS5xa-byabjFq_xBKsv-tcYp2DIef484tX_1W1cE,12327
|
|
42
42
|
sarpyx/processor/core/spectrum.py,sha256=mSG6a-UZu2nRLO8BO5Ethu6SYV4_KT8cN9wKrlBfJz8,36436
|
|
43
43
|
sarpyx/processor/core/subaperture.py,sha256=1jyofcqVHzQB3oQPzQR5K0nCjMqZE06QsqLuMrZlDRc,19899
|
|
@@ -52,29 +52,38 @@ sarpyx/processor/utils/metrics.py,sha256=8wp0yriIQMURmjksRf6lG2kgyIpzS3SVRRdOlqh
|
|
|
52
52
|
sarpyx/processor/utils/summary.py,sha256=bn6LuuhYKrdqjhHkwro_4WYrvG4MU9e7SKIwKpG4NiM,526
|
|
53
53
|
sarpyx/processor/utils/unzip.py,sha256=VvnwPRic42VKmxQm8wlUUuwdlb6z9GOHTqGl9JoVfsI,2131
|
|
54
54
|
sarpyx/processor/utils/viz.py,sha256=9D2KDhlNckIr88zBhYLOGwgqvsws_hgbPKhvBnqCeq4,3396
|
|
55
|
-
sarpyx/science/__init__.py,sha256=
|
|
55
|
+
sarpyx/science/__init__.py,sha256=HwTNdKxnSA05GN9xkdcOkzHO5Z4fQzRxoCIVET-abT4,178
|
|
56
56
|
sarpyx/science/indices.py,sha256=WpsnvvYaK55v8OwmwUakeVHypcYSWUEFMf04zLdh50Y,10428
|
|
57
|
-
sarpyx/sla/__init__.py,sha256=
|
|
57
|
+
sarpyx/sla/__init__.py,sha256=wjRHQKn9n3XATQIxCB0y8VQ7oAXV7fBGfOJUNOyIwWk,798
|
|
58
|
+
sarpyx/sla/metrics.py,sha256=yka7gC6S2SA3oqqfawovxjAz6VTTNJQ_yOU48kOwChk,2880
|
|
58
59
|
sarpyx/sla/utilis.py,sha256=we2VOZM7MtZdD0YaKTjHFchj2Hlu1MP0kNka25170wI,3955
|
|
59
60
|
sarpyx/sla/core/__init__.py,sha256=ptn8ztthve3VP4JabLc7NurmR26U3ljr1mvZaZ6w9Gk,181
|
|
60
61
|
sarpyx/sla/core/meta.py,sha256=lMp-F2G9p_wwzquFXNEQPFSx5ajHz4OGezed9sS-i0E,5166
|
|
61
62
|
sarpyx/sla/core/spectrum.py,sha256=VxVdxxvvHhOD9RnIkZ9383Jr6_0Wgw4Wt04JZPEHmn8,20587
|
|
62
|
-
sarpyx/
|
|
63
|
-
sarpyx/
|
|
64
|
-
sarpyx/
|
|
65
|
-
sarpyx/utils/__init__.py,sha256=
|
|
63
|
+
sarpyx/snapflow/__init__.py,sha256=QzV3XO9S4ljH7fOAVaP8Fx6oK3vMV__naQHSO8W_RL0,195
|
|
64
|
+
sarpyx/snapflow/engine.py,sha256=YMq_zdVf2RVEGS9eF6HOKmkq5W09GawE9ZVnLnkrEQU,253288
|
|
65
|
+
sarpyx/snapflow/op.py,sha256=7Vr_49ro0bmLp9zGmNTi2Hwyj39FLgiw9oYen1FOhuY,9277
|
|
66
|
+
sarpyx/utils/__init__.py,sha256=qrlKX92Y8iZOp1bS5zp3T-sjmHbenjJsGEAHcWWmP2g,630
|
|
66
67
|
sarpyx/utils/complex_losses.py,sha256=RJvWXZ9WMmWqzEIs8VOW6hQFGf5-If_peKrD3uYYXe8,6379
|
|
67
68
|
sarpyx/utils/executor.py,sha256=Rdk4lVBgrSdaUZZgdDEETrlS-ytiOGqMAce4Urn7Jwo,10960
|
|
69
|
+
sarpyx/utils/geos.py,sha256=RLPn9ynsR3rus6cvvkORPQo2IIrcLHKUkHL5cCaYTEk,23017
|
|
70
|
+
sarpyx/utils/grid.py,sha256=8BGgVsDRXY4aSqGo7mJKYBMxfBgpXHEG9ANmQd_Kt48,11330
|
|
68
71
|
sarpyx/utils/hf.py,sha256=AG4A7Cd4dBcevcl_C-4OAl_-CXbYUAA11dpGO5TrH0A,3828
|
|
69
|
-
sarpyx/utils/io.py,sha256=
|
|
72
|
+
sarpyx/utils/io.py,sha256=TlXezDxNEm4PGysSI13i6b6l40MnBMZCaklBBlZ9btA,20302
|
|
70
73
|
sarpyx/utils/losses.py,sha256=0DJnZ1dOF33GjPMDH4R5-X27loE07PZkrg2ldmc4M94,88110
|
|
74
|
+
sarpyx/utils/meta.py,sha256=yuw-uekidlxJrI7Fw-68BLPj2JmXkfT6YrUnCPuVVlA,1552
|
|
71
75
|
sarpyx/utils/metrics.py,sha256=VDz6GNO8HlBQ2A7hlKIfZNPTdd0dcYXmFjMX4d4cdCI,19886
|
|
76
|
+
sarpyx/utils/nisar_utils.py,sha256=VJ5oPb6FDKtYJkWI0Y8c6N53TePT47y_5lWJAnGqWlo,23109
|
|
77
|
+
sarpyx/utils/rfigen.py,sha256=_3v9drsXycjj0SEueoKG3eDgzE4d5hdPs_qr-D0nL-k,4011
|
|
72
78
|
sarpyx/utils/sar_loss.py,sha256=cqxebOoBqJlYOUe87W6eGK97oiV4lgKIoQL9MkOPR0A,23833
|
|
73
79
|
sarpyx/utils/up.py,sha256=ApIjips3SYt3rb4aDo7ljyDBuh8i5pY4i9qo__o3tA4,2590
|
|
74
80
|
sarpyx/utils/viz.py,sha256=2F7T7JtwLbA2E2KtQCE7NnR6U88p9d7W3y10dvzFGzU,3991
|
|
75
|
-
sarpyx/utils/
|
|
76
|
-
sarpyx
|
|
77
|
-
sarpyx-0.1.
|
|
78
|
-
|
|
79
|
-
sarpyx-0.1.
|
|
80
|
-
sarpyx-0.1.
|
|
81
|
+
sarpyx/utils/wkt_utils.py,sha256=Xn4sY2uBWtAnQKfqC0xvnF95RBeR44rAJC-T6skf9zQ,4048
|
|
82
|
+
sarpyx/utils/zarr_utils.py,sha256=fP-7d-3POTUn61BtZyLgswCDYm4ow-CZRdJPx12ZRWA,65276
|
|
83
|
+
sarpyx-0.1.6.dist-info/licenses/LICENSE,sha256=TVOR4tTt739YHIPw1bKRjoeiRBtfcd4L63N17sV98G0,11351
|
|
84
|
+
tests/test_zarr_compat.py,sha256=-0iF-WBy0bzugxHvc2j8M2-AExghrM2Ky-2FyU0FUR8,1217
|
|
85
|
+
sarpyx-0.1.6.dist-info/METADATA,sha256=nfeNI516iuuT1ZkqHuhuR14Tw3bwn3-tJdfzxwn5qZo,1217
|
|
86
|
+
sarpyx-0.1.6.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
87
|
+
sarpyx-0.1.6.dist-info/entry_points.txt,sha256=KaSUkL_cvQIA-8nodwgik9UfA2h-07nluy2DKgPeE6E,241
|
|
88
|
+
sarpyx-0.1.6.dist-info/top_level.txt,sha256=amk2m5OI2lU-hX4eS5WRP-YoUiT5q77jr1cC3NGi5cU,26
|
|
89
|
+
sarpyx-0.1.6.dist-info/RECORD,,
|