Automatically Selecting Reference Points#

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from tqdm import tqdm
import xarray as xr
from pathlib import Path
import rasterio
from scipy.stats import linregress

from faninsar import NSBAS, query, datasets, samplers
out_dir = Path("/Volumes/Data/GeoData/YNG/Sentinel1/Hyp3/descending_roi/result/ARPs/aux_data")

tmp_file = Path("/Volumes/Data/GeoData/YNG/Temperature/ERA5/result/calibrated/spatialT_cal.nc")
cum_file = Path("/Volumes/Data/GeoData/YNG/Sentinel1/Hyp3/descending_roi/result/cumulative_deformation.tif")
pf_pt = query.Points([98.84111965, 38.79726347],crs=4326)
pf_pt.to_shapefile(out_dir / "pf_pt.geojson")
## load cum deformation time series 
with rasterio.open(cum_file) as ds:
    dates = pd.to_datetime(ds.descriptions)

ds_cum = datasets.RasterDataset(paths=[cum_file], verbose=False)
sample_pt = ds_cum[pf_pt]
pf_dfm = sample_pt.points.data[0]

## load temperature time series
ds_tmp = xr.open_dataset(tmp_file)
df_tmp = ds_tmp.sel(lat=pf_pt.y[0], lon=pf_pt.y[0], method="nearest")["surface air temperature"].to_series()
ds_tmp.close()
ftc = NSBAS.FreezeThawCycle(df_tmp.index, df_tmp.values, ER=1.4, day_duration=20)
model_ftc = NSBAS.FreezeThawCycleModelWithVelocity(ftc, dates)
model_ftc.G_br.shape, pf_dfm.shape
((183, 4), (183,))
params = np.linalg.lstsq(model_ftc.G_br, pf_dfm, rcond=None)[0]

## remove the long term trend
# params[2:] = 0
cum_ftc = model_ftc.G_br @ params
plt.figure(figsize=(15, 5))
plt.plot(dates, cum_ftc, marker='o', label="Modelled")
plt.plot(dates, pf_dfm, marker='o', label="Observed")
plt.legend()
plt.title("Cumulative deformation modelled by stefan-equation-based model")
Text(0.5, 1.0, 'Cumulative deformation modelled by stefan-equation-based model')
../../_images/92df62ee498fe4de94285e869478308cd0ea5dd990ce08e096b2baddf7020aa4.png

calculate the slope of function#

slope_file = out_dir / "slope.tif"
r_file = out_dir / "r.tif"
p_file = out_dir / "p.tif"
se_file = out_dir / "se.tif"
def get_linear_slope(cum_dfm, cum_ftc, dates, slope_only=False):
    """Compute the slope of linear function between cum_dfm and cum_ftc
    
    Parameters
    ----------
    cum_dfm : np.ndarray (n_date, n_pixel)
        The cumulative deformation time series
    cum_ftc : np.ndarray (n_date, )
        The modelled freeze-thaw cycle time series of selected pixel
    dates : pd.DatetimeIndex (n_date, )
        The dates of the time series
    slope_only : bool (default: False)
        return the [slope, intercept, r, p, se] if False, otherwise return the 
        slope only. It will be faster if slope_only is True as parallel computing
        is possible.
    """
    model_ls = NSBAS.LinearModel(dates)
    
    # detrnd the data
    ## avoid computing the slope for the masked data
    m = ~np.all(cum_dfm.mask, axis=0)
    params = np.linalg.lstsq(model_ls.G_br, cum_dfm[:,m].data, rcond=None)[0]
    cum_data_detrend = np.full_like(cum_dfm, np.nan)
    cum_data_detrend[:,m] = cum_dfm[:,m] - np.dot(model_ls.G_br, params)
    
    # compute the slope of linear function
    x = cum_ftc
    y = cum_data_detrend
    X = np.array([x, np.ones_like(cum_ftc)]).T
    
    if slope_only:
        params = np.linalg.lstsq(X, y, rcond=None)[0]
        slope = params[0]
        return slope
    
    results = []
    for i in tqdm(range(y.shape[1]), desc="Computing linear slope", unit="pixel"):
        y_i = y[:, i]
        if np.all(np.isnan(y_i)):
            result = np.full((5), np.nan)
        else:
            slope, intercept, r, p, se = linregress(x, y_i)
            result = [slope, intercept, r, p, se]
        results.append(result)
    return np.array(results)

   
