我想在其他CRS中用python绘制栅格化的地理数据。
选择的工具似乎是calculate_default_transform
模块中的rasterio.warp
函数。但是结果不是我期望的。
下面是说明我的问题的MWE。
我创建了一个GeoDataFrame,它只是源CRS中的一个矩形。此外,我创建了一个与源CRS中的矩形对齐的点(只是坐标)的数组。
然后,我使用其to_crs()
方法将多边形重新投影到目标CRS(这很好用)。当我尝试使用calculate_default_transform()
对栅格坐标进行相同操作时,遇到了问题。
奇怪的是,calculate_default_transform
返回的变换仍然在第二和第四位置具有0值,指示目标点在水平和垂直线上,而实际情况并非如此。结果,目标点不再与多边形对齐。
我当然可以将栅格点转换为GeoDataFrame
。但是,这在现实生活中是不可行的,因为我想将其应用到大量数组中。将它们转换为GeoDataFrame
会导致我正在使用的硬件出现内存溢出。
#!/usr/bin/python3
# standard modules
import itertools
# PyPI modules
import numpy as np
from affine import Affine
import fiona
import shapely
import geopandas as gp
import matplotlib.pyplot as plt
from rasterio.warp import calculate_default_transform
from rasterio.control import GroundControlPoint
# =========================================================
# setup
# =========================================================
# dimensions of the raster
width = 4
height = 3
# bounds of the source raster
lon_min = 7.
lon_max = 12.
lat_min = 46.
lat_max = 49.
# source CRS (regular lon-lat grid)
crs_src = fiona.crs.from_epsg(4326)
# target CRS (UTM zone that covers central Europe)
crs_dst = '+proj=utm +zone=33 +ellps=WGS84 +datum=WGS84 +units=m +no_defs'
# =========================================================
# =========================================================
# construct a rectangle as GeoDataFrame for reference
# =========================================================
# counter-clockwise from top left
corners = (
(lon_min, lat_max),
(lon_min, lat_min),
(lon_max, lat_min),
(lon_max, lat_max),
)
polygon = shapely.geometry.Polygon(corners)
box = gp.GeoDataFrame(geometry=[polygon], crs=crs_src)
# =========================================================
# =========================================================
# construct a raster
# =========================================================
# coordinates, starting from top left
lon_inc = (lon_max - lon_min) / (width - 1)
lat_inc = (lat_max - lat_min) / (height - 1)
lons = np.arange(lon_min, lon_max + 0.5 * lon_inc, lon_inc)
lats = np.arange(lat_min, lat_max + 0.5 * lat_inc, lat_inc)[::-1] # top->bottom
# transform
x0 = lons[0]
y0 = lats[0]
translation_src = Affine.translation(lon_min, lat_max)
scale_src = Affine.scale(lon_inc, - lat_inc) # top left -> bottom right
transform_src = translation_src * scale_src
# =========================================================
def compute_raster_coordinates(transform, width, height):
"""Return x- and y-coordinates of raster data as pair of arrays."""
# i : indices in x-direction
# j : indices in y-direction
i = np.arange(width)
j = np.arange(height)
im, jm = np.meshgrid(i, j)
# index -> coordinate
x, y = transform * (im, jm)
return x, y
# =========================================================
# get transform for target CRS
# (using bounds)
# =========================================================
transform_dst, width_dst, height_dst = calculate_default_transform(
src_crs=crs_src,
dst_crs=crs_dst,
width=width,
height=height,
left=lon_min,
right=lon_max,
bottom=lat_min,
top=lat_max,
dst_width=width,
dst_height=height,
)
print('width (src/dst): %i/%i' % (width, width_dst))
print('height (src/dst): %i/%i' % (height, height_dst))
# =========================================================
# try an alternative way to get the desired transform
# (using ground control points)
# =========================================================
# construct sequence of ground control points
gcps_src = []
for i, j in itertools.product(range(width), range(height)):
lon = lons[i]
lat = lats[j]
gcp = GroundControlPoint(j, i, lon, lat)
gcps_src.append(gcp)
# calculate target transform using these points
transform_dst_alt, width_dst_alt, height_dst_alt = calculate_default_transform(
src_crs=crs_src,
dst_crs=crs_dst,
width=width,
height=height,
gcps=gcps_src,
dst_width=width,
dst_height=height,
)
print('width (src/alt): %i/%i' % (width, width_dst_alt))
print('height (src/alt): %i/%i' % (height, height_dst_alt))
# =========================================================
print('')
print('Transform (src):\n%s' % repr(transform_src))
print('')
print('Transform (dst):\n%s' % repr(transform_dst))
print('')
print('Transform (alt):\n%s' % repr(transform_dst_alt))
# =========================================================
# plot the result
# =========================================================
plt.close()
ax = plt.subplot(2, 2, 1)
plt.title('source CRS')
box.to_crs(crs_src).plot(ax=ax)
x, y = compute_raster_coordinates(transform_src, width, height)
plt.plot(x, y, 'r.')
ax = plt.subplot(2, 2, 2)
plt.title('target CRS')
box.to_crs(crs_dst).plot(ax=ax)
x, y = compute_raster_coordinates(transform_dst, width_dst, height_dst)
plt.plot(x, y, 'r.')
ax = plt.subplot(2, 2, 4)
plt.title('target CRS (using alternative method)')
box.to_crs(crs_dst).plot(ax=ax)
x, y = compute_raster_coordinates(
transform_dst_alt, width_dst_alt, height_dst_alt,
)
plt.plot(x, y, 'r.')
plt.show()
# =========================================================
我希望这些点在重新投影到目标CRS之后仍与多边形对齐。