Criando raster onde cada célula registra a distância do mar?

10

Quero criar uma varredura com uma resolução de 25 metros × 25 metros, em que cada célula contém a distância da costa mais próxima, calculada a partir do centro da célula. Para fazer isso, tudo o que tenho é um arquivo de forma das costas da Nova Zelândia .

Eu tentei seguir o tutorial de Dominic Roye para fazê-lo em R que funciona ... mais ou menos. É uma resolução de até 1 km × 1 km, mas se eu tentar aumentar a RAM, ela excederá a disponível no meu PC (é necessário ~ 70 gb de RAM) ou qualquer outra que eu também tenha acesso. Ao dizer isso, acho que essa é uma limitação do R e suspeito que o QGIS possa ter uma maneira mais computacionalmente eficiente de criar esse raster, mas sou novo nele e não consigo descobrir como fazê-lo.

Eu tentei seguir Criando raster com distância para o recurso usando o QGIS? para criá-lo no QGIS, mas ele retorna este erro:

_core.QgsProcessingException: Não foi possível carregar a camada de origem para INPUT: C: /..../ Litoral / nz-linhas de costa-e-ilhas-polígonos-topo-150k.shp não encontrado

e não sei por que.

Alguém tem alguma sugestão do que pode estar errado ou uma maneira alternativa de fazer isso?

Editar:

A varredura que espero produzir teria cerca de 59684 linhas e 40827 colunas, para que ela se sobreponha à varredura anual de déficit de água da LINZ. Se a varredura produzida for maior que a varredura anual por déficit hídrico, eu posso cortá-la em R ...

Uma coisa que eu acho que pode ser um problema em potencial é que o arquivo de forma do litoral da Nova Zelândia tem uma grande quantidade de mar entre as ilhas, e não estou interessado em calcular a distância da costa para essas células. Eu realmente só quero calcular os valores para células que incluem alguma fatia de terra. Não tenho certeza de como fazer isso, ou se é realmente um problema.

André.B
fonte
11
Você está executando um script para fazer isso? Ou você está usando as ferramentas do QGIS? Algo para verificar, mesmo que pareça que deveria - verifique se o arquivo realmente existe onde você diz ... verifique também se você tem acesso de leitura e gravação a essa pasta específica.
Keagan Allan 19/09/19
Atualmente, estou usando as ferramentas, mas tenho muita vontade de aprender o script, mas não sei por onde começar. Estou certo de que o arquivo existe, pois carreguei o arquivo .shp no QGIS e ele aparece como uma imagem. Eu também deveria ter acesso de leitura / gravação, pois sou um administrador da máquina e está apenas na minha caixa de depósito.
André.B 19/09/19
Tente movê-lo para fora do Dropbox para uma unidade local. Pode haver um problema com o caminho que faz com que o QGIS o rejeite. O que você está procurando fazer deve ser bem simples no QGIS. Qual versão do QGIS você está usando?
Keagan Allan
11
Ok, tente converter a polilinha em uma varredura. A ferramenta Proximity no QGIS precisa de uma entrada raster. Brinque com as configurações conforme a ajuda da ferramenta: docs.qgis.org/2.8/en/docs/user_manual/processing_algs/gdalogr/… . Tome nota, ainda é um processo intensivo, eu estou testando isso por diversão e agora ele tem funcionado por 30 minutos e ainda vai ...
Keagan Allan
11
Qual o tamanho da varredura de saída em termos de linhas e colunas que você está tentando criar? Você realmente conseguirá trabalhar com essa varredura depois de criá-la? Se o tamanho do arquivo for um problema, você poderá criar blocos menores, o que também pode ser feito em paralelo em um cluster ou nuvem para acelerar.
Spacedman 22/09/19

Respostas:

9

Com PyQGIS e GDAL, a biblioteca python não é muito difícil de fazer isso. Você precisa de parâmetros de transformação geográfica (resolução superior esquerda x, x pixel, rotação, superior esquerda y, rotação, resolução ns pixel) e número de linhas e colunas para criar a rasterização resultante. Para calcular a distância da costa mais próxima, é necessária uma camada vetorial para representar a costa.

Com o PyQGIS , cada ponto raster como centro da célula é calculado e sua distância ao litoral é medida usando o método 'closestSegmentWithContext' da classe QgsGeometry . A biblioteca python GDAL é usada para produzir uma varredura com esses valores de distância na matriz de linhas x colunas.