sampler = samplers.RowSampler(ds_cum, row_num=20)

for bbox in tqdm(sampler, desc="Processing Patches", unit="patch"):
    with rasterio.open(cum_file) as ds:
        win = ds.window(*bbox)
        cum_data = ds.read(window=win, masked=True)

    n_date, n_row, n_col = cum_data.shape
    cum_data = cum_data.reshape(n_date, n_row*n_col)
    
    # slope = get_linear_slope(cum_data, cum_ftc, dates, slope_only=True).reshape(n_row, n_col)
    
    ### derive slope, r, p, se
    slope_result = get_linear_slope(cum_data, cum_ftc, dates)
    slope = slope_result[:, 0].reshape(n_row, n_col)
    r = slope_result[:, 2].reshape(n_row, n_col)
    p = slope_result[:, 3].reshape(n_row, n_col)
    se = slope_result[:, 4].reshape(n_row, n_col)

    # save the results
    ds_cum.array2tiff(slope, slope_file, bbox=bbox)
    ds_cum.array2tiff(r, r_file, bbox=bbox)
    ds_cum.array2tiff(p, p_file, bbox=bbox)
    ds_cum.array2tiff(se, se_file, bbox=bbox)
Computing linear slope: 100%|██████████| 206035/206035 [00:09<00:00, 21730.62pixel/s]
Computing linear slope: 100%|██████████| 206035/206035 [00:09<00:00, 21575.20pixel/s]
Computing linear slope: 100%|██████████| 206035/206035 [00:09<00:00, 21356.18pixel/s]
Computing linear slope: 100%|██████████| 206035/206035 [00:09<00:00, 21299.25pixel/s]
Computing linear slope: 100%|██████████| 206035/206035 [00:09<00:00, 21721.89pixel/s]
Computing linear slope: 100%|██████████| 206035/206035 [00:10<00:00, 20358.73pixel/s]
Computing linear slope: 100%|██████████| 206035/206035 [00:09<00:00, 20828.65pixel/s]
Computing linear slope: 100%|██████████| 206035/206035 [00:10<00:00, 20214.99pixel/s]
Computing linear slope: 100%|██████████| 206035/206035 [00:10<00:00, 20221.59pixel/s]
Computing linear slope: 100%|██████████| 206035/206035 [00:09<00:00, 20938.57pixel/s]
Computing linear slope: 100%|██████████| 206035/206035 [00:09<00:00, 21085.58pixel/s]
Computing linear slope: 100%|██████████| 206035/206035 [00:09<00:00, 20932.77pixel/s]
Computing linear slope: 100%|██████████| 206035/206035 [00:09<00:00, 21080.15pixel/s]
Computing linear slope: 100%|██████████| 206035/206035 [00:09<00:00, 21230.73pixel/s]
Computing linear slope: 100%|██████████| 206035/206035 [00:09<00:00, 20897.95pixel/s]
Computing linear slope: 100%|██████████| 206035/206035 [00:09<00:00, 20854.55pixel/s]
Computing linear slope: 100%|██████████| 206035/206035 [00:09<00:00, 20976.59pixel/s]
Computing linear slope: 100%|██████████| 206035/206035 [00:09<00:00, 20862.05pixel/s]
Computing linear slope: 100%|██████████| 206035/206035 [00:09<00:00, 20987.10pixel/s]
Computing linear slope: 100%|██████████| 215295/215295 [00:10<00:00, 20934.75pixel/s]
Processing Patches: 100%|██████████| 20/20 [04:08<00:00, 12.44s/patch]

calculate the TPI of DEM#

dem = Path("/Volumes/Data/GeoData/YNG/Sentinel1/Hyp3/descending_roi/input_dem.tif")
ds_dem = datasets.RasterDataset(paths=[dem], verbose=False, roi=ds_cum.roi, fill_nodata=True)
dem_val= ds_dem[ds_dem.roi].boxes.data.squeeze()
profile = ds_cum.get_profile()
lat, lon = profile.to_latlon()
conda install -c conda-forge xarray-spatial
from xrspatial import convolution
from xrspatial import focal

