標高プロファイル、ラインの両側10 km


15

地形帯の標高プロファイルを取得するにはどうすればよいですか?

(定義された線の両側の)10 km以内の最高標高を考慮する必要があります。

私の質問が明確であることを願っています。事前にどうもありがとうございました。


プロファイルを定義する線は単純な直線ですか、それとも角のある複数のセグメントで構成されていますか?
ジェイク

ラインはいくつかのセグメントで構成されています。ただし、すべてのセグメントは直線です。:)曲線がないことを意味します。
カラ

ただ...彼らが言うように..スピットボール...しかし、10kmのバッファでラインをバッファリングできますか?次に、バッファ内のすべての機能を選択します...次に最大値を選択しますか?
ゲル

1
あなたが達成したいもののイメージを提供できますか?
アレクサンドル・ネトー

@ Alex:私が望む結果は、通常の高度グラフです。ただし、選択したパスの両側で最高値が10 kmになるように10 kmのバッファーを使用します。
カラ

回答:


14

コメントに続いて、垂直線分で機能するバージョンがあります。私は徹底的にテストしていないので注意して使用してください!

この方法は、@ whuberの答えよりもはるかに不格好です-一部は私が非常に優秀なプログラマーではないためと、一部はベクトル処理が少し手間のかかるためです。少なくとも垂直線分が必要な場合は、少なくとも開始できることを願っています。

これを実行するには、ShapelyFiona、および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に出力が追加された別の例を示しています。

ここに画像の説明を入力してください

使用ラスターに機能をカテゴリ別ラスタに垂線を変換するツール。ラスターVALUEDist出力シェープファイルのフィールドに設定します。また、ツールを設定することを忘れないEnvironmentsようにExtentCell sizeそしてSnap raster、あなたの根本的なDEMと同じです。最終的に、次のような行のラスタ表現になります。

ここに画像の説明を入力してください

最後に、このラスターを(Intツールまたはラスター計算機を使用して)整数グリッドに変換し、Zonal Statistics as Tableツールの入力ゾーンとして使用します。次のような出力テーブルが作成されるはずです。

ここに画像の説明を入力してください

VALUEこの表のフィールドは、元のプロファイルラインの始点からの距離を示します。他の列は、各トランセクトの値のさまざまな統計(最大、平均など)を提供します。この表を使用して、要約プロファイルをプロットできます。

NB:この方法の明らかな問題の1つは、元のラインが非常に不安定な場合、トランセクトラインの一部がオーバーラップする可能性があることです。ArcGISのゾーン統計ツールはオーバーラップゾーンを処理できないため、これが発生すると、トランセクトラインの1つが他のラインよりも優先されます。これは、あなたがしていることに問題があるかもしれませんし、そうでないかもしれません。

幸運を!


3
+1それは素晴らしい貢献の良いスタートです!2番目の図をよく見ると、いくつかの短いトランセクトに気付くでしょう。それらは曲がり近くで交差しているトランセクトです。これは、トランセクトを計算するアルゴリズムが、各セグメントの変位が等しいと誤って想定しているspcが、曲げにより変位が短くなるためです。代わりに、横方向ベクトルを正規化し(ベクトルの長さでその成分を除算)、トランセクトの目的の半径で乗算します。
whuber

そのとおりです-フィードバック@whuberに感謝します!うまくいけば今すぐ修正
...-JamesS

親愛なるジェームス、ありがとうございます。このソリューションは完全に適合します。
カラ

11

10 km以内の最高標高は、半径10 kmの円形で計算された近傍最大値であるため、軌跡に沿ってこの近傍最大グリッドのプロファイルを抽出するだけです。

これは、軌跡を備えた陰影起伏DEMです(下から上に黒い線が走っています):

DEM

この画像は約17 x 10キロメートルです。方法を説明するために、10 kmではなく1 kmの半径を選択しました。その1 kmのバッファーは黄色で示されています。

