基于Pyflwdir实现流域的提取(参照官网例子)

本文参照官网例子实现流域的提取,官方GitHub地址如下pyflwdir

该工具包目前仅支持D8和LDD两种算法,在效率上具有较好的应用性,我用省级的DEM(30米)数据作为测试,输出效率可以满足一般作业需要。

环境environment.yml如下:

python 复制代码
name: pyflwdir

channels:
  - conda-forge

# note that these are the developer dependencies,
# if you only want the user dependencies, see
# install_requires in setup.py
dependencies:
  # required
  - python>=3.9
  - pyflwdir
  # optional for notebooks
  - cartopy>=0.20 
  - descartes
  - geopandas>=0.8
  - jupyter
  - matplotlib
  - rasterio

用到的utils.py的代码如下:

python 复制代码
import matplotlib
import matplotlib.pyplot as plt
from matplotlib import cm, colors
import cartopy.crs as ccrs
import descartes
import numpy as np
import os
import rasterio
from rasterio import features
import geopandas as gpd

np.random.seed(seed=101)
matplotlib.rcParams["savefig.bbox"] = "tight"
matplotlib.rcParams["savefig.dpi"] = 256
plt.style.use("seaborn-v0_8-whitegrid")

# read example elevation data and derive background hillslope
fn = os.path.join(os.path.dirname(__file__), r"D:\CLIPGY.tif")
with rasterio.open(fn, "r") as src:
    elevtn = src.read(1)
    extent = np.array(src.bounds)[[0, 2, 1, 3]]
    crs = src.crs
ls = matplotlib.colors.LightSource(azdeg=115, altdeg=45)
hs = ls.hillshade(np.ma.masked_equal(elevtn, -9999), vert_exag=1e3)


# convenience method for plotting
def quickplot(
    gdfs=[], raster=None, hillshade=True, extent=extent, hs=hs, title="", filename=""
):
    fig = plt.figure(figsize=(8, 15))
    ax = fig.add_subplot(projection=ccrs.PlateCarree())
    # plot hillshade background
    if hillshade:
        ax.imshow(
            hs,
            origin="upper",
            extent=extent,
            cmap="Greys",
            alpha=0.3,
            zorder=0,
        )
    # plot geopandas GeoDataFrame
    for gdf, kwargs in gdfs:
        gdf.plot(ax=ax, **kwargs)
    if raster is not None:
        data, nodata, kwargs = raster
        ax.imshow(
            np.ma.masked_equal(data, nodata),
            origin="upper",
            extent=extent,
            **kwargs,
        )
    ax.set_aspect("equal")
    ax.set_title(title, fontsize="large")
    ax.text(
        0.01, 0.01, "created with pyflwdir", transform=ax.transAxes, fontsize="large"
    )
    if filename:
        plt.savefig(f"{filename}.png")
    return ax


# convenience method for vectorizing a raster
def vectorize(data, nodata, transform, crs=crs, name="value"):
    feats_gen = features.shapes(
        data,
        mask=data != nodata,
        transform=transform,
        connectivity=8,
    )
    feats = [
        {"geometry": geom, "properties": {name: val}} for geom, val in list(feats_gen)
    ]

    # parse to geopandas for plotting / writing to file
    gdf = gpd.GeoDataFrame.from_features(feats, crs=crs)
    gdf[name] = gdf[name].astype(data.dtype)
    return gdf

执行代码如下:

python 复制代码
import rasterio
import numpy as np
import pyflwdir

from utils import (
    quickplot,
    colors,
    cm,
    plt,
)  # data specific quick plot convenience method

# read elevation data of the rhine basin using rasterio
with rasterio.open(r"D:\Raster.tif", "r") as src:
    elevtn = src.read(1)
    nodata = src.nodata
    transform = src.transform
    crs = src.crs
    extent = np.array(src.bounds)[[0, 2, 1, 3]]
    latlon = src.crs.is_geographic
    prof = src.profile