O código a seguir foi usado para criar uma varredura de distância (resolução de 25 m × 25 m e 1000 linhas x 1000 colunas) começando no ponto (397106.7689872353, 4499634.06675821); perto da costa oeste dos EUA.

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

Depois de executar o código acima, a rasterização resultante foi carregada no QGIS e parece com a imagem a seguir (pseudocolor com 5 classes e rampa espectral). A projeção é UTM 10 N (EPSG: 32610)

insira a descrição da imagem aqui

xunilk
fonte
Isso pode não ser um problema, mas uma coisa que me preocupa um pouco é que o polígono é da Nova Zelândia e de suas ilhas vizinhas, o que significa que inclui uma grande quantidade do mar circundante. Estou tentando entender o código, mas com seu exemplo eu seria capaz de definir o valor de todas as células no mar para NA? Estou realmente interessado apenas na distância do mar a partir de pontos em terra.
André.B 20/09/19
Pedimos desculpas antecipadamente se esta é uma pergunta idiota, mas como escolho um novo ponto de partida na Nova Zelândia da maneira que você define as coordenadas para os estados? Além disso, como faço para mantê-lo no EPSG: 2193?
André.B 23/09/19
7

Pode ser uma solução para tentar:

  1. Gere uma grade (digite "point", algoritmo "Create grid")
  2. Calcule a distância mais próxima entre seus pontos (grade) e sua linha (costa) com o algoritmo "associar atributo pelo mais próximo". Tenha cuidado ao escolher apenas um máximo de 1 vizinhos mais próximos.

Agora você deve ter uma nova camada de pontos com a distância da costa, como neste exemplo insira a descrição da imagem aqui

  1. Se necessário, você pode converter sua nova camada de pontos em uma varredura (algoritmo "rasterizar")

insira a descrição da imagem aqui

Christophe
fonte
2

Dentro do QGIS, você pode tentar o plugin GRASS. Tanto quanto sei, ele gerencia a memória melhor que o R e espero que a outra solução falhe em grandes áreas.

o comando GRASS é chamado r.grow.distance, que você pode encontrar na barra de ferramentas de processamento. Observe que você precisa converter sua linha para varredura primeiro.

insira a descrição da imagem aqui

Um de seus problemas pode ser o tamanho da saída, para que você possa adicionar algumas opções úteis de criação, como (para um arquivo tif) BIGTIFF = SIM, TILED = SIM, COMPRESSAR = LZW, PREDICTOR = 3

radouxju
fonte
Existe uma maneira de eliminar qualquer área do mar para reduzir o tamanho / tempo de computação?
André.B 25/09/19
em teoria, se você usar a distância da tela (todos exibem pixels com o mesmo valor, ou seja, um polígono) em vez da linha da costa, economize tempo de computação. O tamanho não compactado da varredura deve ser o mesmo, mas a compactação será mais eficiente, portanto, você também deve reduzir o tamanho final.
Radouxju 25/09/19
0

Eu tentaria o contrário. Se você estiver usando o polígono da NZ, converta as arestas do polígono em linha. Depois disso, crie um buffer no limite a cada 25 metros de distância do limite (talvez o centrídeo possa ajudar a determinar quando parar). Em seguida, corte os buffers com polígono e depois converta esses polígonos em varredura. Não sei se isso funcionaria, mas definitivamente você precisará de menos RAM. E o PostGiS é ótimo quando você tem problemas de desempenho.

Espero que ajude pelo menos um pouco :)

Kumbra
fonte
0

Originalmente, eu não ia responder minha própria pergunta, mas um colega meu (que não usa este site) me escreveu um monte de código python para fazer o que eu estou procurando; incluindo limitar as células para ter a distância de costa apenas para células terrestres e deixar as células marítimas como NAs. O código a seguir deve ser capaz de executar em qualquer console python, com as únicas coisas que precisam de alteração:

1) Coloque o arquivo de script na mesma pasta que o arquivo de formas de interesse;

2) altere o nome do shapefile no script python para qualquer que seja o nome do seu shapefile;

3) defina a resolução desejada e;

4) altere a extensão para corresponder a outros rasters.

Arquivos de formato maiores do que o que estou usando exigirão grandes quantidades de RAM, mas, caso contrário, o script é rápido de executar (cerca de três minutos para produzir uma varredura de 50m de resolução e dez minutos para uma varredura de 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)
André.B
fonte