#| echo: false
import pandas as pd
import matplotlib.pyplot as plt
pd.options.display.max_rows = 6
pd.options.display.max_columns = 6
pd.options.display.max_colwidth = 35
plt.rcParams['figure.figsize'] = (5, 5)
import numpy as np
import shapely.geometry
import matplotlib.pyplot as plt
import geopandas as gpd
import rasterio
import rasterio.mask
import rasterstats
from rasterio.plot import show
import math
import os
# 出现错误CRSError: Invalid projection: epsg:4326: 的解决方案
# 因为装了多个版本的pyproj
# 在anaconda的目录下搜一下其他的proj.db文件的路径
# 复制路径在所装的环境文件夹下的Library文件夹下的那个路径,使用pyproj.datadir.set_data_dir()来更新目录
import pyproj
# pyproj.datadir.get_data_dir()
path = 'D:\work\miniconda3\Library\share\proj'
pyproj.datadir.set_data_dir(path)
src_srtm = rasterio.open('data/srtm.tif')
src_nlcd = rasterio.open('data/nlcd.tif')
src_grain = rasterio.open('data/grain.tif')
src_elev = rasterio.open('data/elev.tif')
src_dem = rasterio.open('data/dem.tif')
zion = gpd.read_file('data/zion.gpkg')
zion_points = gpd.read_file('data/zion_points.gpkg')
cycle_hire_osm = gpd.read_file('data/cycle_hire_osm.gpkg')
许多地理数据项目涉及集成来自许多不同来源的数据,例如遥感图像(栅格)和行政边界(矢量)。 输入栅格数据集的范围通常大于感兴趣区域。 在这种情况下,栅格裁剪和屏蔽对于统一输入数据的空间范围非常有用。 这两种操作都减少了对象内存的使用和后续分析步骤的相关计算资源,并且可能是创建涉及栅格数据的有吸引力的地图之前必要的预处理步骤。
我们将使用两个对象来说明光栅裁剪:
zion.gpkg
矢量图层目标对象和裁剪对象必须具有相同的投影。 以下将向量层“zion”重新投影到栅格“src_srtm”的 CRS 中:
zion = zion.to_crs(src_srtm.crs)
为了屏蔽图像,即将与“zion”多边形不相交的所有像素转换为“无数据”,我们使用“rasterio.mask.mask”函数,如下所示:
out_image_mask, out_transform_mask = rasterio.mask.mask(
src_srtm,
zion['geometry'],
crop=False,
nodata=9999
)
请注意,我们需要指定与栅格数据类型一致的“无数据”值。 由于“srtm.tif”的类型为“uint16”,因此我们选择“9999”(保证不会出现在栅格中的正整数)。
结果是带有屏蔽值的“out_image”数组:
out_image_mask
array([[[9999, 9999, 9999, ..., 9999, 9999, 9999],
[9999, 9999, 9999, ..., 9999, 9999, 9999],
[9999, 9999, 9999, ..., 9999, 9999, 9999],
...,
[9999, 9999, 9999, ..., 9999, 9999, 9999],
[9999, 9999, 9999, ..., 9999, 9999, 9999],
[9999, 9999, 9999, ..., 9999, 9999, 9999]]], dtype=uint16)
和新的“out_transform”:
out_transform_mask
Affine(0.0008333333332777796, 0.0, -113.23958321278403,
0.0, -0.0008333333332777843, 37.512916763165805)
请注意,遮罩(不裁剪!)不会修改栅格空间配置。 因此,新的变换与原始变换相同:
src_srtm.transform
Affine(0.0008333333332777796, 0.0, -113.23958321278403,
0.0, -0.0008333333332777843, 37.512916763165805)
不幸的是,“out_image”和“out_transform”对象不包含任何指示“9999”表示“无数据”的信息。 要将信息与栅格关联起来,我们必须将其与相应的元数据一起写入文件。 例如,要将裁剪后的栅格写入文件,我们需要修改元数据中的“无数据”设置:
out_meta = src_srtm.meta
out_meta.update(nodata=9999)
out_meta
{'driver': 'GTiff',
'dtype': 'uint16',
'nodata': 9999,
'width': 465,
'height': 457,
'count': 1,
'crs': CRS.from_epsg(4326),
'transform': Affine(0.0008333333332777796, 0.0, -113.23958321278403,
0.0, -0.0008333333332777843, 37.512916763165805)}
然后我们可以将裁剪后的栅格写入文件:
new_dataset = rasterio.open('output/srtm_masked.tif', 'w', **out_meta)
new_dataset.write(out_image_mask)
new_dataset.close()
现在我们可以重新导入栅格:
src_srtm_mask = rasterio.open('output/srtm_masked.tif')
.meta
属性包含 nodata
条目。 现在,任何相关操作(例如绘图)都将考虑“无数据”:
src_srtm_mask.meta
{'driver': 'GTiff',
'dtype': 'uint16',
'nodata': 9999.0,
'width': 465,
'height': 457,
'count': 1,
'crs': CRS.from_epsg(4326),
'transform': Affine(0.0008333333332777796, 0.0, -113.23958321278403,
0.0, -0.0008333333332777843, 37.512916763165805)}
裁剪意味着将栅格范围缩小到矢量图层的范围:
rasterio.mask.mask
表达式中使用相同的内容进行蒙版,只需设置 crop=True
而不是 crop=False
。例如,以下是我们如何获取“zion”的多边形范围,作为“shapely”几何对象:
bb = zion.unary_union.envelope
bb
该范围现在可用于屏蔽。 在这里,我们还使用“all_touched=True”选项,以便包含与范围部分重叠的像素:
out_image_crop, out_transform_crop = rasterio.mask.mask(
src_srtm,
[bb],
crop=True,
all_touched=True,
nodata=9999
)
@fig-raster-crop 显示原始栅格以及裁剪和遮罩的结果。
#| label: fig-raster-crop
#| fig-cap: Raster masking and cropping
fig, axes = plt.subplots(ncols=3, figsize=(9,5))
show(src_srtm, ax=axes[0])
zion.plot(ax=axes[0], color='none', edgecolor='black')
show(src_srtm_mask, ax=axes[1])
zion.plot(ax=axes[1], color='none', edgecolor='black')
show(out_image_crop, transform=out_transform_crop, ax=axes[2])
zion.plot(ax=axes[2], color='none', ed