ax = quickplot(title="Elevation")
im = ax.imshow(
    np.ma.masked_equal(elevtn, -9999),
    extent=extent,
    cmap="gist_earth_r",
    alpha=0.5,
    vmin=0,
    vmax=1000,
)
fig = plt.gcf()
cax = fig.add_axes([0.8, 0.37, 0.02, 0.12])
fig.colorbar(im, cax=cax, orientation="vertical", extend="max")
cax.set_ylabel("elevation [m+EGM96]")
# plt.savefig('elevation.png', dpi=225, bbox_axis='tight')
python 复制代码
#####划分子流域
import geopandas as gpd
import numpy as np
import rasterio
import pyflwdir

# local convenience methods (see utils.py script in notebooks folder)
from utils import vectorize  # convenience method to vectorize rasters
from utils import quickplot, colors, cm  # data specific quick plot method

# returns FlwDirRaster object
flw = pyflwdir.from_dem(
    data=elevtn,
    nodata=src.nodata,
    transform=transform,
    latlon=latlon,
    outlets="min",
)
import geopandas as gpd

feats = flw.streams(min_sto=4)
gdf = gpd.GeoDataFrame.from_features(feats, crs=crs)

# create nice colormap of Blues with less white
cmap_streams = colors.ListedColormap(cm.Blues(np.linspace(0.4, 1, 7)))
gdf_plot_kwds = dict(column="strord", cmap=cmap_streams)
# plot streams with hillshade from elevation data (see utils.py)
ax = quickplot(
    gdfs=[(gdf, gdf_plot_kwds)],
    title="Streams based steepest gradient algorithm",
    filename="flw_streams_steepest_gradient",
)
python 复制代码
# update data type and nodata value properties which are different compared to the input elevation grid and write to geotif
prof.update(dtype=d8_data.dtype, nodata=247)
with rasterio.open(r"D:\flwdir.tif", "w", **prof) as src:
    src.write(d8_data, 1)

######################     
#Delineation of (sub)basins#
###########
with rasterio.open(r"D:\flwdir.tif", "r") as src:
    flwdir = src.read(1)
    crs = src.crs
    flw = pyflwdir.from_array(
        flwdir,
        ftype="d8",
        transform=src.transform,
        latlon=crs.is_geographic,
        cache=True,
    )
    # define output locations
    #x, y = np.array([106.6348,26.8554]), np.array([107.0135,26.8849])
    #x, y = np.array([26.8554,106.6348]), np.array([26.8849,107.0135])
    x, y = np.array([106.4244,107.0443]), np.array([26.8554,27.0384])
    gdf_out = gpd.GeoSeries(gpd.points_from_xy(x, y, crs=4326))
    # delineate subbasins
    subbasins = flw.basins(xy=(x, y), streams=flw.stream_order() >= 5)
    # vectorize subbasins using the vectorize convenience method from utils.py
    gdf_bas = vectorize(subbasins.astype(np.int32), 0, flw.transform, name="basin")
    gdf_bas.head()

# plot
# key-word arguments passed to GeoDataFrame.plot()
gpd_plot_kwds = dict(
    column="basin",
    cmap=cm.Set3,
    legend=True,
    categorical=True,
    legend_kwds=dict(title="Basin ID [-]"),
    alpha=0.5,
    edgecolor="black",
    linewidth=0.8,
)
points = (gdf_out, dict(color="red", markersize=20))
bas = (gdf_bas, gpd_plot_kwds)
# plot using quickplot convenience method from utils.py
ax = quickplot([bas, points], title="Basins from point outlets", filename="flw_basins")
python 复制代码
# calculate subbasins with a minimum stream order 7 and its outlets
subbas, idxs_out = flw.subbasins_streamorder(min_sto=7, mask=None)
# transfrom map and point locations to GeoDataFrames
gdf_subbas = vectorize(subbas.astype(np.int32), 0, flw.transform, name="basin")
gdf_out = gpd.GeoSeries(gpd.points_from_xy(*flw.xy(idxs_out), crs=4326))
# plot
gpd_plot_kwds = dict(
    column="basin", cmap=cm.Set3, edgecolor="black", alpha=0.6, linewidth=0.5
)
bas = (gdf_subbas, gpd_plot_kwds)
points = (gdf_out, dict(color="k", markersize=20))
title = "Subbasins based on a minimum stream order"
ax = quickplot([bas, points], title=title, filename="flw_subbasins")
python 复制代码
# get the first level nine pfafstetter basins
pfafbas1, idxs_out = flw.subbasins_pfafstetter(depth=1)
# vectorize raster to obtain polygons
gdf_pfaf1 = vectorize(pfafbas1.astype(np.int32), 0, flw.transform, name="pfaf")
gdf_out = gpd.GeoSeries(gpd.points_from_xy(*flw.xy(idxs_out), crs=4326))
gdf_pfaf1.head()

