Створення растру, де кожна клітина записує відстань до моря?


10

Я хочу створити растр з роздільною здатністю 25 метрів × 25 метрів, де кожна комірка містить відстань до найближчої берегової лінії, обчислену від центру комірки. Для цього все, що я маю, - це формафайл узбережжя Нової Зеландії .

Я спробував дотримуватися підручника Домініка Роя для його виконання в R, який працює ... Це добре до приблизно 1 км × 1 км, але якщо я спробую підняти яку-небудь більшу оперативну пам'ять, вона вимагає значно перевищувати наявну на моєму ПК (~ 70 Гб оперативної пам’яті) або будь-яку іншу, до якої я теж маю доступ. Кажучи це, я думаю, що це обмеження R, і я підозрюю, що QGIS може мати більш обчислювально ефективний спосіб створення цього растру, але я новачок у ньому, і я не можу точно зрозуміти, як це зробити.

Я спробував слідкувати за створенням растра з відстані до функції за допомогою QGIS? щоб створити його в QGIS, але він повертає цю помилку:

_core.QgsProcessingException: Не вдалося завантажити вихідний шар для INPUT: C: /..../ Coastline / nz-coastlines-and-Islands-polygons-topo-150k.shp не знайдено

і я не впевнений, чому.

Хтось має якісь пропозиції щодо того, що може піти не так чи альтернативним способом цього?

Редагувати:

Растр, який я сподіваюся створити, мав би близько 59684 рядків і 40827 стовпців, щоб він перекривався річним річним дефіцитом води від LINZ. Якщо вироблений растр більший, ніж річний річний дефіцит води, я можу зробити його в R, хоча ...

Одне, що, на мою думку, може бути потенційним питанням - це те, що у форматі берегової лінії NZ є велика кількість моря між островами, і мені не цікаво підрахувати відстань до узбережжя для цих осередків. Я дійсно хочу лише обчислити значення для комірок, які включають деякий шматочок землі. Я не впевнений, як це зробити, чи це насправді проблема.


1
У вас працює сценарій для цього? Або ви використовуєте інструменти в QGIS? Щось перевірити, хоча це звучить так, як і слід - перевірити, чи файл існує там, де ви говорите, так само ... також перевірте, що ви читали та записували доступ до цієї конкретної папки.
Кіаган Аллан

В даний час використовую інструменти, але я дуже прагну вивчити сценарій, просто не знаю, з чого почати. Я впевнений, що файл існує, оскільки я завантажив .shp файл у QGIS і він з'являється як зображення. Я також повинен був читати / записувати доступ, оскільки я адміністратор на машині, і це просто в моєму папці.
Андре.B

Спробуйте перемістити його з Dropbox на локальний диск. Може виникнути проблема зі шляху, який змушує QGIS відхиляти його. Те, що ви шукаєте, має бути досить простим у QGIS. Яку версію QGIS ви використовуєте?
Кіган Аллан

1
Гаразд, спробуйте перетворити полілінію в растр. Інструмент «Близькість» у QGIS потребує растрового введення. Пограйте з налаштуваннями відповідно до довідки інструменту: docs.qgis.org/2.8/en/docs/user_manual/processing_algs/gdalogr/… . Візьміть на замітку, це все ще інтенсивний процес, я зараз тестую це для задоволення, і він працює вже 30 хвилин і все ще йде ...
Keagan Allan

1
Який розмір вихідного растру в частині рядків і стовпців ви намагаєтесь створити? Чи дійсно ви зможете працювати з цим растром, як тільки створити його? Якщо розмір файлу в цілому є проблемою, ви можете створити менші плитки, що також можна зробити паралельно на кластері або хмарі для швидкості.
Спайдермен

Відповіді:


9

З бібліотекою PyQGIS та GDAL python це зробити не дуже складно. Вам потрібні параметри геоперетворення (верхній лівий х, х роздільна здатність пікселя, обертання, верхній лівий у, обертання, ns піксельна роздільна здатність) та номер рядків та стовпців для створення результуючої растрової форми. Для обчислення відстані до найближчої берегової лінії необхідний векторний шар для представлення берегової лінії.

З використанням PyQGIS обчислюється кожна растрова точка як центр комірки, а її відстань до берегової лінії вимірюється за допомогою методу "найближчий сегментWithContext" з класу QgsGeometry . Бібліотека пітонів GDAL використовується для отримання растру з цими значеннями відстані в масиві рядків x стовпців.