DEMの近傍の最大値は常に少し奇妙に見えます。これは、1つの最大値(おそらく丘の頂上)が10 kmをわずかに超え、異なる高度の別の最大値が10 km以内に来るポイントで値が急上昇する傾向があるためです。特に、周囲を支配する丘の頂上は、局所的な最大標高の点を中心とする値の完全な円に寄与します。

近隣最大

このマップでは暗いほど高くなります。

以下は、元のDEM(青)と近傍最大(赤)のプロファイルのプロットです。

プロフィール

これは、軌道を0.1 km離れた等間隔の点(南端から開始)に分割し、それらの点の標高を抽出し、結果のトリプル(開始点からの距離、標高、最大標高)の結合散布図を作成することによって計算されました。0.1 kmのポイント間隔は、バッファー半径よりも大幅に小さくなるように選択されましたが、計算を迅速に行うのに十分な大きさでした(瞬時でした)。


しかし、それは完全に正確ではありませんよね?各ポイントの周りの円形バッファーの代わりに、基礎となるラスターをサンプリングするために長さ20 kmの直交線を使用すべきではありませんか?少なくともそれは、カラの「線の両側にある10km以内の最高値」の要件を考慮して解釈する方法です。
ジェイク

4
@jake私は「不正確」とは言いません。あなたは単に別の解釈を提供するだけです。「それぞれの側で」は、より良い資格を使用できる曖昧な用語です。あなたのような解釈の解決策を提案できます。1つの方法では、ゾーンの最大値を使用します。ただし、それはより複雑で実行がはるかに遅くなります。OPがこの単純なソリューションについてどう考えているかを最初に見てみませんか?
whuber

悪い言葉の選択、私は「正確」に使用してはならない-そのことについて申し訳ありません
ジェイク

1
プロファイルツールの使用方法を知っているので、ほぼ完了です。QGISには、近傍操作を含むGRASSへのインターフェースがあります。r.neighborsを使用して近傍最大演算を適用し、その結果をプロファイルします。
whuber

1
@JamesS平行移動を行いたくない、各クロスプロファイルを中心線に垂直にしたい。(並列シフトアプローチは、近隣最大値の計算に適切な長くて細い近隣を使用することで、ここで説明するとおりに実装できます。)等間隔の垂直線セグメントのセットを構築するためのコードをこのサイトで見つけることができます。ポリラインに沿って; それは難しい部分です。他のすべては、それらのセグメントに沿ってDEM値を抽出し、それらを要約するだけの問題です。
whuber

6

私は同じ問題を抱えてジェームズSのソリューションを試しましたが、GDALをフィオナで動作させることができませんでした。

それからQGIS 2.4でSAGAアルゴリズム「Cross Profiles」を発見し、私が望んでいた結果を得ました。あなたも探していると思います(下記参照)。

ここに画像の説明を入力してください


こんにちは、私は数年前からこの投稿に出会いました。私はスレッドスターターと同じ問題に直面しています。また、(Q)GISが非常に新しいので、上の写真まで得られたことを少し嬉しく思います。しかし、どのようにデータを操作しますか?Cross Profilesレイヤーには、各サンプリングポイントの標高が表示されますが、1)各クロスラインの最大標高の検索2)元のパスとの交差点の座標の検索3)1からの最大標高の関連付け2からの座標で。誰か助けてください。事前に感謝します!Mal
Cpt Reynolds

6

興味のある方は、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()

おかげで-私はこれを試してみましたが、残念ながら完全に機能していません。スクリプトには単一のポリラインフィーチャを含むシェープファイルを指定しましたが、出力は「M100」値にゼロが多数含まれる属性テーブルに過ぎません。マップにフィーチャが表示されません。アイデア?
-davehughes87

気にしないでください-スクリプトがすべての「spc」メートルではなく、ポリラインの各セグメントのENDSで垂直線を計算しているように見えることに気付きました。これは、ループ内でn_profに到達して「nan」値が生成される前に、ポリラインが不足していたことを意味します。
-davehughes87
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.