In my previous article, I introduced how to download and visualize SAR polarization data using Google Earth Engine.
https://dev.to/takeofuture/visualizing-surface-changes-after-the-2026-kumamoto-earthquake-with-sentinel-1-sar-and-google-earth-2ob3
In this article, I will take a different approach.
Instead of relying on Google Earth Engine, we will use NASA's Alaska Satellite Facility (ASF) to search for and download Sentinel-1 data directly.
Then we will generate InSAR (Interferometric Synthetic Aperture Radar) products using ASF HyP3, including:
- Wrapped Phase
- Unwrapped Phase
- Coherence
- LOS Displacement
As a practical example, I will use Sentinel-1 observations around the 2026 Kumamoto earthquake in Japan.
The goal of this article is not yet to analyze the earthquake itself.
Instead, the goal is to build the dataset required for later analysis.
A Quick Review: What Are VV and VH in SAR?
Before moving on to InSAR, let's quickly review the VV and VH polarization channels introduced in the previous article.
What Is SAR Polarization?
SAR stands for Synthetic Aperture Radar.
A SAR satellite sends microwave signals toward the Earth's surface and measures the signals that are reflected or scattered back toward the satellite.
One important property of these signals is polarization.A Quick Review: What Are VV and VH in SAR?
Before moving on to InSAR, let's quickly review the VV and VH polarization channels introduced in the previous article.What Is SAR Polarization?
SAR stands for Synthetic Aperture Radar.
A SAR satellite sends microwave signals toward the Earth's surface and measures the signals that are reflected or scattered back toward the satellite.
One important property of these signals is polarization.

Polarization describes the orientation in which the electric field of the electromagnetic wave oscillates.
SAR commonly uses two polarization directions:
H: Horizontal polarization
V: Vertical polarization
SAR products describe the transmitted and received polarization using two letters.
For example:
VV: transmitted vertically and received vertically
VH: transmitted vertically and received horizontally
HH: transmitted horizontally and received horizontally
HV: transmitted horizontally and received vertically
VV and HH are called co-polarized channels.
VH and HV are called cross-polarized channels.

Why Do VV and VH Look Different?
Microwaves interact differently with soil, vegetation, buildings, and other surface structures.
Because of that, VV and VH can provide different information even when observing exactly the same location.
In general:
VV often responds strongly to direct surface and structural reflections.
VH is often more sensitive to vegetation and complex multiple scattering.
In the previous article, I used SAR backscatter intensity and visualized how VV changed before and after the earthquake.
I also experimented with treating VV and VH as two input channels for image models.
For example, a conventional RGB image contains three channels:
R,G,B
A SAR-based model might instead use:
VV,VH
as two channels.
However, VV and VH backscatter do not respond only to earthquakes.
They are also affected by factors such as:
vegetation
soil moisture
rainfall
surface roughness
building orientation
satellite viewing geometry
For this reason, I decided to move from simple backscatter comparison to InSAR, which uses phase information to estimate changes in the distance between the satellite and the ground.
What Is InSAR?
InSAR stands for Interferometric Synthetic Aperture Radar.
VV and VH mainly describe the strength of the reflected radar signal.
InSAR goes one step further and also uses the phase of the microwave signal.
A simplified way to think about the difference is:
VV / VH: How did the radar signal reflect from the surface?
InSAR: How much did the radar phase change between two observations?
SAR satellites repeatedly observe the same area.
For example:
Observation 1: before an earthquake
Observation 2: after an earthquake
If the ground moves between the two observations, the distance traveled by the radar wave also changes.
That produces a change in the phase of the returned radar signal.
InSAR analyzes this phase difference to detect very small changes in the Earth's surface.
In other words, instead of simply asking:
"Did the image look different?"
we can ask:
"Did the distance between the satellite and the ground change?"
However, InSAR does not directly measure pure vertical displacement.
The measurement is projected along the satellite's viewing direction, called the Line of Sight (LOS).
A simple interpretation is:
LOS displacement = how much the ground moved along the satellite's viewing direction
Three InSAR Products Used in This Project
For this project, I mainly focus on three InSAR products:
- Wrapped Phase
- Coherence
- LOS Displacement
Wrapped Phase
Radar signals are waves, so they have a phase.
When two SAR observations are compared, changes in the satellite-to-ground path length produce a phase difference.
The phase is typically represented within the range:
-π to +π
This is called the Wrapped Phase.
Because phase is periodic, values repeat after one full cycle.
When visualized, this can produce repeating colored bands known as interferometric fringes.

