insardev 2025.2.20.dev0__tar.gz
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.
- insardev-2025.2.20.dev0/LICENSE.txt +29 -0
- insardev-2025.2.20.dev0/PKG-INFO +151 -0
- insardev-2025.2.20.dev0/insardev/Stack.py +106 -0
- insardev-2025.2.20.dev0/insardev/Stack_base.py +149 -0
- insardev-2025.2.20.dev0/insardev/Stack_detrend.py +1214 -0
- insardev-2025.2.20.dev0/insardev/Stack_export.py +591 -0
- insardev-2025.2.20.dev0/insardev/Stack_incidence.py +274 -0
- insardev-2025.2.20.dev0/insardev/Stack_lstsq.py +471 -0
- insardev-2025.2.20.dev0/insardev/Stack_multilooking.py +184 -0
- insardev-2025.2.20.dev0/insardev/Stack_phasediff.py +560 -0
- insardev-2025.2.20.dev0/insardev/Stack_ps.py +220 -0
- insardev-2025.2.20.dev0/insardev/Stack_sbas.py +626 -0
- insardev-2025.2.20.dev0/insardev/Stack_stl.py +222 -0
- insardev-2025.2.20.dev0/insardev/Stack_unwrap.py +589 -0
- insardev-2025.2.20.dev0/insardev/Stack_unwrap_snaphu.py +216 -0
- insardev-2025.2.20.dev0/insardev/__init__.py +15 -0
- insardev-2025.2.20.dev0/insardev/dataset.py +454 -0
- insardev-2025.2.20.dev0/insardev/utils.py +136 -0
- insardev-2025.2.20.dev0/insardev.egg-info/PKG-INFO +151 -0
- insardev-2025.2.20.dev0/insardev.egg-info/SOURCES.txt +23 -0
- insardev-2025.2.20.dev0/insardev.egg-info/dependency_links.txt +1 -0
- insardev-2025.2.20.dev0/insardev.egg-info/requires.txt +16 -0
- insardev-2025.2.20.dev0/insardev.egg-info/top_level.txt +1 -0
- insardev-2025.2.20.dev0/setup.cfg +4 -0
- insardev-2025.2.20.dev0/setup.py +80 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025, Alexey Pechnikov, https://orcid.org/0000-0001-9626-8615 (ORCID)
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
* Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
* Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: insardev
|
|
3
|
+
Version: 2025.2.20.dev0
|
|
4
|
+
Summary: InSAR.dev (Python InSAR): Satellite Interferometry Framework
|
|
5
|
+
Home-page: https://github.com/AlexeyPechnikov/pygmtsar
|
|
6
|
+
Author: Alexey Pechnikov
|
|
7
|
+
Author-email: alexey@pechnikov.dev
|
|
8
|
+
License: BSD-3-Clause
|
|
9
|
+
Keywords: satellite interferometry,InSAR,remote sensing,geospatial analysis,Sentinel-1,SBAS,PSI
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: Natural Language :: English
|
|
13
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
14
|
+
Classifier: Operating System :: POSIX
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Classifier: Operating System :: MacOS
|
|
17
|
+
Classifier: Programming Language :: Python
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE.txt
|
|
25
|
+
Requires-Dist: insardev_toolkit
|
|
26
|
+
Requires-Dist: xarray
|
|
27
|
+
Requires-Dist: numpy
|
|
28
|
+
Requires-Dist: numba
|
|
29
|
+
Requires-Dist: pandas
|
|
30
|
+
Requires-Dist: geopandas
|
|
31
|
+
Requires-Dist: distributed
|
|
32
|
+
Requires-Dist: dask[complete]
|
|
33
|
+
Requires-Dist: scipy
|
|
34
|
+
Requires-Dist: xgboost
|
|
35
|
+
Requires-Dist: cffi
|
|
36
|
+
Requires-Dist: scikit-learn
|
|
37
|
+
Requires-Dist: statsmodels>=0.14.0
|
|
38
|
+
Requires-Dist: matplotlib
|
|
39
|
+
Requires-Dist: adjustText
|
|
40
|
+
Requires-Dist: seaborn
|
|
41
|
+
Dynamic: author
|
|
42
|
+
Dynamic: author-email
|
|
43
|
+
Dynamic: classifier
|
|
44
|
+
Dynamic: description
|
|
45
|
+
Dynamic: description-content-type
|
|
46
|
+
Dynamic: home-page
|
|
47
|
+
Dynamic: keywords
|
|
48
|
+
Dynamic: license
|
|
49
|
+
Dynamic: requires-dist
|
|
50
|
+
Dynamic: requires-python
|
|
51
|
+
Dynamic: summary
|
|
52
|
+
|
|
53
|
+
[](https://github.com/AlexeyPechnikov/pygmtsar)
|
|
54
|
+
[](https://pypi.python.org/pypi/pygmtsar/)
|
|
55
|
+
[](https://hub.docker.com/r/pechnikov/pygmtsar)
|
|
56
|
+
[](https://zenodo.org/badge/latestdoi/398018212)
|
|
57
|
+
[](https://www.patreon.com/pechnikov)
|
|
58
|
+
[](https://insar.dev/ai)
|
|
59
|
+
|
|
60
|
+
## PyGMTSAR (Python InSAR): Powerful, Accessible Satellite Interferometry
|
|
61
|
+
|
|
62
|
+
<img src="assets/logo.jpg" width="15%" />
|
|
63
|
+
|
|
64
|
+
PyGMTSAR (Python InSAR) is designed for both occasional users and experts working with Sentinel-1 satellite interferometry. It supports a wide range of features, including SBAS, PSI, PSI-SBAS, and more. In addition to the examples below, you’ll find more Jupyter notebook use cases on [Patreon](https://www.patreon.com/pechnikov) and updates on [LinkedIn](https://www.linkedin.com/in/alexey-pechnikov/).
|
|
65
|
+
|
|
66
|
+
## About PyGMTSAR
|
|
67
|
+
|
|
68
|
+
PyGMTSAR offers reproducible, high-performance Sentinel-1 interferometry accessible to everyone—whether you prefer Google Colab, cloud servers, or local processing. It automatically retrieves Sentinel-1 SLC scenes and bursts, DEMs, and orbits; computes interferograms and correlations; performs time-series analysis; and provides 3D visualization. This single library enables users to build a fully integrated InSAR project with minimal hassle. Whether you need a single interferogram or a multi-year analysis involving thousands of datasets, PyGMTSAR can handle the task efficiently, even on standard commodity hardware.
|
|
69
|
+
|
|
70
|
+
## PyGMTSAR Live Examples on Google Colab
|
|
71
|
+
|
|
72
|
+
Google Colab is a free service that lets you run interactive notebooks directly in your browser—no powerful computer, extensive disk space, or special installations needed. You can even do InSAR processing from a smartphone. These notebooks automate every step: installing PyGMTSAR library and its dependencies on a Colab host (Ubuntu 22, Python 3.10), downloading Sentinel-1 SLCs, orbit files, SRTM DEM data (automatically converted to ellipsoidal heights via EGM96), land mask data, and then performing complete interferometry with final mapping. You can also modify scene or bursts names to analyze your own area of interest, and each notebook includes instant interactive 3D maps.
|
|
73
|
+
|
|
74
|
+
[](https://colab.research.google.com/drive/1TARVTB7z8goZyEVDRWyTAKJpyuqZxzW2?usp=sharing) **Central Türkiye Earthquakes (2023).** The area is large, covering two consecutive Sentinel-1 scenes or a total of 56 bursts.
|
|
75
|
+
|
|
76
|
+
<img src="assets/turkie_2023a.jpg" width="40%" /><img src="assets/turkie_2023b.jpg" width="40%" />
|
|
77
|
+
|
|
78
|
+
[](https://colab.research.google.com/drive/1dDFG8BoF4WfB6tOF5sAi5mjdBKRbhxHo?usp=sharing) **Pico do Fogo Volcano Eruption, Fogo Island, Cape Verde (2014).** The interferogram for this event is compared to the study *The 2014–2015 eruption of Fogo volcano: Geodetic modeling of Sentinel-1 TOPS interferometry* (*Geophysical Research Letters*, DOI: [10.1002/2015GL066003](https://doi.org/10.1002/2015GL066003)).
|
|
79
|
+
|
|
80
|
+
<img src="assets/pico_2014a.jpg" width="40%" /><img src="assets/pico_2014b.jpg" width="40%" />
|
|
81
|
+
|
|
82
|
+
[](https://colab.research.google.com/drive/1d9RcqBmWIKQDEwJYo8Dh6M4tMjJtvseC?usp=sharing) **La Cumbre Volcano Eruption, Ecuador (2020).** The results compare with the report from Instituto Geofísico, Escuela Politécnica Nacional (IG-EPN) (InSAR software unspecified).
|
|
83
|
+
|
|
84
|
+
<img src="assets/la_cumbre_2020a.jpg" width="40%" /><img src="assets/la_cumbre_2020b.jpg" width="40%" />
|
|
85
|
+
|
|
86
|
+
[](https://colab.research.google.com/drive/1shNGvUlUiXeyV7IcTmDbWaEM6XrB0014?usp=sharing) **Iran–Iraq Earthquake (2017).** The event has been well investigated, and the results compared to outputs from GMTSAR, SNAP, and GAMMA software.
|
|
87
|
+
|
|
88
|
+
<img src="assets/iran_iraq_2017a.jpg" width="40%" /><img src="assets/iran_iraq_2017b.jpg" width="40%" />
|
|
89
|
+
|
|
90
|
+
[](https://colab.research.google.com/drive/1h4XxJZwFfm7EC8NUzl34cCkOVUG2uJr4?usp=sharing) **Imperial Valley Subsidence, CA USA (2015).** This example is provided in the [GMTSAR project](https://topex.ucsd.edu/gmtsar/downloads/) in the archive file [S1A_Stack_CPGF_T173.tar.gz](http://topex.ucsd.edu/gmtsar/tar/S1A_Stack_CPGF_T173.tar.gz), titled 'Sentinel-1 TOPS Time Series'.
|
|
91
|
+
|
|
92
|
+
The resulting InSAR velocity map is available as a self-contained web page at: [Imperial_Valley_2015.html](https://insar.dev/ui/Imperial_Valley_2015.html)
|
|
93
|
+
|
|
94
|
+
<img src="assets/imperial_valley_2015a.jpg" width="40%" /> <img src="assets/imperial_valley_2015b.jpg" width="40%" />
|
|
95
|
+
|
|
96
|
+
[](https://colab.research.google.com/drive/1aqAr9KWKzGx9XpVie1M000C3vUxzNDxu?usp=sharing) **Kalkarindji Flooding, NT Australia (2024).** Correlation loss serves to identify flooded areas.
|
|
97
|
+
|
|
98
|
+
<img src="assets/kalkarindji_2024.jpg" width="80%" />
|
|
99
|
+
|
|
100
|
+
[](https://colab.research.google.com/drive/1ipiQGbvUF8duzjZER8v-_R48DSpSmgvQ?usp=sharing) **Golden Valley Subsidence, CA USA (2021).** This example demonstrates the case study 'Antelope Valley Freeway in Santa Clarita, CA,' as detailed in [SAR Technical Series Part 4 Sentinel-1 global velocity layer: Using global InSAR at scale](https://blog.descarteslabs.com/using-global-insar-at-scale) and [Sentinel-1 Technical Series Part 5 Targeted Analysis](https://blog.descarteslabs.com/sentinel-1-targeted-analysis) with a significant subsidence rate 'exceeding 5cm/year in places'.
|
|
101
|
+
|
|
102
|
+
<img src="assets/golden_valley_2021.jpg" width="80%" />
|
|
103
|
+
|
|
104
|
+
[](https://colab.research.google.com/drive/1O3aZtZsTrQIldvCqlVRel13wJRLhmTJt?usp=sharing) **Lake Sarez Landslides, Tajikistan (2017).** The example reproduces the findings shared in the following paper: [Integration of satellite SAR and optical acquisitions for the characterization of the Lake Sarez landslides in Tajikistan](https://www.google.com/url?q=https%3A%2F%2Fwww.researchgate.net%2Fpublication%2F378176884_Integration_of_satellite_SAR_and_optical_acquisitions_for_the_characterization_of_the_Lake_Sarez_landslides_in_Tajikistan).
|
|
105
|
+
|
|
106
|
+
<img src="assets/lake_sarez_2017.jpg" width="80%" />
|
|
107
|
+
|
|
108
|
+
[](https://colab.research.google.com/drive/19PLuebOZ4gaYX5ym1H7SwUbJKfl23qPr?usp=sharing) **Erzincan Elevation, Türkiye (2019).** This example reproduces 29-page ESA document [DEM generation with Sentinel-1 IW](https://step.esa.int/docs/tutorials/S1TBX%20DEM%20generation%20with%20Sentinel-1%20IW%20Tutorial.pdf).
|
|
109
|
+
|
|
110
|
+
<img src="assets/erzincan_2019.jpg" width="80%" />
|
|
111
|
+
|
|
112
|
+
## More PyGMTSAR Live Examples on Google Colab
|
|
113
|
+
|
|
114
|
+
[](https://colab.research.google.com/drive/1yuuA1ES2ly4QG3hyPg8YYT0nnpGDiQDw?usp=sharing) **Mexico City Subsidence, Mexico (2016).** This example replicates the 29-page ESA manual [TRAINING KIT – HAZA03. LAND SUBSIDENCE WITH SENTINEL-1 using SNAP](https://eo4society.esa.int/wp-content/uploads/2022/01/HAZA03_Land-Subsidence_Mexico-city.pdf).
|
|
115
|
+
|
|
116
|
+
## PyGMTSAR Live Examples on Google Colab Pro
|
|
117
|
+
|
|
118
|
+
I share additional InSAR projects on Google Colab Pro through my [Patreon page](https://www.patreon.com/pechnikov). These are ideal for InSAR learners, researchers, and industry professionals tackling challenging projects with large areas, big stacks of interferograms, low-coherence regions, or significant atmospheric delays. You can run these privately shared notebooks online with Colab Pro or locally/on remote servers.
|
|
119
|
+
|
|
120
|
+
## Projects and Publications Using PyGMTSAR
|
|
121
|
+
|
|
122
|
+
See the [Projects and Publications](/pubs/README.md) page for real-world projects and academic research applying PyGMTSAR. This is not an exhaustive list—contact me if you’d like your project or publication included.
|
|
123
|
+
|
|
124
|
+
## Resources
|
|
125
|
+
|
|
126
|
+
**PyGMTSAR projects and e-books**
|
|
127
|
+
Available on [Patreon](https://www.patreon.com/c/pechnikov/shop). Preview versions can be found in this GitHub repo:
|
|
128
|
+
|
|
129
|
+
- [PyGMTSAR Introduction Preview](https://github.com/AlexeyPechnikov/pygmtsar/blob/pygmtsar2/book/PyGMTSAR_preview.pdf)
|
|
130
|
+
- [PyGMTSAR Gaussian Filtering Preview](https://github.com/AlexeyPechnikov/pygmtsar/blob/pygmtsar2/book/Gaussian_preview.pdf)
|
|
131
|
+
|
|
132
|
+
<img src="assets/listing.jpg" width="40%" />
|
|
133
|
+
|
|
134
|
+
**Video Lessons and Notebooks**
|
|
135
|
+
Find PyGMTSAR (Python InSAR) video lessons and educational notebooks on [Patreon](https://www.patreon.com/collection/12458) and [YouTube](https://www.youtube.com/channel/UCSEeXKAn9f_bDiTjT6l87Lg).
|
|
136
|
+
|
|
137
|
+
**PyGMTSAR AI Assistant**
|
|
138
|
+
The [PyGMTSAR AI Assistant](https://insar.dev/ai), powered by OpenAI ChatGPT, can explain InSAR theory, guide you through examples, help build an InSAR processing pipeline, and troubleshoot.
|
|
139
|
+
|
|
140
|
+
<img width="40%" alt="PyGMTSAR AI Assistant" src="assets/ai.jpg" />
|
|
141
|
+
|
|
142
|
+
**PyGMTSAR on DockerHub**
|
|
143
|
+
Run InSAR processing on macOS, Linux, or Windows via [Docker images](https://hub.docker.com/r/pechnikov/pygmtsar).
|
|
144
|
+
|
|
145
|
+
**PyGMTSAR on PyPI**
|
|
146
|
+
Install the library from [PyPI](https://pypi.python.org/pypi/pygmtsar).
|
|
147
|
+
|
|
148
|
+
**PyGMTSAR Previous Versions**
|
|
149
|
+
2023 releases are still on GitHub, PyPI, DockerHub, and Google Colab. Compare PyGMTSAR InSAR with other software by checking out the [PyGMTSAR 2023 Repository](https://github.com/AlexeyPechnikov/pygmtsar/tree/pygmtsar).
|
|
150
|
+
|
|
151
|
+
© Alexey Pechnikov, 2025
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# ----------------------------------------------------------------------------
|
|
2
|
+
# PyGMTSAR
|
|
3
|
+
#
|
|
4
|
+
# This file is part of the PyGMTSAR project: https://github.com/mobigroup/gmtsar
|
|
5
|
+
#
|
|
6
|
+
# Copyright (c) 2025, Alexey Pechnikov
|
|
7
|
+
#
|
|
8
|
+
# Licensed under the BSD 3-Clause License (see LICENSE for details)
|
|
9
|
+
# ----------------------------------------------------------------------------
|
|
10
|
+
from .Stack_export import Stack_export
|
|
11
|
+
|
|
12
|
+
class Stack(Stack_export):
|
|
13
|
+
|
|
14
|
+
def __repr__(self):
|
|
15
|
+
return 'Object %s %d bursts %d dates' % (self.__class__.__name__, len(self.ds), len(self.ds[0].date))
|
|
16
|
+
|
|
17
|
+
def to_dataset(self):
|
|
18
|
+
import numpy as np
|
|
19
|
+
import xarray as xr
|
|
20
|
+
data = xr.concat(xr.align(*self.ds, join='outer'), dim='stack_dim').mean('stack_dim')
|
|
21
|
+
return data
|
|
22
|
+
|
|
23
|
+
def __init__(self, basedir, pattern_burst='*_*_?W?', pattern_date = '????????.nc', scale = 2.5e-07):
|
|
24
|
+
"""
|
|
25
|
+
Initialize an instance of the Stack class.
|
|
26
|
+
"""
|
|
27
|
+
import numpy as np
|
|
28
|
+
import xarray as xr
|
|
29
|
+
import glob
|
|
30
|
+
import os
|
|
31
|
+
|
|
32
|
+
self.basedir = basedir
|
|
33
|
+
|
|
34
|
+
bursts = glob.glob(pattern_burst, root_dir=self.basedir)
|
|
35
|
+
datas = []
|
|
36
|
+
for burst in bursts:
|
|
37
|
+
data = xr.open_mfdataset(
|
|
38
|
+
os.path.join(self.basedir, burst, pattern_date),
|
|
39
|
+
engine=self.netcdf_engine_read,
|
|
40
|
+
format=self.netcdf_format,
|
|
41
|
+
parallel=True,
|
|
42
|
+
concat_dim='date',
|
|
43
|
+
chunks={'date': 1, 'y': self.chunksize, 'x': self.chunksize},
|
|
44
|
+
combine='nested',
|
|
45
|
+
)
|
|
46
|
+
#print (data)
|
|
47
|
+
# zero in np.int16 type means NODATA
|
|
48
|
+
data = self.spatial_ref(
|
|
49
|
+
scale*(data.re.astype(np.float32) + 1j*data.im.astype(np.float32)).where(data.re != 0).rename('data'),
|
|
50
|
+
data
|
|
51
|
+
)\
|
|
52
|
+
.to_dataset(name='data')\
|
|
53
|
+
.assign({v: data[v] for v in data.data_vars if v not in ['im', 're']})\
|
|
54
|
+
.assign_attrs({'burst': burst})
|
|
55
|
+
datas.append(data)
|
|
56
|
+
del data
|
|
57
|
+
|
|
58
|
+
self.ds = datas
|
|
59
|
+
|
|
60
|
+
def baseline_table(self):
|
|
61
|
+
import xarray as xr
|
|
62
|
+
return xr.concat([ds.BPR for ds in self.ds], dim='burst').mean('burst').to_dataframe()[['BPR']]
|
|
63
|
+
|
|
64
|
+
def baseline_pairs(self, days=None, meters=None, invert=False):
|
|
65
|
+
"""
|
|
66
|
+
Generates a sorted list of baseline pairs.
|
|
67
|
+
Returns
|
|
68
|
+
-------
|
|
69
|
+
pandas.DataFrame
|
|
70
|
+
A DataFrame containing the sorted list of baseline pairs with reference and repeat dates,
|
|
71
|
+
timelines, and baselines.
|
|
72
|
+
|
|
73
|
+
"""
|
|
74
|
+
import numpy as np
|
|
75
|
+
import pandas as pd
|
|
76
|
+
|
|
77
|
+
if days is None:
|
|
78
|
+
# use large number for unlimited time interval in days
|
|
79
|
+
days = 1e6
|
|
80
|
+
|
|
81
|
+
tbl = self.baseline_table()
|
|
82
|
+
data = []
|
|
83
|
+
for line1 in tbl.itertuples():
|
|
84
|
+
counter = 0
|
|
85
|
+
for line2 in tbl.itertuples():
|
|
86
|
+
#print (line1, line2)
|
|
87
|
+
if not (line1.Index < line2.Index and (line2.Index - line1.Index).days < days + 1):
|
|
88
|
+
continue
|
|
89
|
+
if meters is not None and not (abs(line1.BPR - line2.BPR)< meters + 1):
|
|
90
|
+
continue
|
|
91
|
+
|
|
92
|
+
counter += 1
|
|
93
|
+
if not invert:
|
|
94
|
+
data.append({'ref':line1.Index, 'rep': line2.Index,
|
|
95
|
+
'ref_baseline': np.round(line1.BPR, 2),
|
|
96
|
+
'rep_baseline': np.round(line2.BPR, 2)})
|
|
97
|
+
else:
|
|
98
|
+
data.append({'ref':line2.Index, 'rep': line1.Index,
|
|
99
|
+
'ref_baseline': np.round(line2.BPR, 2),
|
|
100
|
+
'rep_baseline': np.round(line1.BPR, 2)})
|
|
101
|
+
|
|
102
|
+
df = pd.DataFrame(data).sort_values(['ref', 'rep'])
|
|
103
|
+
return df.assign(pair=[f'{ref} {rep}' for ref, rep in zip(df['ref'].dt.date, df['rep'].dt.date)],
|
|
104
|
+
baseline=df.rep_baseline - df.ref_baseline,
|
|
105
|
+
duration=(df['rep'] - df['ref']).dt.days,
|
|
106
|
+
rel=np.datetime64('nat'))
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# ----------------------------------------------------------------------------
|
|
2
|
+
# InSAR.dev
|
|
3
|
+
#
|
|
4
|
+
# This file is part of the InSAR.dev project: https://InSAR.dev
|
|
5
|
+
#
|
|
6
|
+
# Copyright (c) 2025, Alexey Pechnikov
|
|
7
|
+
#
|
|
8
|
+
# Licensed under the BSD 3-Clause License (see LICENSE for details)
|
|
9
|
+
# ----------------------------------------------------------------------------
|
|
10
|
+
from insardev_toolkit import tqdm_joblib, tqdm_dask
|
|
11
|
+
from .dataset import dataset
|
|
12
|
+
|
|
13
|
+
class Stack_base(tqdm_joblib, dataset):
|
|
14
|
+
|
|
15
|
+
def get_pairs(self, pairs, dates=False):
|
|
16
|
+
"""
|
|
17
|
+
Get pairs as DataFrame and optionally dates array.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
pairs : np.ndarray, optional
|
|
22
|
+
An array of pairs. If None, all pairs are considered. Default is None.
|
|
23
|
+
dates : bool, optional
|
|
24
|
+
Whether to return dates array. Default is False.
|
|
25
|
+
name : str, optional
|
|
26
|
+
The name of the phase filter. Default is 'phasefilt'.
|
|
27
|
+
|
|
28
|
+
Returns
|
|
29
|
+
-------
|
|
30
|
+
pd.DataFrame or tuple
|
|
31
|
+
A DataFrame of pairs. If dates is True, also returns an array of dates.
|
|
32
|
+
"""
|
|
33
|
+
import xarray as xr
|
|
34
|
+
import pandas as pd
|
|
35
|
+
import numpy as np
|
|
36
|
+
from glob import glob
|
|
37
|
+
|
|
38
|
+
if isinstance(pairs, pd.DataFrame):
|
|
39
|
+
# workaround for baseline_pairs() output
|
|
40
|
+
pairs = pairs.rename(columns={'ref_date': 'ref', 'rep_date': 'rep'})
|
|
41
|
+
elif isinstance(pairs, (xr.DataArray, xr.Dataset)):
|
|
42
|
+
# pairs = pd.DataFrame({
|
|
43
|
+
# 'ref': pairs.coords['ref'].values,
|
|
44
|
+
# 'rep': pairs.coords['rep'].values
|
|
45
|
+
# })
|
|
46
|
+
refs = pairs.coords['ref'].values
|
|
47
|
+
reps = pairs.coords['rep'].values
|
|
48
|
+
pairs = pd.DataFrame({
|
|
49
|
+
'ref': refs if isinstance(refs, np.ndarray) else [refs],
|
|
50
|
+
'rep': reps if isinstance(reps, np.ndarray) else [reps]
|
|
51
|
+
})
|
|
52
|
+
else:
|
|
53
|
+
# Convert numpy array to DataFrame
|
|
54
|
+
# in case of 1d array with 2 items convert to a single pair
|
|
55
|
+
pairs_2d = [pairs] if np.asarray(pairs).shape == (2,) else pairs
|
|
56
|
+
pairs = pd.DataFrame(pairs_2d, columns=['ref', 'rep'])
|
|
57
|
+
|
|
58
|
+
# Convert ref and rep columns to datetime format
|
|
59
|
+
pairs['ref'] = pd.to_datetime(pairs['ref'])
|
|
60
|
+
pairs['rep'] = pd.to_datetime(pairs['rep'])
|
|
61
|
+
pairs['pair'] = [f'{ref} {rep}' for ref, rep in zip(pairs['ref'].dt.date, pairs['rep'].dt.date)]
|
|
62
|
+
# Calculate the duration in days and add it as a new column
|
|
63
|
+
#pairs['duration'] = (pairs['rep'] - pairs['ref']).dt.days
|
|
64
|
+
|
|
65
|
+
if dates:
|
|
66
|
+
# pairs is DataFrame
|
|
67
|
+
dates = np.unique(pairs[['ref', 'rep']].astype(str).values.flatten())
|
|
68
|
+
return (pairs, dates)
|
|
69
|
+
return pairs
|
|
70
|
+
|
|
71
|
+
def get_pairs_matrix(self, pairs):
|
|
72
|
+
"""
|
|
73
|
+
Create a matrix based on interferogram dates and pairs.
|
|
74
|
+
|
|
75
|
+
Parameters
|
|
76
|
+
----------
|
|
77
|
+
pairs : pandas.DataFrame or xarray.DataArray or xarray.Dataset
|
|
78
|
+
DataFrame or DataArray containing interferogram date pairs.
|
|
79
|
+
|
|
80
|
+
Returns
|
|
81
|
+
-------
|
|
82
|
+
numpy.ndarray
|
|
83
|
+
A matrix with one row for every interferogram and one column for every date.
|
|
84
|
+
Each element in the matrix is a float, with 1 indicating the start date,
|
|
85
|
+
-1 indicating the end date, 0 if the date is covered by the corresponding
|
|
86
|
+
interferogram timeline, and NaN otherwise.
|
|
87
|
+
|
|
88
|
+
"""
|
|
89
|
+
import numpy as np
|
|
90
|
+
import pandas as pd
|
|
91
|
+
|
|
92
|
+
# also define image capture dates from interferogram date pairs
|
|
93
|
+
pairs, dates = self.get_pairs(pairs, dates=True)
|
|
94
|
+
pairs = pairs[['ref', 'rep']].astype(str).values
|
|
95
|
+
|
|
96
|
+
# here are one row for every interferogram and one column for every date
|
|
97
|
+
matrix = []
|
|
98
|
+
for pair in pairs:
|
|
99
|
+
#mrow = [date>=pair[0] and date<=pair[1] for date in dates]
|
|
100
|
+
mrow = [(-1 if date==pair[0] else (1 if date==pair[1] else (0 if date>pair[0] and date<pair[1] else np.nan))) for date in dates]
|
|
101
|
+
matrix.append(mrow)
|
|
102
|
+
matrix = np.stack(matrix).astype(np.float32)
|
|
103
|
+
return matrix
|
|
104
|
+
|
|
105
|
+
@staticmethod
|
|
106
|
+
def phase_to_positive_range(phase):
|
|
107
|
+
"""
|
|
108
|
+
Convert phase from the range [-pi, pi] to [0, 2pi].
|
|
109
|
+
|
|
110
|
+
Parameters
|
|
111
|
+
----------
|
|
112
|
+
phase : array_like
|
|
113
|
+
Input phase values in the range [-pi, pi].
|
|
114
|
+
|
|
115
|
+
Returns
|
|
116
|
+
-------
|
|
117
|
+
ndarray
|
|
118
|
+
Phase values converted to the range [0, 2pi].
|
|
119
|
+
|
|
120
|
+
Examples
|
|
121
|
+
--------
|
|
122
|
+
>>> phase_to_positive_range(np.array([-np.pi, -np.pi/2, np.pi, 2*-np.pi-1e-6, 2*-np.pi]))
|
|
123
|
+
array([3.14159265, 4.71238898, 3.14159265, 6.28318431, 0. ])
|
|
124
|
+
"""
|
|
125
|
+
import numpy as np
|
|
126
|
+
return (phase + 2 * np.pi) % (2 * np.pi)
|
|
127
|
+
|
|
128
|
+
@staticmethod
|
|
129
|
+
def phase_to_symmetric_range(phase):
|
|
130
|
+
"""
|
|
131
|
+
Convert phase from the range [0, 2pi] to [-pi, pi].
|
|
132
|
+
|
|
133
|
+
Parameters
|
|
134
|
+
----------
|
|
135
|
+
phase : array_like
|
|
136
|
+
Input phase values in the range [0, 2pi].
|
|
137
|
+
|
|
138
|
+
Returns
|
|
139
|
+
-------
|
|
140
|
+
ndarray
|
|
141
|
+
Phase values converted to the range [-pi, pi].
|
|
142
|
+
|
|
143
|
+
Examples
|
|
144
|
+
--------
|
|
145
|
+
>>> phase_to_symmetric_range(np.array([0, np.pi, 3*np.pi/2, 2*np.pi]))
|
|
146
|
+
array([ 0. , 3.14159265, -1.57079633, 0. ])
|
|
147
|
+
"""
|
|
148
|
+
import numpy as np
|
|
149
|
+
return (phase + np.pi) % (2 * np.pi) - np.pi
|