Профіль висоти 10 км в кожну сторону лінії


15

Як я можу отримати профіль висоти для смуги місцевості?

Слід враховувати найвищу висоту в межах 10 км (з кожного боку визначеної лінії).

Я сподіваюся, що моє питання зрозуміле. Наперед Вам дякую.


Чи лінія, що визначає ваш профіль, є простою прямою чи вона складається з декількох відрізків з кутами?
Джейк

Лінія складається з декількох сегментів. Але всі сегменти прямі. :) Я маю на увазі, що кривих немає.
Кара

Просто ... як то кажуть..спітболінг ... але чи можете ви забудувати лінію буфером у 10 км. потім виберіть усі функції в буфері ... потім виберіть найвищі значення?
Гер

1
Чи можете ви надати образ того, що ви хочете досягти?
Олександр Нето

@ Алекс: результат, який я хочу, - це звичайний графік висоти. Але з буфером 10 км для того, щоб найвище значення 10 км кожна сторона вибраного шляху показана на графіку.
Кара

Відповіді:


14

Виходячи з коментарів, ось версія, яка працює з перпендикулярними відрізками ліній. Будь ласка, використовуйте з обережністю, оскільки я не перевіряв її ретельно!

Цей метод набагато химерніший, ніж відповідь @ 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 не можуть мати справу з зонами, що перекриваються, тому, коли це трапиться, одна з ваших трансектних ліній матиме перевагу над іншою. Це може бути або не бути проблемою для того, що ви робите.

Удачі!


3
+1 Це приємний початок для великого внеску! Якщо уважно придивитись до другої фігури, ви помітите кілька коротших трансектів: це ті, що перетинаються біля вигинів. Це тому, що ваш алгоритм для обчислення трансектів неправильно передбачає, що переміщення кожного сегмента буде рівним spc, але вигини скорочують переміщення. Натомість слід нормалізувати вектор поперечного напрямку (розділити його компоненти на довжину вектора), а потім помножити на потрібний радіус трансекта.
whuber

Ви абсолютно праві - дякую за відгук @whuber! Сподіваюсь, виправлено зараз ...
JamesS

Дорогий Джеймс, я дуже спробую тобі дякую. Це рішення ідеально підходить.
Кара

11

Найвища висота на відстані 10 км - це максимальне значення мікрорайону, обчислене круговим радіусом 10 км, тому просто витягніть профіль цієї максимальної сітки навколо траєкторії.

Приклад

Ось DEM з загорнутими на схилах пагорбами з траєкторією (чорна лінія, що йде знизу вгору):

DEM

Це зображення приблизно 17 на 10 кілометрів. Для ілюстрації методу я вибрав радіус всього 1 км, а не 10 км. Її буфер на 1 км зображений жовтим кольором.

Максимум околиці DEM завжди виглядатиме дещо дивно, тому що він, як правило, стрибне у значеннях у точках, коли один максимум (вершина пагорба, можливо) падає трохи більше 10 км, а інший максимум на іншій висоті припадає лише в межах 10 км . Зокрема, вершини пагорбів, що панують над їх оточенням, сприятимуть ідеальним колам цінностей, зосередженим у точці локальної максимальної висоти:

Макс

На цій карті темніше вище.

Ось графік профілів оригінальної DEM (синій) та максимум околиць (червоний):

Профілі

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


Це не зовсім точно, правда? Замість кругового буфера навколо кожної точки не слід використовувати ортогональну лінію довжиною 20 км для вибірки базового растру? Принаймні, так я би трактував вимогу Кара "про найвище значення в межах 10 км з кожної сторони лінії", яке враховується.
Джейк

4
@jake Я б не сказав "неточним": ви просто пропонуєте альтернативне тлумачення. "На кожній стороні" - неясний термін, який міг би використовувати кращу кваліфікацію. Я можу запропонувати рішення для тлумачень, як ваше; один метод використовує зональний максимум. Однак це складніше і набагато повільніше у виконанні. Чому ми спочатку не бачимо, що думає ОП про це просте рішення?
whuber

Поганий вибір слів, я не повинен був використовувати "точний" - вибачте про це
Джейк

1
Оскільки ви знаєте, як користуватися інструментом «Профіль», ви майже закінчили. QGIS має інтерфейси до GRASS, які включають операції із сусідством. Просто застосуйте максимальну операцію по сусідству, використовуючи r.neighbors і профілюйте його результат.
whuber

1
@JamesS Ви не хочете робити паралельний зсув, ви хочете зробити кожен поперечний профіль перпендикулярним до центральної лінії. (Підхід паралельного зсуву може бути реалізований точно так, як я описав тут, використовуючи відповідний довгий і худий мікрорайон для максимального розрахунку мікрорайону.) Я впевнений, що ви можете знайти код на цьому сайті для побудови наборів рівномірно розташованих перпендикулярних відрізків ліній. уздовж полілінії; це важка частина. Все інше - лише питання вилучення значень DEM уздовж цих сегментів та їх узагальнення.
whuber

6

У мене була така ж проблема і я спробував рішення Джеймса S, але не міг змусити GDAL працювати з Фіоною.

Потім я виявив алгоритм SAGA "Перехресні профілі" в QGIS 2.4, і отримав саме той результат, який я хотів, і, я вважаю, ви теж шукаєте (див. Нижче).

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


Привіт, я щойно натрапив на цю посаду від декількох років тому. Я зіткнувся з тією ж проблемою, що і з нитковим стартером, і, будучи дуже новим для (Q) ГІС, я щасливий, що потрапив на малюнок вище. Як же я працюю з даними? У шарі перехресних профілів відображається висота для кожної вибіркової точки, але я б просив допомогти в 1) пошуку максимальної висоти для кожної поперечної лінії 2) пошуку координат перетину з початковою траєкторією 3) асоціації максимальних висот від 1 з координатами від 2. Чи може хтось допомогти, будь ласка? Заздалегідь дякую! Mal
Cpt Reynolds

6

Для всіх, хто цікавиться, ось модифікована версія коду 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()

Дякую кет - я спробував це, але, на жаль, не працює для мене повністю. Я дав скрипту форм-файл із єдиною полілінійною функцією, але моїм результатом є лише таблиця атрибутів з великою кількістю нулів для значення "M100" - жодних функцій, які не відображаються на карті. Ідеї?
davehughes87

Не зауважте - тепер зрозуміло, що ваш сценарій, здається, обчислює перпендикулярні лінії на ENDS кожного сегмента полілінії, а не на кожних метрах "spc". Це означає, що мені не вистачало поліліній для роботи до того, як n_prof був досягнутий у циклі і не було створено значення "nan".
davehughes87
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.