These fringes can indicate spatial changes in radar phase.
However, because the values are wrapped repeatedly between -π and +π, we cannot directly read a displacement such as "10 cm" from the Wrapped Phase alone.Coherence
Another important InSAR product is Coherence.
Coherence describes how consistently the radar signals from two observations correspond to each other.
Its value is generally between:
0 and 1
A rough interpretation is:
close to 1: the two SAR observations correspond well, and the phase is relatively reliable
close to 0: the correspondence is weak, possibly because of noise or significant surface changes
Coherence can decrease because of:vegetation changes
water surfaces
rainfall
soil moisture changes
major surface changes
differences in observation conditions
Coherence is therefore useful when deciding which LOS displacement values are reliable enough to analyze.
For example, in a later analysis I use only pixels satisfying:
Coherence >= 0.5LOS Displacement
Wrapped Phase contains periodic phase values.
To estimate physical displacement, the phase wrapping first needs to be resolved using phase unwrapping.
The result is an Unwrapped Phase, where phase changes are represented continuously.
The radar wavelength can then be used to convert the phase difference into a distance change.
This produces LOS Displacement.


The processing concept is:
Wrapped Phase
↓
Phase Unwrapping
↓
Unwrapped Phase
↓
Convert phase using radar wavelength
↓
LOS Displacement
One important point is that Unwrapped Phase is still phase, not yet distance.
LOS Displacement is the product expressed as physical distance.
For the HyP3 LOS products used here, the sign convention is generally interpreted as:
- positive: displacement toward the satellite
- negative: displacement away from the satellite A LOS displacement of 10 cm does not necessarily mean that the ground moved vertically upward by 10 cm.
It means that the ground displacement projected onto the satellite's viewing direction was 10 cm.
- VV/VH vs. InSAR
We can summarize the difference as follows:
In very simplified terms:
VV/VH asks whether the way the surface reflects radar has changed.
InSAR asks whether the satellite-to-ground distance has changed.
Now let's obtain the actual data.
Create a NASA Earthdata Login
To download Sentinel-1 data from ASF, first create a NASA Earthdata Login account.
Registration instructions:
https://urs.earthdata.nasa.gov/documentation/for_users/how_to_register
Because we will use Python tools such as asf_search and hyp3_sdk, you may also need to pre-authorize the appropriate ASF/HyP3 applications for your Earthdata account.
Instructions:
https://urs.earthdata.nasa.gov/documentation/for_users/how_to_preauth_app
In this workflow, we will:
Search Sentinel-1
↓
Download SLC data
↓
Create observation pairs
↓
Submit InSAR jobs to HyP3
↓
Download the processed InSAR products
- Install the Python Libraries The main libraries used in this article are: asf_search: search and download Sentinel-1 data from ASF hyp3_sdk: submit and retrieve HyP3 processing jobs shapely: work with geographical areas and geometries Create a requirements.txt file:
asf_search==12.3.1
hyp3_sdk==7.7.7
shapely==2.1.2
Then install them in your Python virtual environment:
pip install -r requirements.txt
- Search for Sentinel-1 SLC Data The earthquake used in this example occurred in Kumamoto, Japan, on July 28, 2026. I searched for Sentinel-1 acquisitions from June through August 16, 2026. Rather than searching only at a single epicenter coordinate, I define a rectangular Area of Interest (AOI) around the epicenter. The bounding box is given as: west south east north I also restrict the search to Relative Orbit 156. This matters because InSAR compares phase between acquisitions, so observation geometry should be as consistent as possible. Create: 0.list_up.py
import asf_search as asf
# Kumamoto earthquake epicenter
EPICENTER = (130.6783333333, 32.625)
# Area of Interest
AOI_BBOX = (
130.515, # west
32.480, # south
130.845, # east
32.765, # north
)
RELATIVE_ORBIT = 156
def bbox_wkt(bbox):
west, south, east, north = bbox
return (
f"POLYGON(("
f"{west} {south},"
f"{east} {south},"
f"{east} {north},"
f"{west} {north},"
f"{west} {south}"
f"))"
)
results = asf.geo_search(
dataset=asf.DATASET.SENTINEL1,
processingLevel="SLC",
beamMode="IW",
relativeOrbit=RELATIVE_ORBIT,
intersectsWith=bbox_wkt(AOI_BBOX),
start="2026-06-01T00:00:00Z",
end="2026-08-16T23:59:59Z",
maxResults=100,
)
print("Search results:", len(results))
for product in results:
p = product.properties
print(
p.get("sceneName"),
p.get("startTime"),
p.get("flightDirection"),
p.get("polarization"),
"frame =", p.get("frameNumber"),
)
Run:
python 0.list_up.py
The result in this case was:
Search results: 7
S1C_IW_SLC__1SDV_20260815T091320_20260815T091347_009004_011DF6_CC4C 2026-08-15T09:13:20Z ASCENDING VV+VH frame = 102
S1C_IW_SLC__1SDV_20260803T091319_20260803T091346_008829_011833_BE60 2026-08-03T09:13:19Z ASCENDING VV+VH frame = 102
S1C_IW_SLC__1SDV_20260722T091318_20260722T091345_008654_01125E_4884 2026-07-22T09:13:18Z ASCENDING VV+VH frame = 102
S1C_IW_SLC__1SDV_20260710T091318_20260710T091345_008479_010C8A_76D5 2026-07-10T09:13:18Z ASCENDING VV+VH frame = 102
S1C_IW_SLC__1SDV_20260628T091317_20260628T091344_008304_0106DC_85C8 2026-06-28T09:13:17Z ASCENDING VV+VH frame = 102
S1D_IW_SLC__1SDV_20260622T091330_20260622T091357_003347_005E09_DD43 2026-06-22T09:13:30Z ASCENDING VV+VH frame = 102
S1D_IW_SLC__1SDV_20260610T091329_20260610T091356_003172_005852_B0A1 2026-06-10T09:13:29Z ASCENDING VV+VH frame = 102
Seven SLC acquisitions were found.
For example:
S1C_IW_SLC__1SDV_20260722...
contains information such as:
S1C Sentinel-1C
IW Interferometric Wide Swath mode
SLC Single Look Complex
20260722 Acquisition date
The results also show consistent:
ASCENDING
VV+VH
frame = 102
ASCENDING means the satellite was traveling generally from south to north during the acquisition.
VV+VH means both VV and VH polarization channels are included.
Frame 102 is one of the observation-geometry parameters used to keep the acquisitions comparable.
At this stage, we have not generated any InSAR products yet.
We are only checking whether suitable Sentinel-1 SLC acquisitions exist before and after the earthquake.
Download Sentinel-1 SLC Data
ASF metadata search can be performed without authentication, but downloading the actual Sentinel-1 SLC files requires NASA Earthdata authentication.
Instead of writing credentials directly inside the source code, store them as environment variables.
export EARTHDATA_USERNAME="your-earthdata-username"
export EARTHDATA_PASSWORD="your-earthdata-password"
Create:
1.download_s1_slc.py
import os
from pathlib import Path
import asf_search as asf
# --------------------------------------------------
# Search configuration
# --------------------------------------------------
AOI_BBOX = (
130.515, # west
32.480, # south
130.845, # east
32.765, # north
)
RELATIVE_ORBIT = 156
FRAME_NUMBER = 102
FLIGHT_DIRECTION = "ASCENDING"
SEARCH_START = "2026-06-01"
SEARCH_END = "2026-08-16"
DOWNLOAD_DIR = Path("./data/slc")
def bbox_wkt(bbox):
west, south, east, north = bbox
return (
f"POLYGON(("
f"{west} {south},"
f"{east} {south},"
f"{east} {north},"
f"{west} {north},"
f"{west} {south}"
f"))"
)
# --------------------------------------------------
# Earthdata Login
# --------------------------------------------------
username = os.environ.get("EARTHDATA_USERNAME")
password = os.environ.get("EARTHDATA_PASSWORD")
if not username or not password:
raise RuntimeError(
"Please set EARTHDATA_USERNAME and EARTHDATA_PASSWORD."
)
session = asf.ASFSession().auth_with_creds(
username,
password,
)
# --------------------------------------------------
# Search Sentinel-1 SLC
# --------------------------------------------------
results = asf.geo_search(
dataset=asf.DATASET.SENTINEL1,
processingLevel="SLC",
beamMode="IW",
relativeOrbit=RELATIVE_ORBIT,
intersectsWith=bbox_wkt(AOI_BBOX),
start=f"{SEARCH_START}T00:00:00Z",
end=f"{SEARCH_END}T23:59:59Z",
maxResults=100,
)
# --------------------------------------------------
# Filter by observation geometry
# --------------------------------------------------
selected = []
for product in results:
p = product.properties
direction = str(
p.get("flightDirection", "")
).upper()
frame = p.get("frameNumber")
polarization = str(
p.get("polarization", "")
).upper()
if (
direction == FLIGHT_DIRECTION
and frame == FRAME_NUMBER
and "VV" in polarization
):
selected.append(product)
print("Products to download:", len(selected))
# --------------------------------------------------
# Download
# --------------------------------------------------
DOWNLOAD_DIR.mkdir(
parents=True,
exist_ok=True,
)
for product in selected:
p = product.properties
print()
print(
"Downloading:",
p.get("sceneName")
)
product.download(
path=str(DOWNLOAD_DIR),
session=session,
)
print()
print("Download complete.")
print("Output:", DOWNLOAD_DIR.resolve())
Because these files can be large, I ran the script in the background:
nohup python 1.download_s1_slc.py > 1.download_s1_slc.log 2>&1 &
After the download completed:
data/slc/
├── S1C_IW_SLC__1SDV_20260628T091317_20260628T091344_008304_0106DC_85C8.zip
├── S1C_IW_SLC__1SDV_20260710T091318_20260710T091345_008479_010C8A_76D5.zip
├── S1C_IW_SLC__1SDV_20260722T091318_20260722T091345_008654_01125E_4884.zip
├── S1C_IW_SLC__1SDV_20260803T091319_20260803T091346_008829_011833_BE60.zip
├── S1C_IW_SLC__1SDV_20260815T091320_20260815T091347_009004_011DF6_CC4C.zip
├── S1D_IW_SLC__1SDV_20260610T091329_20260610T091356_003172_005852_B0A1.zip
└── S1D_IW_SLC__1SDV_20260622T091330_20260622T091357_003347_005E09_DD43.zip
Look Inside an SLC Product
Let's inspect one downloaded ZIP file.
unzip -l data/slc/S1C_IW_SLC__1SDV_20260722T091318_20260722T091345_008654_01125E_4884.zip \
| grep -i preview
One of the files inside the archive is:
preview/quick-look.png
We can extract only that preview:
mkdir -p preview
unzip -j \
data/slc/S1C_IW_SLC__1SDV_20260722T091318_20260722T091345_008654_011DF6_CC4C.zip \
'*/preview/quick-look.png' \
-d preview
If your filename differs, replace it with the actual downloaded granule name.
Here is an example of the Sentinel-1 SLC quick-look image:

