Skip to content

Commit 458a549

Browse files
[pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
1 parent 212b11a commit 458a549

19 files changed

Lines changed: 240 additions & 201 deletions

docs/example_compare_sources.ipynb

Lines changed: 72 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
"cells": [
33
{
44
"cell_type": "markdown",
5-
"metadata": {},
65
"id": "26faf28f-336d-4ced-9be8-4217ef2deea7",
6+
"metadata": {},
77
"source": [
88
"# Cross-source comparison\n",
99
"\n",
@@ -20,24 +20,80 @@
2020
},
2121
{
2222
"cell_type": "markdown",
23-
"metadata": {},
2423
"id": "76c2bc1a-3c6c-4c4b-afb9-e979957bfcfe",
24+
"metadata": {},
2525
"source": [
2626
"## Load the longest-baseline timeseries TIFF from each run"
2727
]
2828
},
2929
{
3030
"cell_type": "code",
31-
"metadata": {},
32-
"id": "862df8b7-dddd-4dca-a7a8-6e94e9a1da0c",
3331
"execution_count": null,
32+
"id": "862df8b7-dddd-4dca-a7a8-6e94e9a1da0c",
33+
"metadata": {},
3434
"outputs": [],
35-
"source": "import re\nfrom datetime import datetime\nfrom pathlib import Path\n\nimport numpy as np\nimport rasterio\nimport matplotlib.pyplot as plt\n\n# Each per-source example notebook writes its outputs to ./example_<source>\n# relative to whatever directory it was launched from. Run this notebook from\n# the same CWD as those three notebooks.\nRUNS_DIR = Path(\".\")\nSOURCES = {\n \"Sentinel-1 bursts\": RUNS_DIR / \"example_s1_burst\",\n \"OPERA CSLC\": RUNS_DIR / \"example_opera_cslc\",\n \"NISAR GSLC\": RUNS_DIR / \"example_nisar\",\n}\n\nPAIR_RE = re.compile(r\"^(\\d{8})_(\\d{8})\\.tif$\")\n\ndef longest_pair(ts_dir: Path) -> tuple[Path, datetime, datetime]:\n \"\"\"Return the `YYYYMMDD_YYYYMMDD.tif` with the widest baseline.\"\"\"\n best = None\n best_span = -1\n for p in ts_dir.glob(\"*.tif\"):\n m = PAIR_RE.match(p.name)\n if not m:\n continue\n d1 = datetime.strptime(m.group(1), \"%Y%m%d\")\n d2 = datetime.strptime(m.group(2), \"%Y%m%d\")\n span = (d2 - d1).days\n if span > best_span:\n best_span = span\n best = (p, d1, d2)\n assert best is not None, f\"no pair-baseline TIFFs in {ts_dir}\"\n return best\n\ndef load_raster(path: Path):\n with rasterio.open(path) as src:\n return {\n \"data\": src.read(1, masked=True),\n \"bounds\": src.bounds,\n \"unit\": (src.units[0] if src.units else \"?\") or \"?\",\n }\n\nrasters = {}\nfor label, root in SOURCES.items():\n ts_dir = root / \"dolphin\" / \"timeseries\"\n longest_path, d1, d2 = longest_pair(ts_dir)\n r = load_raster(longest_path)\n r.update(pair_name=longest_path.name, date_start=d1, date_end=d2, baseline_days=(d2 - d1).days)\n rasters[label] = r\n print(\n f\"{label:20s} {longest_path.name} unit={r['unit']} baseline={r['baseline_days']}d\"\n )"
35+
"source": [
36+
"import re\n",
37+
"from datetime import datetime\n",
38+
"from pathlib import Path\n",
39+
"\n",
40+
"import matplotlib.pyplot as plt\n",
41+
"import numpy as np\n",
42+
"import rasterio\n",
43+
"\n",
44+
"# Each per-source example notebook writes its outputs to ./example_<source>\n",
45+
"# relative to whatever directory it was launched from. Run this notebook from\n",
46+
"# the same CWD as those three notebooks.\n",
47+
"RUNS_DIR = Path(\".\")\n",
48+
"SOURCES = {\n",
49+
" \"Sentinel-1 bursts\": RUNS_DIR / \"example_s1_burst\",\n",
50+
" \"OPERA CSLC\": RUNS_DIR / \"example_opera_cslc\",\n",
51+
" \"NISAR GSLC\": RUNS_DIR / \"example_nisar\",\n",
52+
"}\n",
53+
"\n",
54+
"PAIR_RE = re.compile(r\"^(\\d{8})_(\\d{8})\\.tif$\")\n",
55+
"\n",
56+
"def longest_pair(ts_dir: Path) -> tuple[Path, datetime, datetime]:\n",
57+
" \"\"\"Return the `YYYYMMDD_YYYYMMDD.tif` with the widest baseline.\"\"\"\n",
58+
" best = None\n",
59+
" best_span = -1\n",
60+
" for p in ts_dir.glob(\"*.tif\"):\n",
61+
" m = PAIR_RE.match(p.name)\n",
62+
" if not m:\n",
63+
" continue\n",
64+
" d1 = datetime.strptime(m.group(1), \"%Y%m%d\")\n",
65+
" d2 = datetime.strptime(m.group(2), \"%Y%m%d\")\n",
66+
" span = (d2 - d1).days\n",
67+
" if span > best_span:\n",
68+
" best_span = span\n",
69+
" best = (p, d1, d2)\n",
70+
" assert best is not None, f\"no pair-baseline TIFFs in {ts_dir}\"\n",
71+
" return best\n",
72+
"\n",
73+
"def load_raster(path: Path):\n",
74+
" with rasterio.open(path) as src:\n",
75+
" return {\n",
76+
" \"data\": src.read(1, masked=True),\n",
77+
" \"bounds\": src.bounds,\n",
78+
" \"unit\": (src.units[0] if src.units else \"?\") or \"?\",\n",
79+
" }\n",
80+
"\n",
81+
"rasters = {}\n",
82+
"for label, root in SOURCES.items():\n",
83+
" ts_dir = root / \"dolphin\" / \"timeseries\"\n",
84+
" longest_path, d1, d2 = longest_pair(ts_dir)\n",
85+
" r = load_raster(longest_path)\n",
86+
" r.update(pair_name=longest_path.name, date_start=d1, date_end=d2, baseline_days=(d2 - d1).days)\n",
87+
" rasters[label] = r\n",
88+
" print(\n",
89+
" f\"{label:20s} {longest_path.name} unit={r['unit']} baseline={r['baseline_days']}d\"\n",
90+
" )"
91+
]
3692
},
3793
{
3894
"cell_type": "markdown",
39-
"metadata": {},
4095
"id": "00c745c0-7ffc-4d93-ab40-dd5dd39d3153",
96+
"metadata": {},
4197
"source": [
4298
"## Cumulative displacement (meters)\n",
4399
"\n",
@@ -46,9 +102,9 @@
46102
},
47103
{
48104
"cell_type": "code",
49-
"metadata": {},
50-
"id": "96cbde80-7969-4ce8-89e2-5cdbf1eb1fa4",
51105
"execution_count": null,
106+
"id": "96cbde80-7969-4ce8-89e2-5cdbf1eb1fa4",
107+
"metadata": {},
52108
"outputs": [],
53109
"source": [
54110
"def finite_values(a):\n",
@@ -93,8 +149,8 @@
93149
},
94150
{
95151
"cell_type": "markdown",
96-
"metadata": {},
97152
"id": "c9f60e41-bfc0-43f8-9c86-09e33e07440c",
153+
"metadata": {},
98154
"source": [
99155
"## Effective velocity (meters / year)\n",
100156
"\n",
@@ -103,9 +159,9 @@
103159
},
104160
{
105161
"cell_type": "code",
106-
"metadata": {},
107-
"id": "80a7e0ee-35c5-486a-a580-c121c6871fe3",
108162
"execution_count": null,
163+
"id": "80a7e0ee-35c5-486a-a580-c121c6871fe3",
164+
"metadata": {},
109165
"outputs": [],
110166
"source": [
111167
"rasters_eff = {}\n",
@@ -143,17 +199,17 @@
143199
},
144200
{
145201
"cell_type": "markdown",
146-
"metadata": {},
147202
"id": "eef5bc98-b369-4230-b9d8-295889f11efc",
203+
"metadata": {},
148204
"source": [
149205
"## Quick stats"
150206
]
151207
},
152208
{
153209
"cell_type": "code",
154-
"metadata": {},
155-
"id": "4245053f-b51a-4d04-b741-745fb2bb04af",
156210
"execution_count": null,
211+
"id": "4245053f-b51a-4d04-b741-745fb2bb04af",
212+
"metadata": {},
157213
"outputs": [],
158214
"source": [
159215
"print(f\"{'source':20s} {'baseline':>10s} {'n_valid':>10s} {'disp_std':>12s} {'eff_v_std':>12s}\")\n",
@@ -178,9 +234,9 @@
178234
"name": "python3"
179235
},
180236
"language_info": {
181-
"name": "python",
237+
"file_extension": ".py",
182238
"mimetype": "text/x-python",
183-
"file_extension": ".py"
239+
"name": "python"
184240
}
185241
},
186242
"nbformat": 4,

docs/example_nisar.ipynb

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
"cells": [
33
{
44
"cell_type": "markdown",
5-
"metadata": {},
65
"id": "85564ba9-fef1-4e33-824c-69a58469aff1",
6+
"metadata": {},
77
"source": [
8-
"# sweets example 3 \u2014 NISAR GSLC workflow\n",
8+
"# sweets example 3 NISAR GSLC workflow\n",
99
"\n",
1010
"End-to-end InSAR run over the same LA AOI, this time using pre-geocoded [NISAR L2 GSLC HDF5s](https://nisar.jpl.nasa.gov/) pulled from CMR. NISAR ships UTM-projected complex imagery at L-band, so the pipeline is the simplest of the three sources: no COMPASS, no burst database, no orbit files, no geometry stitching. sweets writes a ~1 KB VRT wrapper per polarization that injects the correct geotransform on top of the HDF5 subdataset (GDAL's HDF5 driver can't parse NISAR's separate xCoordinates/yCoordinates arrays), and hands the VRTs directly to dolphin.\n",
1111
"\n",
12-
"**Why use it?** NISAR is L-band (~24 cm wavelength) \u2014 cuts through vegetation and decorrelates much more slowly than Sentinel-1 C-band, so coherent stacks can span months instead of weeks.\n",
12+
"**Why use it?** NISAR is L-band (~24 cm wavelength) cuts through vegetation and decorrelates much more slowly than Sentinel-1 C-band, so coherent stacks can span months instead of weeks.\n",
1313
"\n",
1414
"**AOI / time**: same LA polygon + 2025-12-01 -> 2025-12-31 window as the Sentinel-1 and OPERA example notebooks. NISAR track 34 frame 18 (the ASF Vertex fields `Track` and `Frame`) covers LA on an ascending pass during this period.\n",
1515
"\n",
@@ -18,8 +18,8 @@
1818
},
1919
{
2020
"cell_type": "markdown",
21-
"metadata": {},
2221
"id": "cbc5042b-98dd-455a-9523-f3d24cad15a1",
22+
"metadata": {},
2323
"source": [
2424
"## Earthdata credentials\n",
2525
"\n",
@@ -36,8 +36,8 @@
3636
},
3737
{
3838
"cell_type": "markdown",
39-
"metadata": {},
4039
"id": "e016fbe7-e804-443f-8df8-f83795b0c6de",
40+
"metadata": {},
4141
"source": [
4242
"## Configure the run\n",
4343
"\n",
@@ -48,9 +48,9 @@
4848
},
4949
{
5050
"cell_type": "code",
51-
"metadata": {},
52-
"id": "f9c82ae0-5561-4cad-8aab-6cc1467f9d50",
5351
"execution_count": null,
52+
"id": "f9c82ae0-5561-4cad-8aab-6cc1467f9d50",
53+
"metadata": {},
5454
"outputs": [],
5555
"source": [
5656
"from pathlib import Path\n",
@@ -63,9 +63,9 @@
6363
},
6464
{
6565
"cell_type": "code",
66-
"metadata": {},
67-
"id": "cf3b86c9-46ae-4c55-a591-7da19cceffc9",
6866
"execution_count": null,
67+
"id": "cf3b86c9-46ae-4c55-a591-7da19cceffc9",
68+
"metadata": {},
6969
"outputs": [],
7070
"source": [
7171
"!sweets config \\\n",
@@ -82,72 +82,72 @@
8282
},
8383
{
8484
"cell_type": "code",
85-
"metadata": {},
86-
"id": "0e227531-9e75-42c5-a6cc-d088b7eea084",
8785
"execution_count": null,
86+
"id": "0e227531-9e75-42c5-a6cc-d088b7eea084",
87+
"metadata": {},
8888
"outputs": [],
8989
"source": [
9090
"!head -60 {CONFIG_YAML}"
9191
]
9292
},
9393
{
9494
"cell_type": "markdown",
95-
"metadata": {},
9695
"id": "7be19beb-58bc-4170-a1fa-b468f38f2e40",
96+
"metadata": {},
9797
"source": [
9898
"## Run the workflow\n",
9999
"\n",
100100
"Under the hood:\n",
101101
"\n",
102102
"1. sweets queries CMR for NISAR GSLC products on track 34 frame 18 that intersect the AOI, ranks the returned granules by (stack size, polarization match, frequency match), and picks the largest coherent signature group.\n",
103-
"2. For each product in that group, sweets asks opera-utils for a bbox-subset of the HDF5 and verifies it actually has data in the AOI (NISAR PR products can advertise a frequency whose actual grid extent is narrower than the bounding polygon \u2014 those get skipped automatically and sweets falls through to the next signature).\n",
103+
"2. For each product in that group, sweets asks opera-utils for a bbox-subset of the HDF5 and verifies it actually has data in the AOI (NISAR PR products can advertise a frequency whose actual grid extent is narrower than the bounding polygon those get skipped automatically and sweets falls through to the next signature).\n",
104104
"3. Each successful subset gets a per-polarization VRT wrapper. dolphin opens those as normal rasters.\n",
105105
"4. dolphin runs phase linking, interferogram network selection, SNAPHU unwrapping, timeseries inversion and velocity estimation as usual. The NISAR L-band wavelength is auto-detected from the HDF5 MODE code so outputs land in meters, not radians."
106106
]
107107
},
108108
{
109109
"cell_type": "code",
110-
"metadata": {},
111-
"id": "abfab05e-87c0-4913-be26-1e05d352b15c",
112110
"execution_count": null,
111+
"id": "abfab05e-87c0-4913-be26-1e05d352b15c",
112+
"metadata": {},
113113
"outputs": [],
114114
"source": [
115115
"!sweets run {CONFIG_YAML}"
116116
]
117117
},
118118
{
119119
"cell_type": "markdown",
120-
"metadata": {},
121120
"id": "1cd06413-3951-4aed-b459-e8c0596cfde6",
121+
"metadata": {},
122122
"source": [
123123
"## Inspect the outputs"
124124
]
125125
},
126126
{
127127
"cell_type": "code",
128-
"metadata": {},
129-
"id": "563d3f40-a8a4-40d7-9774-907ed64b8c10",
130128
"execution_count": null,
129+
"id": "563d3f40-a8a4-40d7-9774-907ed64b8c10",
130+
"metadata": {},
131131
"outputs": [],
132132
"source": [
133133
"!ls {WORK_DIR}/dolphin/"
134134
]
135135
},
136136
{
137137
"cell_type": "code",
138-
"metadata": {},
139-
"id": "b7ab7fc7-05c3-4fac-9c44-255be3656208",
140138
"execution_count": null,
139+
"id": "b7ab7fc7-05c3-4fac-9c44-255be3656208",
140+
"metadata": {},
141141
"outputs": [],
142142
"source": [
143143
"!ls {WORK_DIR}/dolphin/timeseries/"
144144
]
145145
},
146146
{
147147
"cell_type": "code",
148-
"metadata": {},
149-
"id": "83818ad7-4e59-49c8-ad23-7b52bb5e2e6a",
150148
"execution_count": null,
149+
"id": "83818ad7-4e59-49c8-ad23-7b52bb5e2e6a",
150+
"metadata": {},
151151
"outputs": [],
152152
"source": [
153153
"# Velocity raster: NISAR L-band, ~24 cm wavelength.\n",
@@ -156,8 +156,8 @@
156156
},
157157
{
158158
"cell_type": "markdown",
159-
"metadata": {},
160159
"id": "909650a7-39eb-4c3b-bd20-821fb362eada",
160+
"metadata": {},
161161
"source": [
162162
"## Under-the-hood: the VRT wrappers\n",
163163
"\n",
@@ -166,24 +166,25 @@
166166
},
167167
{
168168
"cell_type": "code",
169-
"metadata": {},
170-
"id": "d43e982f-8c61-4d89-b2c5-421c9d977605",
171169
"execution_count": null,
170+
"id": "d43e982f-8c61-4d89-b2c5-421c9d977605",
171+
"metadata": {},
172172
"outputs": [],
173173
"source": [
174174
"!ls -lh {WORK_DIR}/data/ 2>&1 | head -10"
175175
]
176176
},
177177
{
178178
"cell_type": "code",
179-
"metadata": {},
180-
"id": "5c15a4d7-440b-4711-92a8-b5ee636f88e8",
181179
"execution_count": null,
180+
"id": "5c15a4d7-440b-4711-92a8-b5ee636f88e8",
181+
"metadata": {},
182182
"outputs": [],
183183
"source": [
184184
"# Peek at one VRT to see how sweets injects the geotransform over\n",
185185
"# the HDF5 subdataset.\n",
186186
"import glob\n",
187+
"\n",
187188
"vrts = sorted(glob.glob(f\"{WORK_DIR}/data/*.vrt\"))\n",
188189
"if vrts:\n",
189190
" print(open(vrts[0]).read())"
@@ -197,9 +198,9 @@
197198
"name": "python3"
198199
},
199200
"language_info": {
200-
"name": "python",
201+
"file_extension": ".py",
201202
"mimetype": "text/x-python",
202-
"file_extension": ".py"
203+
"name": "python"
203204
}
204205
},
205206
"nbformat": 4,

scripts/ui.ipynb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,10 @@
2626
"import sys\n",
2727
"from datetime import datetime\n",
2828
"\n",
29-
"\n",
3029
"import ipywidgets as widgets\n",
3130
"import pydantic\n",
32-
"from rich import print\n",
3331
"from ipywidgets import GridBox, Layout\n",
32+
"from rich import print\n",
3433
"\n",
3534
"from sweets.core import Workflow\n",
3635
"\n",

src/sweets/_burst_db.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
import zipfile
22
from pathlib import Path
3-
from typing import Optional
43

54
import requests
65
from loguru import logger
76

87
from ._types import Filename
98
from .utils import get_cache_dir
109

11-
BURST_DB_URL = "https://github.com/scottstanie/burst_db/raw/frames-with-data/data/s1-frames-9frames-5min-10max-bbox-only.gpkg.zip" # noqa: E501
10+
BURST_DB_URL = "https://github.com/scottstanie/burst_db/raw/frames-with-data/data/s1-frames-9frames-5min-10max-bbox-only.gpkg.zip"
1211

1312

14-
def get_burst_db(url: str = BURST_DB_URL, out_file: Optional[Filename] = None) -> Path:
13+
def get_burst_db(url: str = BURST_DB_URL, out_file: Filename | None = None) -> Path:
1514
"""Read or download the burst-db file.
1615
1716
TODO: getting a URL to a release version of the sqlite database.

0 commit comments

Comments
 (0)