โปรไฟล์ระดับความสูง 10 กิโลเมตรแต่ละด้านของเส้น


15

ฉันจะได้รับโปรไฟล์ระดับความสูงสำหรับแถบภูมิประเทศได้อย่างไร

ควรคำนึงถึงระดับความสูงสูงสุดภายใน 10 กม. (ในแต่ละด้านของเส้นที่กำหนด)

ฉันหวังว่าคำถามของฉันจะชัดเจน ขอบคุณล่วงหน้า.


เส้นกำหนดโปรไฟล์ของคุณเป็นเส้นตรงอย่างง่ายหรือประกอบด้วยหลายส่วนที่มีมุม?
Jake

บรรทัดประกอบด้วยหลายส่วน แต่ทุกส่วนเป็นเส้นตรง :) ฉันหมายถึงไม่มีเส้นโค้ง
คาร่า

แค่ ... อย่างที่พวกเขาบอก .. การแทงบอล ... แต่คุณช่วยบัฟเฟอร์ด้วย 10km buffer ได้ไหม จากนั้นเลือกคุณสมบัติทั้งหมดภายในบัฟเฟอร์ ... จากนั้นเลือกค่าสูงสุด?
Ger

1
คุณสามารถให้ภาพของสิ่งที่คุณต้องการบรรลุ?
Alexandre Neto

@ Alex: ผลลัพธ์ที่ฉันต้องการคือกราฟระดับความสูงปกติ แต่ด้วยบัฟเฟอร์ 10km เพื่อให้ค่าสูงสุด 10km ในแต่ละด้านของเส้นทางที่เลือกจะแสดงบนกราฟ
คาร่า

คำตอบ:


14

ต่อจากความคิดเห็นต่อไปนี้เป็นเวอร์ชันที่ใช้งานได้กับส่วนของเส้นตั้งฉาก โปรดใช้ด้วยความระมัดระวังเนื่องจากฉันยังไม่ได้ทดสอบอย่างละเอียด!

วิธีนี้มีความหมายมากกว่า clunky มากกว่าคำตอบของ @ whuber - ส่วนหนึ่งเป็นเพราะฉันไม่ได้เป็นโปรแกรมเมอร์ที่ดีมากและส่วนหนึ่งเป็นเพราะการประมวลผลเวกเตอร์เป็นบิตของ faff ฉันหวังว่าอย่างน้อยคุณจะสามารถเริ่มต้นได้หากส่วนของเส้นตั้งฉากเป็นสิ่งที่คุณต้องการ

คุณจะต้องติดตั้งแพ็คเกจ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

ป้อนคำอธิบายรูปภาพที่นี่

ใช้เครื่องมือFeature to Rasterเพื่อแปลงเส้นตั้งฉากเป็นแรสเตอร์เชิงหมวดหมู่ ตั้งค่า raster VALUEเป็นDistฟิลด์ใน outputfilefile ยังจำได้ในการตั้งเครื่องมือEnvironmentsเพื่อให้Extent, Cell sizeและSnap rasterเป็นเช่นเดียวกับสำหรับ DEM พื้นฐานของคุณ คุณควรท้ายด้วยการแสดงภาพแรสเตอร์ของคุณดังนี้:

ป้อนคำอธิบายรูปภาพที่นี่

ในที่สุดแปลงแรสเตอร์นี้เป็นตารางจำนวนเต็ม (โดยใช้เครื่องมือIntหรือเครื่องคิดเลขแรสเตอร์) และใช้เป็นโซนป้อนข้อมูลสำหรับเครื่องมือZonal Statistics เป็นเครื่องมือตาราง คุณควรท้ายด้วยตารางผลลัพธ์เช่นนี้

ป้อนคำอธิบายรูปภาพที่นี่

VALUEฟิลด์ในตารางนี้จะช่วยให้ระยะทางจากจุดเริ่มต้นของบรรทัดรายละเอียดเดิม คอลัมน์อื่น ๆ ให้สถิติต่าง ๆ (สูงสุด, ค่าเฉลี่ย, ฯลฯ ) สำหรับค่าในแต่ละการตัดต่อ คุณสามารถใช้ตารางนี้เพื่อพล็อตโปรไฟล์สรุปของคุณ

