Как я могу получить профиль высоты для полосы местности?
Наибольшая высота в пределах 10 км (на каждой стороне определенной линии) должна быть принята во внимание.
Я надеюсь, что мой вопрос ясен. Заранее большое спасибо.
Как я могу получить профиль высоты для полосы местности?
Наибольшая высота в пределах 10 км (на каждой стороне определенной линии) должна быть принята во внимание.
Я надеюсь, что мой вопрос ясен. Заранее большое спасибо.
Ответы:
Исходя из комментариев, вот версия, которая работает с перпендикулярными отрезками. Пожалуйста, используйте с осторожностью, поскольку я не проверил это полностью!
Этот метод гораздо более неуклюжий, чем ответ @ whuber - отчасти потому, что я не очень хороший программист, а отчасти потому, что обработка векторов - это нечто вроде ошибки. Я надеюсь, что по крайней мере вы начнете, если вам нужны перпендикулярные отрезки.
Для этого вам нужно установить пакеты Shapely , Fiona и Numpy Python (вместе с их зависимостями).
#-------------------------------------------------------------------------------
# Name: perp_lines.py
# Purpose: Generates multiple profile lines perpendicular to an input line
#
# Author: JamesS
#
# Created: 13/02/2013
#-------------------------------------------------------------------------------
""" Takes a shapefile containing a single line as input. Generates lines
perpendicular to the original with the specified length and spacing and
writes them to a new shapefile.
The data should be in a projected co-ordinate system.
"""
import numpy as np
from fiona import collection
from shapely.geometry import LineString, MultiLineString
# ##############################################################################
# User input
# Input shapefile. Must be a single, simple line, in projected co-ordinates
in_shp = r'D:\Perp_Lines\Centre_Line.shp'
# The shapefile to which the perpendicular lines will be written
out_shp = r'D:\Perp_Lines\Output.shp'
# Profile spacing. The distance at which to space the perpendicular profiles
# In the same units as the original shapefile (e.g. metres)
spc = 100
# Length of cross-sections to calculate either side of central line
# i.e. the total length will be twice the value entered here.
# In the same co-ordinates as the original shapefile
sect_len = 1000
# ##############################################################################
# Open the shapefile and get the data
source = collection(in_shp, "r")
data = source.next()['geometry']
line = LineString(data['coordinates'])
# Define a schema for the output features. Add a new field called 'Dist'
# to uniquely identify each profile
schema = source.schema.copy()
schema['properties']['Dist'] = 'float'
# Open a new sink for the output features, using the same format driver
# and coordinate reference system as the source.
sink = collection(out_shp, "w", driver=source.driver, schema=schema,
crs=source.crs)
# Calculate the number of profiles to generate
n_prof = int(line.length/spc)
# Start iterating along the line
for prof in range(1, n_prof+1):
# Get the start, mid and end points for this segment
seg_st = line.interpolate((prof-1)*spc)
seg_mid = line.interpolate((prof-0.5)*spc)
seg_end = line.interpolate(prof*spc)
# Get a displacement vector for this segment
vec = np.array([[seg_end.x - seg_st.x,], [seg_end.y - seg_st.y,]])
# Rotate the vector 90 deg clockwise and 90 deg counter clockwise
rot_anti = np.array([[0, -1], [1, 0]])
rot_clock = np.array([[0, 1], [-1, 0]])
vec_anti = np.dot(rot_anti, vec)
vec_clock = np.dot(rot_clock, vec)
# Normalise the perpendicular vectors
len_anti = ((vec_anti**2).sum())**0.5
vec_anti = vec_anti/len_anti
len_clock = ((vec_clock**2).sum())**0.5
vec_clock = vec_clock/len_clock
# Scale them up to the profile length
vec_anti = vec_anti*sect_len
vec_clock = vec_clock*sect_len
# Calculate displacements from midpoint
prof_st = (seg_mid.x + float(vec_anti[0]), seg_mid.y + float(vec_anti[1]))
prof_end = (seg_mid.x + float(vec_clock[0]), seg_mid.y + float(vec_clock[1]))
# Write to output
rec = {'geometry':{'type':'LineString', 'coordinates':(prof_st, prof_end)},
'properties':{'Id':0, 'Dist':(prof-0.5)*spc}}
sink.write(rec)
# Tidy up
source.close()
sink.close()
На рисунке ниже показан пример вывода из скрипта. Вы вводите шейп-файл, представляющий вашу центральную линию, и указываете длину перпендикулярных линий и их расстояние. На выходе получается новый шейп-файл, содержащий красные линии на этом изображении, с каждым из которых связан атрибут, определяющий его расстояние от начала профиля.
Как сказал @whuber в комментариях, как только вы дойдете до этого этапа, все остальное будет довольно просто. На рисунке ниже показан другой пример с выходом, добавленным в ArcMap.
Используйте инструмент Feature to Raster для преобразования перпендикулярных линий в категориальный растр. Установите растр VALUE
в качестве Dist
поля в выходном шейп-файле. Кроме того, помните , чтобы установить инструмент Environments
так , что Extent
, Cell size
и Snap raster
такие же , как для базового DEM. Вы должны получить растровое представление ваших строк, что-то вроде этого:
Наконец, преобразуйте этот растр в целочисленную сетку (используя инструмент Int или растровый калькулятор) и используйте его в качестве входных зон для Zonal Statistics как инструмента Table . Вы должны получить выходную таблицу, подобную этой:
VALUE
Поле в этой таблице дает расстояние от начала исходной линии профиля. Другие столбцы дают различную статистику (максимум, среднее и т. Д.) Для значений в каждом трансекте. Вы можете использовать эту таблицу для построения вашего итогового профиля.
NB. Одна очевидная проблема этого метода заключается в том, что если исходная линия очень волнистая, некоторые из линий разреза могут перекрываться. Инструменты зональной статистики в ArcGIS не могут работать с перекрывающимися зонами, поэтому, когда это происходит, одна из ваших линий разреза будет иметь приоритет над другой. Это может или не может быть проблемой для того, что вы делаете.
Удачи!
spc
, но изгибы сокращают смещения. Вместо этого вы должны нормализовать вектор поперечного направления (разделить его компоненты на длину вектора), а затем умножить его на желаемый радиус разреза.
Наибольшее превышение в пределах 10 км является максимальным значением окрестности, вычисленным с круговым радиусом 10 км, поэтому просто извлеките профиль этой сетки максимума окрестности вдоль траектории.
Вот высотная матрица с траекторией (черная линия, идущая снизу вверх):
Это изображение примерно 17 на 10 километров. Я выбрал радиус всего 1 км, а не 10 км, чтобы проиллюстрировать метод. Его 1-километровый буфер показан обведенным желтым цветом.
Максимум окрестности матрицы высот всегда будет выглядеть немного странно, потому что он будет стремиться подскочить в значениях в точках, где один максимум (возможно, вершина холма) падает чуть более 10 км, а другой максимум на другой высоте находится в пределах 10 км. , В частности, вершины холмов, которые доминируют в их окружении, будут создавать совершенные круги ценностей, центрированные в точке локального максимального подъема:
Темнее выше на этой карте.
Вот график профилей исходной матрицы высот (синий) и максимума окрестности (красный):
Он был рассчитан путем деления траектории на равномерно расположенные точки на расстоянии 0,1 км (начиная от южной оконечности), извлечения возвышений в этих точках и составления объединенной диаграммы рассеяния из получаемых троек (расстояние от начала, превышение, максимальное превышение). Расстояние между точками в 0,1 км было выбрано существенно меньшим, чем радиус буфера, но достаточно большим, чтобы вычисление прошло быстро (оно было мгновенным).
У меня была та же проблема, и я попробовал решение Джеймса С., но не смог заставить GDAL работать с Фионой.
Затем я обнаружил алгоритм SAGA "Cross Profiles" в QGIS 2.4 и получил именно тот результат, который мне нужен, и который, я полагаю, вы тоже ищете (см. Ниже).
Для всех, кто заинтересован, вот модифицированная версия кода JamesS, создающая перпендикулярные линии, используя только библиотеки numpy и osgeo. Благодаря JamesS, его ответ мне сегодня очень помог!
import osgeo
from osgeo import ogr
import numpy as np
# ##############################################################################
# User input
# Input shapefile. Must be a single, simple line, in projected co-ordinates
in_shp = r'S:\line_utm_new.shp'
# The shapefile to which the perpendicular lines will be written
out_shp = r'S:\line_utm_neu_perp.shp'
# Profile spacing. The distance at which to space the perpendicular profiles
# In the same units as the original shapefile (e.g. metres)
spc = 100
# Length of cross-sections to calculate either side of central line
# i.e. the total length will be twice the value entered here.
# In the same co-ordinates as the original shapefile
sect_len = 1000
# ##############################################################################
# Open the shapefile and get the data
driverShp = ogr.GetDriverByName('ESRI Shapefile')
sourceShp = driverShp.Open(in_shp, 0)
layerIn = sourceShp.GetLayer()
layerRef = layerIn.GetSpatialRef()
# Go to first (and only) feature
layerIn.ResetReading()
featureIn = layerIn.GetNextFeature()
geomIn = featureIn.GetGeometryRef()
# Define a shp for the output features. Add a new field called 'M100' where the z-value
# of the line is stored to uniquely identify each profile
outShp = driverShp.CreateDataSource(out_shp)
layerOut = outShp.CreateLayer('line_utm_neu_perp', layerRef, osgeo.ogr.wkbLineString)
layerDefn = layerOut.GetLayerDefn() # gets parameters of the current shapefile
layerOut.CreateField(ogr.FieldDefn('M100', ogr.OFTReal))
# Calculate the number of profiles/perpendicular lines to generate
n_prof = int(geomIn.Length()/spc)
# Define rotation vectors
rot_anti = np.array([[0, -1], [1, 0]])
rot_clock = np.array([[0, 1], [-1, 0]])
# Start iterating along the line
for prof in range(1, n_prof):
# Get the start, mid and end points for this segment
seg_st = geomIn.GetPoint(prof-1) # (x, y, z)
seg_mid = geomIn.GetPoint(prof)
seg_end = geomIn.GetPoint(prof+1)
# Get a displacement vector for this segment
vec = np.array([[seg_end[0] - seg_st[0],], [seg_end[1] - seg_st[1],]])
# Rotate the vector 90 deg clockwise and 90 deg counter clockwise
vec_anti = np.dot(rot_anti, vec)
vec_clock = np.dot(rot_clock, vec)
# Normalise the perpendicular vectors
len_anti = ((vec_anti**2).sum())**0.5
vec_anti = vec_anti/len_anti
len_clock = ((vec_clock**2).sum())**0.5
vec_clock = vec_clock/len_clock
# Scale them up to the profile length
vec_anti = vec_anti*sect_len
vec_clock = vec_clock*sect_len
# Calculate displacements from midpoint
prof_st = (seg_mid[0] + float(vec_anti[0]), seg_mid[1] + float(vec_anti[1]))
prof_end = (seg_mid[0] + float(vec_clock[0]), seg_mid[1] + float(vec_clock[1]))
# Write to output
geomLine = ogr.Geometry(ogr.wkbLineString)
geomLine.AddPoint(prof_st[0],prof_st[1])
geomLine.AddPoint(prof_end[0],prof_end[1])
featureLine = ogr.Feature(layerDefn)
featureLine.SetGeometry(geomLine)
featureLine.SetFID(prof)
featureLine.SetField('M100',round(seg_mid[2],1))
layerOut.CreateFeature(featureLine)
# Tidy up
outShp.Destroy()
sourceShp.Destroy()