da_dem = xr.DataArray(
    dem_val,  
    dims=['lat', 'lon'],
    coords={'lat': lat, 'lon': lon}
)
 
# calculate TPI_sd from dem
kernel = convolution.annulus_kernel(1, 1, 25, 1)
TPI_sd = ((da_dem - focal.apply(da_dem, kernel=kernel))
            / focal.apply(da_dem, kernel=kernel, func=focal._calc_std)).values

ds_cum.array2tiff(TPI_sd, out_dir / "TPI_sd.tif")

parse mean_coherence#

home_dir = Path("/Volumes/Data/GeoData/YNG/Sentinel1/Hyp3/descending_roi")

ds_hyp3 = datasets.HyP3S1(home_dir)
pairs_all = ds_hyp3.pairs

con_nearest_winter = (
    pairs_all.where(pairs_all["2017-01":"2023-04"], return_type="mask")
    & (pairs_all.days > 180)
    & (pairs_all.days < 360 + 180)
    & (pairs_all.primary.month.map(lambda x: x in [1, 2, 3]))
    # & (pairs_all.secondary.month.map(lambda x: x in [1, 2, 3]))
)

# loops with 5 acquisitions
pairs_idx = (pairs_all.days < 12 * 3 + 1) & (
    pairs_all.where(pairs_all["2017-01":"2023-04"], return_type="mask")
) | con_nearest_winter

pairs = pairs_all[pairs_idx]

Calculate the mean coherence#

coh_mean = ds_hyp3.coh_dataset.get_mean_coh(pairs=pairs)
ds_hyp3.array2tiff(coh_mean, out_dir / "mean_coh.tif")
100%|██████████| 786/786 [01:15<00:00, 10.40it/s]
plt.imshow(coh_mean)
plt.colorbar()
<matplotlib.colorbar.Colorbar at 0x32e876030>
../../_images/8e4ae23ef88851683ab24b2ccc8ce1be887011603ad8e2cc518ccd1ce0dd64ed.png
ds_hyp3.array2tiff(coh_mean, out_dir / "mean_coh.tif")

parse nan count#

nan_count = ds_hyp3.get_nan_count(pairs)
nan_file = out_dir / "nan_count.tif"
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[5], line 1
----> 1 nan_count = ds_hyp3.get_nan_count(pairs)
      2 nan_file = out_dir / "nan_count.tif"

File /Volumes/Data/Github/FanInSAR/faninsar/datasets/ifg.py:532, in InterferogramDataset.get_nan_count(self, pairs, roi)
    529 files = [self._load_warp_file(f) for f in self.files.paths[m]]
    531 # calculate the number of nan values
--> 532 nan_count = (self._file_query_bbox(roi, files[0]).squeeze(0).mask).astype(int)
    533 for f in tqdm(files[1:]):
    534     nan_count += (self._file_query_bbox(roi, f).squeeze(0).mask).astype(int)

File /opt/miniconda3/envs/geo/lib/python3.12/site-packages/numpy/ma/core.py:2572, in _arraymethod.<locals>.wrapped_method(self, *args, **params)
   2571 def wrapped_method(self, *args, **params):
-> 2572     result = getattr(self._data, funcname)(*args, **params)
   2573     result = result.view(type(self))
   2574     result._update_from(self)

ValueError: cannot select an axis to squeeze out which has size not equal to one
ds_hyp3._file_query_bbox(roi, files[0]).squeeze(0).mask).astype(int)
HyP3S1 Dataset
    bbox: BoundingBox(left=443501.82025355106, bottom=4263758.21737383, right=536101.820253551, top=4335118.21737383, crs=EPSG:32647)
    file count: 2750
ds_hyp3.array2tiff(nan_count.astype(float), nan_file)
with rasterio.open(nan_file) as ds:
    arr = ds.read(1,masked=True)
    plt.imshow(arr, cmap="viridis")
../../_images/d8956a9a608ed2b7c41e25e78c20d565f8cee67f80a7db546802aeaad82ae271.png