หมายเหตุ:ปัญหาหนึ่งที่เห็นได้ชัดของวิธีนี้คือถ้าเส้นเดิมของคุณสั่นคลอนมากเส้นตัดบางเส้นอาจทับซ้อนกัน เครื่องมือสถิติโซนใน ArcGIS ไม่สามารถจัดการกับโซนที่ทับซ้อนกันดังนั้นเมื่อสิ่งนี้เกิดขึ้นหนึ่งบรรทัด transect ของคุณจะมีความสำคัญกว่าอีก สิ่งนี้อาจจะใช่หรือไม่ใช่ปัญหาสำหรับสิ่งที่คุณทำ

โชคดี!


3
+1 นั่นเป็นการเริ่มต้นที่ดีที่จะมีส่วนร่วมอย่างมาก! หากคุณดูตัวเลขที่สองอย่างใกล้ชิดคุณจะสังเกตว่ามีการตัดทอนที่สั้นกว่า: มันเป็นรอยตัดที่อยู่ใกล้กับโค้ง นี่เป็นเพราะอัลกอริทึมของคุณในการคำนวณการตัดภาพอย่างไม่ถูกต้องถือว่าการกระจัดของแต่ละส่วนจะเท่ากันspcแต่จะทำให้การเคลื่อนที่สั้นลง แต่คุณควรทำให้เวกเตอร์ทิศทางตามแนวนอนเป็นปกติ (หารส่วนประกอบด้วยความยาวของเวกเตอร์) แล้วคูณด้วยรัศมีที่ต้องการของ transect
whuber

คุณพูดถูกแล้ว - ขอบคุณสำหรับความคิดเห็น @whuber! คงหวังว่าตอนนี้ ...
Jamess

เรียนเจมส์ฉันจะพยายามทำเช่นนั้นขอบคุณมาก โซลูชันนี้เหมาะสมอย่างสมบูรณ์แบบ
คาร่า

11

ระดับความสูงสูงสุดภายใน 10 กม. คือค่าสูงสุดของพื้นที่ใกล้เคียงที่คำนวณด้วยรัศมีวงกลม 10 กม. ดังนั้นเพียงแยกโปรไฟล์ของกริดสูงสุดของพื้นที่ใกล้เคียงนี้ไปตามเส้นทาง

ตัวอย่าง

นี่คือ DEM hillshaded ที่มีวิถี (เส้นสีดำวิ่งจากล่างขึ้นบน):

DEM

ภาพนี้มีความยาวประมาณ 17 ถึง 10 กิโลเมตร ฉันเลือกรัศมีเพียง 1 กม. มากกว่า 10 กม. เพื่อแสดงวิธีการ บัฟเฟอร์ 1 กม. จะแสดงเป็นสีเหลือง

ย่านที่สูงที่สุดของ DEM มักจะดูแปลก ๆ อยู่เสมอเพราะมันมีแนวโน้มที่จะกระโดดตามจุดที่มีค่าสูงสุดที่หนึ่ง (บนเนินเขาอาจ) ตกหลุมเกิน 10 กม. และสูงสุดอีกระดับที่แตกต่างกันมาเพียงภายใน 10 กม. . โดยเฉพาะอย่างยิ่งยอดเขาที่อยู่เหนือสภาพแวดล้อมของพวกเขาจะช่วยให้เกิดวงกลมที่สมบูรณ์แบบของค่าที่มีศูนย์กลางอยู่ที่จุดที่ระดับความสูงสูงสุดในท้องถิ่น:

พื้นที่ใกล้เคียงสูงสุด

เข้มขึ้นบนแผนที่นี้

นี่คือพล็อตของโปรไฟล์ของ DEM ดั้งเดิม (สีน้ำเงิน) และพื้นที่ใกล้เคียงสูงสุด (สีแดง):

โปรไฟล์

มันคำนวณโดยการแบ่งวิถีเป็นจุดเว้นระยะห่างอย่างสม่ำเสมอที่ระยะทาง 0.1 กม. (เริ่มต้นที่ปลายด้านใต้) การแยกระดับความสูงที่จุดเหล่านั้น ระยะห่างระหว่างจุด 0.1 กม. ได้รับเลือกให้มีขนาดเล็กกว่ารัศมีบัฟเฟอร์อย่างมาก แต่ใหญ่พอที่จะทำให้การคำนวณเป็นไปอย่างรวดเร็ว (มันเป็นแบบทันที)


