地形帯の標高プロファイルを取得するにはどうすればよいですか?
(定義された線の両側の)10 km以内の最高標高を考慮する必要があります。
私の質問が明確であることを願っています。事前にどうもありがとうございました。
地形帯の標高プロファイルを取得するにはどうすればよいですか?
(定義された線の両側の)10 km以内の最高標高を考慮する必要があります。
私の質問が明確であることを願っています。事前にどうもありがとうございました。
回答:
コメントに続いて、垂直線分で機能するバージョンがあります。私は徹底的にテストしていないので注意して使用してください!
この方法は、@ 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に出力が追加された別の例を示しています。
使用ラスターに機能をカテゴリ別ラスタに垂線を変換するツール。ラスターVALUE
をDist
出力シェープファイルのフィールドに設定します。また、ツールを設定することを忘れないEnvironments
ようにExtent
、Cell size
そしてSnap raster
、あなたの根本的なDEMと同じです。最終的に、次のような行のラスタ表現になります。
最後に、このラスターを(Intツールまたはラスター計算機を使用して)整数グリッドに変換し、Zonal Statistics as Tableツールの入力ゾーンとして使用します。次のような出力テーブルが作成されるはずです。
VALUE
この表のフィールドは、元のプロファイルラインの始点からの距離を示します。他の列は、各トランセクトの値のさまざまな統計(最大、平均など)を提供します。この表を使用して、要約プロファイルをプロットできます。
NB:この方法の明らかな問題の1つは、元のラインが非常に不安定な場合、トランセクトラインの一部がオーバーラップする可能性があることです。ArcGISのゾーン統計ツールはオーバーラップゾーンを処理できないため、これが発生すると、トランセクトラインの1つが他のラインよりも優先されます。これは、あなたがしていることに問題があるかもしれませんし、そうでないかもしれません。
幸運を!
spc
が、曲げにより変位が短くなるためです。代わりに、横方向ベクトルを正規化し(ベクトルの長さでその成分を除算)、トランセクトの目的の半径で乗算します。
10 km以内の最高標高は、半径10 kmの円形で計算された近傍最大値であるため、軌跡に沿ってこの近傍最大グリッドのプロファイルを抽出するだけです。
これは、軌跡を備えた陰影起伏DEMです(下から上に黒い線が走っています):
この画像は約17 x 10キロメートルです。方法を説明するために、10 kmではなく1 kmの半径を選択しました。その1 kmのバッファーは黄色で示されています。
DEMの近傍の最大値は常に少し奇妙に見えます。これは、1つの最大値(おそらく丘の頂上)が10 kmをわずかに超え、異なる高度の別の最大値が10 km以内に来るポイントで値が急上昇する傾向があるためです。特に、周囲を支配する丘の頂上は、局所的な最大標高の点を中心とする値の完全な円に寄与します。
このマップでは暗いほど高くなります。
以下は、元のDEM(青)と近傍最大(赤)のプロファイルのプロットです。
これは、軌道を0.1 km離れた等間隔の点(南端から開始)に分割し、それらの点の標高を抽出し、結果のトリプル(開始点からの距離、標高、最大標高)の結合散布図を作成することによって計算されました。0.1 kmのポイント間隔は、バッファー半径よりも大幅に小さくなるように選択されましたが、計算を迅速に行うのに十分な大きさでした(瞬時でした)。
私は同じ問題を抱えてジェームズSのソリューションを試しましたが、GDALをフィオナで動作させることができませんでした。
それからQGIS 2.4でSAGAアルゴリズム「Cross Profiles」を発見し、私が望んでいた結果を得ました。あなたも探していると思います(下記参照)。
興味のある方は、numpyおよびosgeoライブラリのみを使用して垂直線を作成するJamesSコードの修正版をご覧ください。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()