# plot
gpd_plot_kwds = dict(
    column="pfaf",
    cmap=cm.Set3_r,
    legend=True,
    categorical=True,
    legend_kwds=dict(title="Pfafstetter \nlevel 1 index [-]", ncol=3),
    alpha=0.6,
    edgecolor="black",
    linewidth=0.4,
)

points = (gdf_out, dict(color="k", markersize=20))
bas = (gdf_pfaf1, gpd_plot_kwds)
title = "Subbasins based on pfafstetter coding (level=1)"
ax = quickplot([bas, points], title=title, filename="flw_pfafbas1")
python 复制代码
# lets create a second pfafstetter layer with a minimum subbasin area of 5000 km2
pfafbas2, idxs_out = flw.subbasins_pfafstetter(depth=2, upa_min=5000)
gdf_pfaf2 = vectorize(pfafbas2.astype(np.int32), 0, flw.transform, name="pfaf2")
gdf_out = gpd.GeoSeries(gpd.points_from_xy(*flw.xy(idxs_out), crs=4326))
gdf_pfaf2["pfaf"] = gdf_pfaf2["pfaf2"] // 10
gdf_pfaf2.head()


# plot
bas = (gdf_pfaf2, gpd_plot_kwds)
points = (gdf_out, dict(color="k", markersize=20))
title = "Subbasins based on pfafstetter coding (level=2)"
ax = quickplot([bas, points], title=title, filename="flw_pfafbas2")
python 复制代码
# calculate subbasins with a minimum stream order 7 and its outlets
min_area = 2000
subbas, idxs_out = flw.subbasins_area(min_area)
# transfrom map and point locations to GeoDataFrames
gdf_subbas = vectorize(subbas.astype(np.int32), 0, flw.transform, name="basin")
# randomize index for visualization
basids = gdf_subbas["basin"].values
gdf_subbas["color"] = np.random.choice(basids, size=basids.size, replace=False)
# plot
gpd_plot_kwds = dict(
    column="color", cmap=cm.Set3, edgecolor="black", alpha=0.6, linewidth=0.5
)
bas = (gdf_subbas, gpd_plot_kwds)
title = f"Subbasins based on a minimum area of {min_area} km2"
ax = quickplot([bas], title=title, filename="flw_subbasins_area")
相关推荐
江沉晚呤时8 小时前
在 C# 中调用 Python 脚本:实现跨语言功能集成
python·microsoft·c#·.net·.netcore·.net core
电脑能手8 小时前
如何远程访问在WSL运行的Jupyter Notebook
ide·python·jupyter
Edward-tan9 小时前
CCPD 车牌数据集提取标注,并转为标准 YOLO 格式
python
老胖闲聊9 小时前
Python I/O 库【输入输出】全面详解
开发语言·python
倔强青铜三9 小时前
苦练Python第18天:Python异常处理锦囊
人工智能·python·面试
倔强青铜三9 小时前
苦练Python第17天:你必须掌握的Python内置函数
人工智能·python·面试
迷路爸爸18010 小时前
让 VSCode 调试器像 PyCharm 一样显示 Tensor Shape、变量形状、变量长度、维度信息
ide·vscode·python·pycharm·debug·调试
咸鱼鲸11 小时前
【PyTorch】PyTorch中的数据预处理操作
人工智能·pytorch·python
Dxy123931021611 小时前
Python ExcelWriter详解:从基础到高级的完整指南
开发语言·python
金玉满堂@bj11 小时前
Conda 安装包的用途
python