在每个像元记录着大海距离的地方创建栅格?


10

我想创建一个分辨率为25米×25米的栅格,其中每个像元包含到最近的海岸线的距离,该距离是根据像元的中心计算的。为此,我所拥有的只是新西兰海岸线的形状文件

我已经尝试按照Dominic Roye的教程在R中完成此工作,这有点...。可以将分辨率降低到大约1 km×1 km,但是如果我尝试将RAM调高一点,它的需求将远远超出PC上的可用内存(需要约70 gb的RAM)或其他我也可以访问的内存。这么说,我认为这是R的局限性,我怀疑QGIS可能具有创建此栅格的计算效率更高的方式,但我对此并不陌生,我还不太清楚如何做到这一点。

我已尝试使用QGIS创建具有一定距离的要素之后进行栅格化?在QGIS中创建它,但返回此错误:

_core.QgsProcessingException:无法为INPUT加载源层:C:/..../ Coastline / nz-coastlines-and-islands-polygons-topo-150k.shp未找到

我不知道为什么。

是否有人对可能出什么问题或采取其他替代方法有任何建议?

编辑:

我希望生成的栅格大约有59684行和40827列,以便与LINZ 的年度缺水栅格重叠。如果生成的栅格大于年度缺水栅格,我可以在R中进行剪裁...

我认为可能是一个潜在的问题是,新西兰海岸线的形状文件在各岛之间有大量海洋,因此我对计算这些单元到海岸的距离不感兴趣。我真的只想计算包含某些区域的单元格的值。我不确定如何执行此操作,或者是否确实存在问题。


1
您正在运行脚本来执行此操作吗?还是正在使用QGIS中的工具?即使听起来像应该进行检查,也要进行检查-检查文件在您说的位置确实存在...还检查您是否对该特定文件夹具有读写访问权限。
Keagan Allan

当前正在使用这些工具,但是我非常想学习该脚本,只是不确定从哪里开始。我确定该文件存在,因为我已经将.shp文件加载到QGIS中,并且作为图像弹出。我也应该具有读/写访问权限,因为我是计算机的管理员,它只在我的保管箱中。
André.B

尝试将其从Dropbox移到本地驱动器。导致QGIS拒绝它的路径可能存在问题。您想要做的事情在QGIS中应该非常简单。您正在使用哪个版本的QGIS?
Keagan Allan

1
好的,尝试将折线转换为栅格。QGIS中的“接近度”工具需要栅格输入。按照工具的帮助进行设置:docs.qgis.org/2.8/en/docs/user_manual/processing_algs/gdalogr/…。请注意,它仍然是一个密集的过程,我正在对其进行有趣的测试,并且它已经运行了30分钟并且仍在运行……
Keagan Allan

1
您要创建的行和列的输出栅格大小是多少?创建栅格后,您实际上将能够使用它吗?如果整个文件的大小有问题,是否可以创建较小的切片,也可以在群集或云上并行执行以提高速度。
Spacedman

Answers:


9

使用PyQGISGDAL python库并不是很难做到这一点。您需要地理转换参数(左上角的x,x像素分辨率,旋转度,左上角的y,旋转度,ns像素分辨率)以及行数和列数以创建最终的栅格。为了计算到最近海岸线的距离,需要一个用于表示海岸线的矢量层。

使用PyQGIS,可以计算每个栅格点作为像元的中心,并使用QgsGeometry类中的“ closestSegmentWithContext”方法测量其到海岸线的距离。GDAL python库用于以行x列数组的形式生成具有这些距离值的栅格。

以下代码用于创建以点为起点的距离栅格(25 m×25 m分辨率和1000行x 1000列)(397106.7689872353、4499634.06675821);美国西部海岸线附近。

from osgeo import gdal, osr
import numpy as np
from math import sqrt

registry = QgsProject.instance()

line = registry.mapLayersByName('shoreline_10N')

crs = line[0].crs()

wkt = crs.toWkt()

feats_line = [ feat for feat in line[0].getFeatures()]

pt = QgsPoint(397106.7689872353, 4499634.06675821)

xSize = 25
ySize = 25

rows = 1000
cols = 1000

raster = [ [] for i in range(cols) ]

x =   xSize/2
y = - ySize/2

for i in range(rows):
    for j in range(cols):
        point = QgsPointXY(pt.x() + x, pt.y() + y)
        tupla = feats_line[0].geometry().closestSegmentWithContext(point)
        raster[i].append(sqrt(tupla[0]))

        x += xSize
    x =  xSize/2
    y -= ySize

data = np.array(raster)

# Create gtif file 
driver = gdal.GetDriverByName("GTiff")

output_file = "/home/zeito/pyqgis_data/distance_raster.tif"

dst_ds = driver.Create(output_file, 
                       cols, 
                       rows, 
                       1, 
                       gdal.GDT_Float32)

#writting output raster
dst_ds.GetRasterBand(1).WriteArray( data )

transform = (pt.x(), xSize, 0, pt.y(), 0, -ySize)

#setting extension of output raster
# top left x, w-e pixel resolution, rotation, top left y, rotation, n-s pixel resolution
dst_ds.SetGeoTransform(transform)

# setting spatial reference of output raster 
srs = osr.SpatialReference()
srs.ImportFromWkt(wkt)
dst_ds.SetProjection( srs.ExportToWkt() )

dst_ds = None

运行以上代码后,将生成的栅格加载到QGIS中,如下图所示(具有5类和Spectral渐变的伪彩色)。投影为UTM 10 N(EPSG:32610)

在此处输入图片说明