Наступний код був використаний для створення растрової відстані (25 м × 25 м і 1000 рядків х 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 класами та спектральна рампа). Проекція становить UTM 10 N (EPSG: 32610)

введіть тут опис зображення


Це може бути не проблемою, але одне, що мене трохи хвилює, це те, що полігон знаходиться в Новій Зеландії та прилеглих до нього островах, а це означає, що він включає величезну кількість навколишнього моря. Я намагаюся обвести код навколо коду, але з вашого прикладу я зміг би встановити значення для всіх клітин у морі на NA? Мене справді цікавить лише відстань до моря від точок на суші.
Андре.B

Вибачтеся заздалегідь, якщо це нерозумне питання, але як я можу вибрати нову вихідну точку в Новій Зеландії таким чином, щоб ви встановили координати для штатів? Також як я можу зберігати його в EPSG: 2193?
Андре.B

7

Спробуйте спробувати:

  1. Створення сітки (введіть "крапку", алгоритм "Створити сітку")
  2. Обчисліть найближчу відстань між точками (сіткою) та вашою лінією (узбережжям) за допомогою алгоритму "приєднати атрибут до найближчого". Будьте обережні, щоб вибрати лише максимум 1 найближчого сусіда.

Тепер у вас повинен бути новий точковий шар з відстанню до узбережжя, як у цьому прикладі введіть тут опис зображення

  1. Якщо потрібно, ви можете перетворити новий точковий шар у растровий (алгоритм "растеризація")

введіть тут опис зображення


2

У QGIS ви можете спробувати плагін GRASS. Наскільки я знаю, вона управляє пам'яттю краще, ніж R, і я очікую, що інше рішення вийде з ладу на великих площах.

команда GRASS називається r.grow.distance, яку ви можете знайти на панелі інструментів обробки. Зауважте, що спочатку вам потрібно перетворити лінію в растр.

введіть тут опис зображення

Однією з ваших проблем може бути розмір виводу, тому ви можете додати кілька корисних варіантів створення, таких як (для файлу tif) BIGTIFF = ТАК, TILED = ТАК, COMPRESS = LZW, PREDICTOR = 3


Чи є спосіб, щоб я міг усунути будь-яку зону моря, щоб зменшити розмір / час обчислення?
Андре.B

теоретично, якщо ви використовуєте відстань від see (всі бачать пікселі з однаковим значенням, тобто багатокутник), а не від лінії узбережжя, ви повинні заощадити час обчислень. Нестиснений розмір растра повинен бути однаковим, але стиснення буде більш ефективним, тому ви також повинні зменшити кінцевий розмір.
radouxju

0

Я б спробував інше. Якщо ви використовуєте полігон NZ, тоді перетворіть краї полігону в лінію. Після цього створіть буфер на кордоні кожні 25 метрів відстані від кордону (можливо, центорид може допомогти визначити час зупинки). Потім виріжте буфери з багатокутника, а потім перетворіть ці багатокутники в растрові. Я не впевнений, що це спрацює, але, безумовно, вам знадобиться менше оперативної пам'яті. І PostGiS - це чудово, коли у вас є проблеми з продуктивністю.

Сподіваюся, це може допомогти хоч трохи :)


0

Я спочатку не збирався відповідати на власне запитання, але мій колега (який не використовує цей сайт), написав мені купу пітонного коду, щоб робити те, що я хочу; включаючи обмеження клітин, щоб вони мали відстань до узбережжя лише для наземних клітин і залишали клітини, засновані на морі, як НА. Наступний код повинен бути в змозі запускатися з будь-якої консолі python, лише для цього потрібно змінити:

1) Помістіть файл сценарію в ту саму папку, що і файл форми, який цікавить;

2) змінити ім'я формафайлу в скрипті python на те, на що ви хочете назвати файл вашої форми;

3) встановити бажану роздільну здатність;

4) змінити міру, щоб відповідати іншим растрам.

Більш масштабні формати, ніж те, що я використовую, вимагають великої кількості оперативної пам’яті, але в іншому випадку сценарій швидко запускається (приблизно три хвилини для отримання растру з роздільною здатністю 50 м і десять хвилин для растру з роздільною здатністю 25 м).

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

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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.