선의 각 측면에서 10km의 고도 프로파일


15

지형 대역에 대한 고도 프로파일을 얻으려면 어떻게해야합니까?

10km 이내 (정의 된 선의 각 측면에서) 가장 높은 고도를 고려해야합니다.

내 질문이 명확하기를 바랍니다. 대단히 감사합니다.


프로파일을 정의하는 선이 단순한 직선입니까, 아니면 모서리가있는 여러 세그먼트로 구성되어 있습니까?
Jake

선은 여러 세그먼트로 구성됩니다. 그러나 모든 세그먼트는 직선입니다. :) 곡선이 없다는 것을 의미합니다.
Kara

그냥 ... 스피트 볼링이라고 말하지만 10km 버퍼로 라인을 버퍼링 할 수 있습니까? 그런 다음 버퍼 내의 모든 기능을 선택한 다음 가장 높은 값을 선택합니까?
Ger

1
원하는 것을 이미지로 제공 할 수 있습니까?
Alexandre Neto 2013

@ Alex : 내가 원하는 결과는 일반적인 고도 그래프입니다. 그러나 선택된 경로의 각 측면에서 10km의 최고 값을 얻기 위해 10km 버퍼가 그래프에 표시됩니다.
Kara

답변:


14

주석에 이어 수직 선분과 함께 작동하는 버전이 있습니다. 철저히 테스트하지 않았으므로주의해서 사용하십시오!

이 방법은 @whuber의 답변보다 훨씬 더 복잡합니다. 부분적으로는 내가 훌륭한 프로그래머가 아니기 때문에, 벡터 처리가 약간의 문제이기 때문입니다. 수직 선분이 필요한 경우 적어도 시작하기를 바랍니다.

이것을 실행하려면 Shapely , FionaNumpy 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이며, 각 프로파일에는 프로파일 시작과의 거리를 지정하는 관련 속성이 있습니다.

스크립트 출력 예

@ whuber가 의견에서 말했듯 이이 단계에 도달하면 나머지는 상당히 쉽습니다. 아래 이미지는 ArcMap에 출력이 추가 된 다른 예를 보여줍니다.

여기에 이미지 설명을 입력하십시오

래스터에 피쳐 도구를 사용하여 수직선을 범주 형 래스터로 변환합니다. 래스터 VALUEDist출력 셰이프 파일 의 필드로 설정하십시오 . 또한 도구 설정을 기억 Environments하는 정도 Extent, Cell size그리고 Snap raster당신의 기본 DEM과 동일합니다. 다음과 같이 라인의 래스터 표현으로 끝나야합니다.

여기에 이미지 설명을 입력하십시오

마지막으로이 래스터를 정수 그리드 ( Int 도구 또는 래스터 계산기 사용)로 변환하고 영역 통계를 테이블로 도구 의 입력 영역으로 사용하십시오 . 다음과 같은 출력 테이블로 끝나야합니다.

여기에 이미지 설명을 입력하십시오

VALUE이 테이블 의 필드는 원래 프로파일 라인의 시작부터 거리를 제공합니다. 다른 열은 각 변환의 값에 대한 다양한 통계 (최대, 평균 등)를 제공합니다. 이 테이블을 사용하여 요약 프로파일을 플롯 할 수 있습니다.

주의 : 이 방법의 한 가지 명백한 문제는 원래 선이 매우 흔들리면 일부 가로 선이 겹칠 수 있다는 것입니다. ArcGIS의 구역 통계 도구는 겹치는 영역을 처리 할 수 ​​없으므로이 경우 가로선 중 하나가 다른 가로선보다 우선합니다. 이것은 당신이하고있는 일에 문제가 될 수도 있고 아닐 수도 있습니다.

행운을 빕니다!


3
+1 훌륭한 기여를 시작하기에 좋은 출발입니다! 두 번째 수치를 자세히 보면 약간 짧은 전달이 있음을 알 수 있습니다. 이것은 굽힘 근처에서 교차하는 것입니다. 이는 변환을 계산하는 알고리즘이 각 세그먼트의 변위가 동일하다고 가정 spc하지만 굽힘이 변위를 단축시키기 때문입니다. 대신 가로 방향 벡터를 정규화하고 (구성 요소를 벡터 길이로 나눔) 원하는 가로 길이를 곱해야합니다.
whuber

당신이 옳습니다-피드백 @ whuber에 감사드립니다! 희망적으로 지금
고쳐라

친애하는 제임스, 나는 그것을 대단히 감사합니다. 이 솔루션은 완벽하게 적합합니다.
Kara

11

10km 내에서 가장 높은 고도는 원형 10km 반경으로 계산 된 주변 최대 값이므로, 궤도를 따라이 주변 최대 그리드의 프로파일을 추출하십시오.

궤도가있는 언덕이있는 DEM은 다음과 같습니다 (검정색 선이 아래에서 위로)

DEM

