高度剖面线的每侧10 km


15

如何获得某个地形带的高程剖面图?

应考虑10 km(在所定义的线的两侧)内的最高海拔。

希望我的问题清楚。提前非常感谢您。


定义轮廓的线是一条简单的直线,还是由多个带有角的线段组成?
杰克

该行包括几个部分。但是所有段都是直线。:)我的意思是没有曲线。
卡拉

只是...就像他们所说的..spitballing ...但是您能用10公里的缓冲区来缓冲线路吗?然后选择缓冲区中的所有功能...然后选择最大值?
Ger

1
您能提供想要达到的目标的图像吗?
亚历山大·内托

@ Alex:我想要的结果是一个常规的高程图。但是使用10km的缓冲区,以便在图形中显示所选路径的每一侧的最大值10km。
卡拉

Answers:


14

在注释之后,这是适用于垂直线段的版本。请谨慎使用,因为我尚未对其进行彻底的测试!

这种方法比@whuber的答案笨拙得多-部分是因为我不是一个很好的程序员,部分是因为矢量处理有点麻烦。如果您需要垂直线段,我希望它至少可以帮助您入门。

您需要拥有ShapelyFionaNumpy 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()

下图显示了脚本输出的示例。您输入一个代表中心线的shapefile,并指定垂直线的长度及其间距。输出是一个新的shapefile,该文件在此图像中包含红线,每条红线都有一个关联的属性,用于指定距轮廓起点的距离。

脚本输出示例

正如@whuber在评论中所说,一旦您进入了这个阶段,其余的工作就很容易了。下图显示了将输出添加到ArcMap的另一个示例。

在此处输入图片说明

使用“ 要素转栅格”工具将垂直线转换为分类栅格。将栅格设置为输出shapefile中VALUEDist字段。还记得设置工具Environments,这样ExtentCell sizeSnap raster是相同的底层DEM。您应该以行的栅格表示形式结束,如下所示:

在此处输入图片说明

最后,将此栅格转换为整数网格(使用Int工具或栅格计算器),并将其用作“ 区域统计表作为表格”工具的输入区域。您应该最终得到如下输出表:

在此处输入图片说明

VALUE表中字段给出了距原始轮廓线起点的距离。其他列为每个横断面的值提供了各种统计信息(最大值,平均值等)。您可以使用此表来绘制您的摘要配置文件。

注意:此方法的一个明显问题是,如果您的原始线非常摆动,则某些样条线可能会重叠。ArcGIS中的区域统计工具无法处理重叠的区域,因此,发生这种情况时,一条样条线将优先于另一条样条线。这可能对您正在做的事情没有问题。

祝好运!


3
+1这是一个伟大贡献的良好开端!如果仔细观察第二个图,您会发现一些较短的样条线:它们是在弯道附近相交的样条线。这是因为您的计算样条线的算法错误地假定了每个线段的位移都相等spc,但是折弯会缩短位移。相反,您应该对横向矢量进行归一化(将其分量除以矢量的长度),然后再乘以所需的样条半径。
whuber

您说得很对-感谢@whuber的反馈!希望现在已解决...
JamesS

亲爱的詹姆斯,我会尽力感谢您。该解决方案非常适合。
卡拉

11

10 km内的最高海拔是使用圆形10 km半径计算的邻域最大值,因此只需沿轨迹提取该邻域最大网格的轮廓即可。

这是一个带有轨迹的阴影DEM(黑线从底部到顶部):

DEM

该图像大约是17 x 10公里。为了说明该方法,我选择的半径仅为1 km,而不是10 km。其1 km的缓冲区以黄色显示。

DEM的邻域最大值总是看起来有些奇怪,因为它会在一个最大值(可能是山顶)落在10 km以上而另一个高度在另一个高程处在10 km之内的点上跳值。 。特别是,支配其周围环境的山顶将贡献以局部最大高程点为中心的理想值圆:

最大邻域

在此地图上,暗度较高。

这是原始DEM(蓝色)和邻域最大值(红色)的轮廓图:

个人资料

计算方法是将轨迹分成相距0.1 km的规则间隔的点(从南端开始),提取这些点的高程,然后对所得三元线(距起点,高程,最大高程的距离)进行合并散点图。0.1 km的点间距选择为实质上小于缓冲区半径,但足够大以使计算快速进行(是瞬时的)。


不过,这并不完全正确,对吧?不应使用围绕每个点的圆形缓冲区,而不是使用长度为20 km的正交线对基础栅格进行采样吗?至少这就是我解释卡拉考虑到的“线路两侧10公里内的最高值”的要求。
杰克

4
@jake我不会说“不准确”:您只是提供了另一种解释。“在……的每一面”是一个模糊的术语,可以使用更好的限定条件。我可以为像您这样的解释提出解决方案;一种方法使用区域最大值。但是,它更复杂,执行起来也慢得多。我们为什么不首先了解OP对这个简单解决方案的看法?
Whuber

错误的字词选择,我不应该使用“准确”的字眼-对此感到遗憾
Jake

1
因为您知道如何使用配置文件工具,所以您差不多完成了。QGIS具有与GRASS的接口,其中包括邻域操作。只需使用r.neighbors应用邻域最大运算并分析其结果。
whuber

1
@JamesS您不想进行平行移动,而是要使每个交叉轮廓都垂直于中心线。(通过使用适当的长而窄的邻域进行邻域最大值计算,可以完全按照我在此描述的方式实现并行移位方法。)我很确定您可以在此站点上找到用于构建等距垂直线段集的代码沿多段线;那是困难的部分。其他所有事情都只是沿这些段提取DEM值并将其汇总的问题。
ub

6

我遇到了同样的问题,并尝试了James S的解决方案,但无法让GDAL与Fiona一起使用。

然后,我在QGIS 2.4中发现了SAGA算法“ Cross Profiles”,并得到了我想要的结果,并且我想您也在寻找(见下文)。

在此处输入图片说明


嗨,我几年前才看到这篇文章。我正面临着与线程启动器相同的问题,而且,对于(Q)GIS来说,它还是一个新手,我很高兴获得上图所示的知识。但是,如何处理数据?Cross Profiles图层显示了每个采样点的高程,但我会在以下方面寻求帮助: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()

谢谢ket-我已经尝试过了,但是不幸的是,它对我来说无法充分发挥作用。我已经为脚本提供了一个具有单个折线特征的shapefile,但是我的输出只是属性表,其中“ M100”值的零位很大-映射中未显示任何特征。有想法吗?
davehughes87,2015年

没关系-现在意识到您的脚本似乎在折线的每个线段(而不是每个“ spc”米)的ENDS处计算垂直线。这意味着在循环中到达n_prof并生成“ nan”值之前,我用尽了折线。
davehughes87
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.