如何获得某个地形带的高程剖面图?
应考虑10 km(在所定义的线的两侧)内的最高海拔。
希望我的问题清楚。提前非常感谢您。
如何获得某个地形带的高程剖面图?
应考虑10 km(在所定义的线的两侧)内的最高海拔。
希望我的问题清楚。提前非常感谢您。
Answers:
在注释之后,这是适用于垂直线段的版本。请谨慎使用,因为我尚未对其进行彻底的测试!
这种方法比@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()
下图显示了脚本输出的示例。您输入一个代表中心线的shapefile,并指定垂直线的长度及其间距。输出是一个新的shapefile,该文件在此图像中包含红线,每条红线都有一个关联的属性,用于指定距轮廓起点的距离。
正如@whuber在评论中所说,一旦您进入了这个阶段,其余的工作就很容易了。下图显示了将输出添加到ArcMap的另一个示例。
使用“ 要素转栅格”工具将垂直线转换为分类栅格。将栅格设置为输出shapefile中VALUE
的Dist
字段。还记得设置工具Environments
,这样Extent
,Cell size
和Snap raster
是相同的底层DEM。您应该以行的栅格表示形式结束,如下所示:
最后,将此栅格转换为整数网格(使用Int工具或栅格计算器),并将其用作“ 区域统计表作为表格”工具的输入区域。您应该最终得到如下输出表:
的 VALUE
表中字段给出了距原始轮廓线起点的距离。其他列为每个横断面的值提供了各种统计信息(最大值,平均值等)。您可以使用此表来绘制您的摘要配置文件。
注意:此方法的一个明显问题是,如果您的原始线非常摆动,则某些样条线可能会重叠。ArcGIS中的区域统计工具无法处理重叠的区域,因此,发生这种情况时,一条样条线将优先于另一条样条线。这可能对您正在做的事情没有问题。
祝好运!
spc
,但是折弯会缩短位移。相反,您应该对横向矢量进行归一化(将其分量除以矢量的长度),然后再乘以所需的样条半径。
10 km内的最高海拔是使用圆形10 km半径计算的邻域最大值,因此只需沿轨迹提取该邻域最大网格的轮廓即可。
这是一个带有轨迹的阴影DEM(黑线从底部到顶部):
该图像大约是17 x 10公里。为了说明该方法,我选择的半径仅为1 km,而不是10 km。其1 km的缓冲区以黄色显示。
DEM的邻域最大值总是看起来有些奇怪,因为它会在一个最大值(可能是山顶)落在10 km以上而另一个高度在另一个高程处在10 km之内的点上跳值。 。特别是,支配其周围环境的山顶将贡献以局部最大高程点为中心的理想值圆:
在此地图上,暗度较高。
这是原始DEM(蓝色)和邻域最大值(红色)的轮廓图:
计算方法是将轨迹分成相距0.1 km的规则间隔的点(从南端开始),提取这些点的高程,然后对所得三元线(距起点,高程,最大高程的距离)进行合并散点图。0.1 km的点间距选择为实质上小于缓冲区半径,但足够大以使计算快速进行(是瞬时的)。
我遇到了同样的问题,并尝试了James S的解决方案,但无法让GDAL与Fiona一起使用。
然后,我在QGIS 2.4中发现了SAGA算法“ Cross Profiles”,并得到了我想要的结果,并且我想您也在寻找(见下文)。
对于感兴趣的任何人,这是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()