将python中的地理空间栅格数据重新投影到另一个CRS

时间:2019-08-06 16:09:54

标签: python geospatial affinetransform rasterio

我想在其他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之后仍与多边形对齐。

0 个答案:

没有答案