이 이미지는 약 17 x 10 킬로미터입니다. 방법을 설명하기 위해 10km가 아닌 1km의 반경을 선택했습니다. 1km 버퍼는 노란색으로 표시되어 있습니다.

DEM의 이웃 최대 값은 항상 조금 이상하게 보일 것입니다. 왜냐하면 하나의 최대 값 (언덕 정상)이 10km를 약간 넘어서고 다른 높이의 다른 최대 값이 10km 이내에있는 지점에서 값이 급상승하는 경향이 있기 때문입니다. . 특히, 주변 환경을 지배하는 언덕은 지역 최대 고도 지점을 중심으로 완벽한 가치의 원을 제공합니다.

최대 이웃

이지도에서 더 어둡습니다.

다음은 원래 DEM (파란색)과 인접 최대 값 (빨간색)의 프로파일을 나타냅니다.

프로필

궤적을 0.1km 간격으로 (남쪽 끝에서 시작하여) 일정한 간격으로 점으로 나누고, 그 점에서 표고를 추출하고 결과 삼중점의 결합 산점도 (처음부터의 거리, 표고, 최대 표고)를 만들어 계산했습니다. 0.1km의 포인트 간격은 버퍼 반경보다 실질적으로 작지만 계산을 빠르게 진행할 수있을만큼 충분히 크게 선택되었습니다 (즉시).


그래도 완전히 정확하지는 않습니까? 각 점 주위의 원형 버퍼 대신 기본 래스터를 샘플링하는 데 길이 20km의 직교 선을 사용해서는 안됩니까? 적어도 그것이 카라의 요구 사항을 "라인의 각 측면에서 10km 이내의 최고 값"으로 해석하는 방법을 고려한 것입니다.
Jake

4
@jake 나는 "정확하지 않다"고 말하지 않을 것입니다 : 당신은 단지 다른 해석을 제공 할뿐입니다. "양쪽에"는 더 나은 자격을 사용할 수있는 모호한 용어입니다. 나는 당신과 같은 해석을위한 해결책을 제안 할 수 있습니다. 한 가지 방법은 구역 최대 값을 사용합니다. 그러나 실행이 더 복잡하고 느려집니다. OP가이 간단한 솔루션에 대해 어떻게 생각하는지 먼저 보지 않겠습니까?
whuber

잘못된 단어 선택, "정확한"을 사용해서는 안됩니다. 죄송합니다.
Jake

1
프로필 도구를 사용하는 방법을 알고 있으므로 거의 끝났습니다. QGIS에는 이웃 작업을 포함하는 GRASS에 대한 인터페이스가 있습니다. r.neighbors를 사용하여 이웃 최대 작업을 적용 하고 결과를 프로파일하십시오.
whuber

1
@JamesS 평행 이동을 원하지 않고 각 교차 프로파일을 중심선에 수직으로 만들고 싶습니다. (근접 최대 계산에 적절한 길고 마른 이웃을 사용하여 여기에 설명 된대로 평행 이동 접근 방식을 정확하게 구현할 수 있습니다.)이 사이트에서 동일한 간격의 수직선 세그먼트 세트를 구성하는 코드를 찾을 수 있다고 확신합니다 폴리 라인을 따라; 그것은 어려운 부분입니다. 다른 모든 것은 해당 세그먼트를 따라 DEM 값을 추출하고 요약하는 것입니다.
whuber

6

나는 같은 문제가 있었고 James S의 솔루션을 시도했지만 GDAL이 Fiona와 함께 작동하도록 할 수 없었습니다.

그런 다음 QGIS 2.4에서 SAGA 알고리즘 "Cross Profiles"를 발견하고 원하는 결과를 얻었으며 사용자도 찾고 있다고 가정합니다 (아래 참조).

여기에 이미지 설명을 입력하십시오


안녕하세요, 몇 년 전부터이 게시물을 보았습니다. 나는 스레드 스타터와 같은 문제에 직면하고 있으며 (Q) GIS를 처음 접했을 때 위의 그림까지 도달하게되어 기쁩니다. 그래도 데이터 작업은 어떻게합니까? Cross Profiles 레이어는 샘플링 된 각 점의 표고를 보여 주지만 1) 각 십자선의 최대 표고 찾기 2) 원래 경로와의 교점 좌표 찾기 3) 1에서 최대 표고 연관 누구든지 도울 수 있습니까? 미리 감사드립니다! 말
Cpt 레이놀즈

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"값에 대해 0이 많은 속성 테이블 일 뿐이며 맵에 표시되는 피처는 없습니다. 아이디어?
davehughes87

신경 쓰지 마십시오-스크립트가 모든 "spc"미터가 아니라 폴리 라인의 각 세그먼트의 ENDS에서 수직선을 계산하는 것 같습니다. 이것은 루프에서 n_prof에 도달하고 "nan"값이 생성되기 전에 작업 할 폴리 라인이 부족했음을 의미합니다.
davehughes87
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.