แต่นั่นไม่ถูกต้องทั้งหมดใช่มั้ย แทนที่จะใช้บัฟเฟอร์แบบวงกลมรอบ ๆ จุดแต่ละจุดไม่ควรใช้เส้นมุมฉากยาว 20 กม. ในการลองแรสเตอร์ต้นแบบ? อย่างน้อยนั่นคือวิธีที่ฉันจะตีความข้อกำหนดของ Kara ว่า "ค่าสูงสุดภายใน 10 กม. ในแต่ละด้านของบรรทัด" ที่ถูกนำมาพิจารณา
Jake

4
@ เจคฉันจะไม่พูดว่า "ไม่ถูกต้อง": คุณแค่เสนอการตีความทางเลือก "ในแต่ละด้านของ" เป็นคำที่คลุมเครือซึ่งสามารถใช้คุณสมบัติที่ดีกว่า ฉันสามารถเสนอแนวทางแก้ไขสำหรับการตีความเช่นเดียวกับคุณ วิธีการหนึ่งใช้ค่าสูงสุดเขต อย่างไรก็ตามมันมีความซับซ้อนและช้ากว่าในการประมวลผลมาก ทำไมเราไม่เห็นว่า OP คิดอย่างไรกับวิธีแก้ปัญหาง่ายๆนี้?
whuber

การเลือกคำที่ไม่เหมาะสมฉันไม่ควรใช้ "ถูกต้อง" - ขอโทษด้วย
Jake

1
เนื่องจากคุณรู้วิธีใช้เครื่องมือโปรไฟล์คุณเกือบเสร็จแล้ว QGIS มีส่วนต่อประสานกับ GRASS ซึ่งรวมถึงการปฏิบัติงานในพื้นที่ใกล้เคียง เพียงใช้การดำเนินการสูงสุดในละแวกใกล้เคียงโดยใช้r.neighborและระบุผลลัพธ์
whuber

1
@JamesS คุณไม่ต้องการกะแบบขนานคุณต้องการให้แต่ละ cross-profile ตั้งฉากกับเส้นกึ่งกลาง (วิธีการเลื่อนแบบขนานสามารถดำเนินการได้ตรงตามที่ฉันอธิบายที่นี่โดยใช้ย่านที่ยาวและผอมที่เหมาะสมสำหรับการคำนวณค่าสูงสุดของย่าน) ฉันค่อนข้างแน่ใจว่าคุณสามารถค้นหารหัสในเว็บไซต์นี้เพื่อสร้างชุดของส่วนของเส้นตั้งฉาก พร้อมรูปหลายเหลี่ยม; นั่นคือส่วนที่ยาก ทุกอย่างอื่นเป็นเพียงเรื่องของการแยกค่า DEM ตามกลุ่มเหล่านั้นและสรุปพวกเขา
whuber

6

ฉันมีปัญหาเดียวกันและลองวิธีแก้ปัญหาของ James S แต่ไม่สามารถทำให้ GDAL ทำงานร่วมกับ Fiona ได้

จากนั้นฉันก็ค้นพบอัลกอริทึม SAGA "Cross Profiles" ใน QGIS 2.4 และได้ผลลัพธ์ที่ฉันต้องการและฉันคิดว่าคุณกำลังมองหาด้วย (ดูด้านล่าง)

ป้อนคำอธิบายรูปภาพที่นี่


สวัสดีฉันเพิ่งเจอโพสต์นี้เมื่อไม่กี่ปีที่ผ่านมา ฉันกำลังเผชิญกับปัญหาเช่นเดียวกับเธรดเริ่มต้นและเป็นใหม่มากกับ (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 สคริปต์ด้วยคุณลักษณะ polyline เดียว แต่ผลลัพธ์ของฉันเป็นเพียงตารางแอตทริบิวต์ที่มีศูนย์จำนวนมากสำหรับค่า "M100" - ไม่มีคุณสมบัติที่แสดงในแผนที่ ไอเดีย?
davehughes87

ไม่เป็นไร - ตระหนักแล้วว่าสคริปต์ของคุณดูเหมือนจะคำนวณเส้นตั้งฉากที่จุดสิ้นสุดของแต่ละส่วนของโพลิไลน์ไม่ใช่ทุกเมตร "spc" ซึ่งหมายความว่าฉันหมดโพลีไลน์ที่จะทำงานก่อนที่จะถึง n_prof ในลูปและมีการสร้างค่า "nan"
davehughes87
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.