Як я можу отримати профіль висоти для смуги місцевості?
Слід враховувати найвищу висоту в межах 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 або растровий калькулятор) і використовуйте його як зони введення для Зональної статистики як інструмент « Таблиця ». Ви маєте вийти з такою таблицею виводу:
VALUE
Поле в цій таблиці дає відстань від початку вихідної лінії профілю. В інших стовпцях наводяться різні статистичні дані (максимум, середнє значення тощо) для значень кожного трансекту. Ви можете використовувати цю таблицю для побудови вашого підсумкового профілю.
NB: Однією з очевидних проблем цього методу є те, що, якщо ваша початкова лінія дуже химерно, деякі трансектні лінії можуть перетинатися. Засоби зональної статистики в ArcGIS не можуть мати справу з зонами, що перекриваються, тому, коли це трапиться, одна з ваших трансектних ліній матиме перевагу над іншою. Це може бути або не бути проблемою для того, що ви робите.
Удачі!
spc
, але вигини скорочують переміщення. Натомість слід нормалізувати вектор поперечного напрямку (розділити його компоненти на довжину вектора), а потім помножити на потрібний радіус трансекта.
Найвища висота на відстані 10 км - це максимальне значення мікрорайону, обчислене круговим радіусом 10 км, тому просто витягніть профіль цієї максимальної сітки навколо траєкторії.
Ось DEM з загорнутими на схилах пагорбами з траєкторією (чорна лінія, що йде знизу вгору):
Це зображення приблизно 17 на 10 кілометрів. Для ілюстрації методу я вибрав радіус всього 1 км, а не 10 км. Її буфер на 1 км зображений жовтим кольором.
Максимум околиці DEM завжди виглядатиме дещо дивно, тому що він, як правило, стрибне у значеннях у точках, коли один максимум (вершина пагорба, можливо) падає трохи більше 10 км, а інший максимум на іншій висоті припадає лише в межах 10 км . Зокрема, вершини пагорбів, що панують над їх оточенням, сприятимуть ідеальним колам цінностей, зосередженим у точці локальної максимальної висоти:
На цій карті темніше вище.
Ось графік профілів оригінальної DEM (синій) та максимум околиць (червоний):
Він був обчислений шляхом поділу траєкторії на регулярно розташовані точки на відстані 0,1 км (починаючи від південної верхівки), витягуючи висоти в цих точках і здійснюючи з’єднаний розкид отриманих трійки (відстань від початку, висота, максимальна висота). Відстань у точці 0,1 км було вибрано значно меншим за радіус буфера, але досить великим, щоб обчислення було швидко проходити (це було миттєво).
У мене була така ж проблема і я спробував рішення Джеймса S, але не міг змусити GDAL працювати з Фіоною.
Потім я виявив алгоритм SAGA "Перехресні профілі" в 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()