This is not yet an InSAR image.
It is a preview of one SAR acquisition covering a much larger area than the Kumamoto AOI.
Generate InSAR Products with ASF HyP3
Now we have the Sentinel-1 SLC acquisitions.
The next step is to combine observations from two different dates and generate InSAR products.
For this, I use ASF HyP3 InSAR GAMMA.
InSAR requires a pair of compatible SLC acquisitions observing the same area.
In this example, I automatically select acquisitions separated by 12 days.
For example:
2026/07/10 → 2026/07/22 PRE-earthquake
2026/07/22 → 2026/08/03 COSEISMIC
2026/08/03 → 2026/08/15 POST-earthquake
The important point is that the local SLC ZIP files are not uploaded to HyP3.
Instead, the script extracts the Sentinel-1 granule IDs from the filenames and submits those IDs to HyP3.
ASF performs the InSAR processing on its side.
Make sure the Earthdata credentials are still available:
export EARTHDATA_USERNAME="your-earthdata-username"
export EARTHDATA_PASSWORD="your-earthdata-password"
Create:
2.make_insar.py
import os
import re
import zipfile
from datetime import datetime
from pathlib import Path
import hyp3_sdk as sdk
# --------------------------------------------------
# Configuration
# --------------------------------------------------
SLC_DIR = Path("./data/slc")
RESULT_DIR = Path("./data/insar")
EARTHQUAKE_DATE = datetime(2026, 7, 28)
PAIR_DAYS = 12
# --------------------------------------------------
# Earthdata Login
# --------------------------------------------------
username = os.environ.get("EARTHDATA_USERNAME")
password = os.environ.get("EARTHDATA_PASSWORD")
if not username or not password:
raise RuntimeError(
"Please set EARTHDATA_USERNAME and EARTHDATA_PASSWORD."
)
hyp3 = sdk.HyP3(
username=username,
password=password,
)
# --------------------------------------------------
# Read acquisition date and granule ID
# --------------------------------------------------
def get_slc_info(zip_path):
granule_id = zip_path.stem
# Example:
# S1C_IW_SLC__1SDV_20260722T091318_...
match = re.search(
r"_(\d{8})T\d{6}_",
granule_id
)
if not match:
raise RuntimeError(
f"Could not parse acquisition date: {granule_id}"
)
date = datetime.strptime(
match.group(1),
"%Y%m%d"
)
return {
"path": zip_path,
"granule": granule_id,
"date": date,
}
slcs = [
get_slc_info(path)
for path in sorted(SLC_DIR.glob("*.zip"))
]
slcs.sort(
key=lambda x: x["date"]
)
print("SLC acquisitions")
print("-" * 70)
for s in slcs:
print(
s["date"].strftime("%Y-%m-%d"),
s["granule"]
)
# --------------------------------------------------
# Build 12-day InSAR pairs
# --------------------------------------------------
pairs = []
for i in range(len(slcs)):
for j in range(i + 1, len(slcs)):
reference = slcs[i]
secondary = slcs[j]
delta = (
secondary["date"]
- reference["date"]
).days
if delta != PAIR_DAYS:
continue
if secondary["date"] < EARTHQUAKE_DATE:
role = "pre"
elif (
reference["date"]
<= EARTHQUAKE_DATE
<= secondary["date"]
):
role = "coseismic"
else:
role = "post"
pairs.append({
"role": role,
"reference": reference,
"secondary": secondary,
})
print()
print("Selected InSAR pairs")
print("-" * 70)
for pair in pairs:
print(
f"{pair['role']:10s}",
pair["reference"]["date"].strftime("%Y-%m-%d"),
"->",
pair["secondary"]["date"].strftime("%Y-%m-%d"),
)
# --------------------------------------------------
# Submit HyP3 InSAR jobs
# --------------------------------------------------
RESULT_DIR.mkdir(
parents=True,
exist_ok=True,
)
for number, pair in enumerate(
pairs,
start=1
):
reference = pair["reference"]
secondary = pair["secondary"]
role = pair["role"]
ref_date = reference["date"].strftime("%Y%m%d")
sec_date = secondary["date"].strftime("%Y%m%d")
pair_name = (
f"kumamoto_{role}_{ref_date}_{sec_date}"
)
pair_dir = RESULT_DIR / pair_name
download_dir = pair_dir / "download"
product_dir = pair_dir / "product"
download_dir.mkdir(
parents=True,
exist_ok=True,
)
product_dir.mkdir(
parents=True,
exist_ok=True,
)
print()
print("=" * 70)
print(
f"SUBMIT {number}/{len(pairs)}:",
pair_name
)
print(
"Reference:",
reference["granule"]
)
print(
"Secondary:",
secondary["granule"]
)
# ----------------------------------------------
# HyP3 InSAR GAMMA
# ----------------------------------------------
job = hyp3.submit_insar_job(
reference["granule"],
secondary["granule"],
name=pair_name,
looks="10x2",
include_wrapped_phase=True,
include_displacement_maps=True,
include_look_vectors=True,
include_inc_map=True,
include_dem=True,
apply_water_mask=True,
phase_filter_parameter=0.6,
)
# ----------------------------------------------
# Wait for processing
# ----------------------------------------------
print("Waiting for HyP3 processing...")
completed = hyp3.watch(
job,
timeout=12 * 60 * 60,
interval=60,
)
# ----------------------------------------------
# Download completed product
# ----------------------------------------------
print("Downloading InSAR product...")
downloaded = completed.download_files(
location=download_dir
)
# ----------------------------------------------
# Extract ZIP
# ----------------------------------------------
for file_path in downloaded:
file_path = Path(file_path)
if file_path.suffix.lower() != ".zip":
continue
with zipfile.ZipFile(
file_path,
"r"
) as z:
z.extractall(
product_dir
)
print(
"Completed:",
pair_name
)
print()
print("=" * 70)
print("ALL InSAR PROCESSING COMPLETE")
print("Output:", RESULT_DIR.resolve())
HyP3 processing can take some time, so I recommend running it in the background:
nohup python 2.make_insar.py > 2.make_insar.log 2>&1 &
You can monitor progress with:
tail -f 2.make_insar.log
*InSAR Data Is Ready
After HyP3 finishes processing and the product is downloaded, each observation pair contains a set of InSAR products.
For example:
data/
└── insar/
└── kumamoto_pre_20260610_20260622/
├── download/
│ └── ...zip
└── product/
└── S1DD_20260610T091329_20260622T091330_.../
├── *_corr.tif
├── *_wrapped_phase.tif
├── *_unw_phase.tif
├── *_los_disp.tif
├── *_dem.tif
├── *_inc_map.tif
├── *_water_mask.tif
└── ...
The main products used later are:
*_wrapped_phase.tif Wrapped Phase
*_unw_phase.tif Unwrapped Phase
*_corr.tif Coherence
*_los_disp.tif LOS Displacement
At this point, we have completed the process of turning Sentinel-1 SLC acquisitions into a dataset that can be used for quantitative InSAR analysis.
Example: Wrapped Phase
Below is an example of a Wrapped Phase visualization generated by HyP3.