这可能不是问题,但我有点担心的是多边形是新西兰及其周围的岛屿,这意味着它包含大量的周围海域。我正在设法弄清楚代码,但是通过您的示例,我可以将海上所有细胞的值设置为NA吗?我真的只对从陆地上到大海的距离感兴趣。
André.B

如果这是一个愚蠢的问题,请事先道歉,但是我如何通过设置州的坐标来选择新西兰的新起点呢?另外,如何将其保存在EPSG:2193中?
André.B

7

可能是尝试的解决方案:

  1. 生成网格(类型为“点”,算法为“创建网格”)
  2. 使用算法“按最近的连接属性”计算点(网格)和线(海岸)之间的最近距离。请谨慎选择最多最多1个最近的邻居。

现在,您应该拥有一个新点层,该点层具有到海岸的距离,例如本示例 在此处输入图片说明

  1. 如果需要,可以将新的点图层转换为栅格(算法“栅格化”)

在此处输入图片说明


2

在QGIS中,您可以尝试GRASS插件。据我所知,它比R更好地管理内存,我希望其他解决方案在大范围内也会失败。

GRASS命令称为r.grow.distance,您可以在处理工具栏中找到它。请注意,首先需要将线转换为栅格。

在此处输入图片说明

问题之一可能是输出的大小,因此您可以添加一些有用的创建选项,例如(对于tif文件)BIGTIFF = YES,TILED = YES,COMPRESS = LZW,PREDICTOR = 3


有没有一种方法可以消除任何海域以减少尺寸/计算时间?
André.B

从理论上讲,如果您使用距视线(所有视线具有相同值,即一个多边形)的距离而不是距海岸线的距离,则应该节省计算时间。栅格的未压缩大小应该相同,但是压缩会更有效,因此您还应该减小最终大小。
radouxju

0

我会尝试其他方法。如果使用的是poligon,则将多边形的边线转换为直线。之后,在距边界每25米的距离上在边界上创建缓冲区(也许中心点可能有助于确定何时停止)。然后用多边形切出缓冲区,然后将该多边形转换为栅格。我不确定这是否行得通,但绝对可以,您需要更少的RAM。当您遇到性能问题时,PostGiS很棒。

希望它可能至少会有所帮助:)


0

我本来不想回答自己的问题,但是我的一个同事(不使用此站点)给我写了一堆python代码来做我想要做的事情;包括限制细胞仅对陆地细胞具有到海岸的距离,并将海基细胞作为NA保留。以下代码应该能够从任何python控制台运行,唯一需要更改的地方是:

1)将脚本文件与所需的形状文件放在同一文件夹中;

2)将python脚本中的shapefile的名称更改为shapefile的名称;

3)设置所需的分辨率,并且;

4)更改范围以匹配其他栅格。

比我使用的更大的shapefile需要大量RAM,但否则脚本可以快速运行(生成50m分辨率栅格大约需要三分钟,而生成25m分辨率栅格则需要十分钟)。

#------------------------------------------------------------------------------

from osgeo import gdal, ogr
import numpy as np
from scipy import ndimage
import matplotlib.pyplot as plt
import time

startTime = time.perf_counter()

#------------------------------------------------------------------------------

# Define spatial footprint for new raster
cellSize = 50 # ANDRE CHANGE THIS!!
noData = -9999
xMin, xMax, yMin, yMax = [1089000, 2092000, 4747000, 6224000]
nCol = int((xMax - xMin) / cellSize)
nRow = int((yMax - yMin) / cellSize)
gdal.AllRegister()
rasterDriver = gdal.GetDriverByName('GTiff')
NZTM = 'PROJCS["NZGD2000 / New Zealand Transverse Mercator 2000",GEOGCS["NZGD2000",DATUM["New_Zealand_Geodetic_Datum_2000",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6167"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4167"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",173],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",1600000],PARAMETER["false_northing",10000000],AUTHORITY["EPSG","2193"],AXIS["Easting",EAST],AXIS["Northing",NORTH]]'

#------------------------------------------------------------------------------ 

inFile = "new_zealand.shp" # CHANGE THIS!!

# Import vector file and extract information
vectorData = ogr.Open(inFile)
vectorLayer = vectorData.GetLayer()
vectorSRS = vectorLayer.GetSpatialRef()
x_min, x_max, y_min, y_max = vectorLayer.GetExtent()

# Create raster file and write information
rasterFile = 'nz.tif'
rasterData = rasterDriver.Create(rasterFile, nCol, nRow, 1, gdal.GDT_Int32, options=['COMPRESS=LZW'])
rasterData.SetGeoTransform((xMin, cellSize, 0, yMax, 0, -cellSize))
rasterData.SetProjection(vectorSRS.ExportToWkt())
band = rasterData.GetRasterBand(1)
band.WriteArray(np.zeros((nRow, nCol)))
band.SetNoDataValue(noData)
gdal.RasterizeLayer(rasterData, [1], vectorLayer, burn_values=[1])
array = band.ReadAsArray()
del(rasterData)

#------------------------------------------------------------------------------

distance = ndimage.distance_transform_edt(array)
distance = distance * cellSize
np.place(distance, array==0, noData)

# Create raster file and write information
rasterFile = 'nz-coast-distance.tif'
rasterData = rasterDriver.Create(rasterFile, nCol, nRow, 1, gdal.GDT_Float32, options=['COMPRESS=LZW'])
rasterData.SetGeoTransform((xMin, cellSize, 0, yMax, 0, -cellSize))
rasterData.SetProjection(vectorSRS.ExportToWkt())
band = rasterData.GetRasterBand(1)
band.WriteArray(distance)
band.SetNoDataValue(noData)
del(rasterData)

#------------------------------------------------------------------------------

endTime = time.perf_counter()

processTime = endTime - startTime

print(processTime)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.