The colors represent wrapped phase values.
The repeating color pattern does not mean that the ground moved by a corresponding physical color-coded distance.
The phase still repeats periodically between approximately -π and +π.
Example: Unwrapped Phase
The following image shows the corresponding Unwrapped Phase.

The important distinction is:
Wrapped Phase
phase angle with wrapping
↓
Unwrapped Phase
continuous phase
↓
LOS Displacement
physical distance
The Unwrapped Phase is not yet the final distance measurement.
It is still phase, but the periodic wrapping has been resolved.
The radar wavelength is then used to convert the phase into physical LOS displacement.
What's Next?
In this article, I focused only on:
Sentinel-1 search
↓
SLC download
↓
Observation pairing
↓
HyP3 InSAR processing
↓
InSAR GeoTIFF products
I have not yet evaluated whether the earthquake produced a large displacement signal.
That will be the next step.
Using the products generated here, we can investigate questions such as:
How much did LOS displacement change before and after the earthquake?
Which pixels should be trusted based on Coherence?
What spatial patterns appear in the Wrapped Phase?
Can machine learning detect the earthquake as an anomaly?
Can self-supervised models such as MAE + Vision Transformer detect abnormal InSAR patterns?
The important point for this article is that the InSAR dataset is now ready.
We can now move from data preparation to actual earthquake deformation analysis.
Data and Processing
Data source:
ESA / Copernicus Sentinel-1 IW SLC
Distributed through:
NASA Alaska Satellite Facility (ASF) DAAC
InSAR processing:
ASF HyP3 InSAR GAMMA
Top comments (0)