ฉันจะได้รับโปรไฟล์ระดับความสูงสำหรับแถบภูมิประเทศได้อย่างไร
ควรคำนึงถึงระดับความสูงสูงสุดภายใน 10 กม. (ในแต่ละด้านของเส้นที่กำหนด)
ฉันหวังว่าคำถามของฉันจะชัดเจน ขอบคุณล่วงหน้า.
ฉันจะได้รับโปรไฟล์ระดับความสูงสำหรับแถบภูมิประเทศได้อย่างไร
ควรคำนึงถึงระดับความสูงสูงสุดภายใน 10 กม. (ในแต่ละด้านของเส้นที่กำหนด)
ฉันหวังว่าคำถามของฉันจะชัดเจน ขอบคุณล่วงหน้า.
คำตอบ:
ต่อจากความคิดเห็นต่อไปนี้เป็นเวอร์ชันที่ใช้งานได้กับส่วนของเส้นตั้งฉาก โปรดใช้ด้วยความระมัดระวังเนื่องจากฉันยังไม่ได้ทดสอบอย่างละเอียด!
วิธีนี้มีความหมายมากกว่า 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 ของคุณจะมีความสำคัญกว่าอีก สิ่งนี้อาจจะใช่หรือไม่ใช่ปัญหาสำหรับสิ่งที่คุณทำ
โชคดี!
spc
แต่จะทำให้การเคลื่อนที่สั้นลง แต่คุณควรทำให้เวกเตอร์ทิศทางตามแนวนอนเป็นปกติ (หารส่วนประกอบด้วยความยาวของเวกเตอร์) แล้วคูณด้วยรัศมีที่ต้องการของ transect
ระดับความสูงสูงสุดภายใน 10 กม. คือค่าสูงสุดของพื้นที่ใกล้เคียงที่คำนวณด้วยรัศมีวงกลม 10 กม. ดังนั้นเพียงแยกโปรไฟล์ของกริดสูงสุดของพื้นที่ใกล้เคียงนี้ไปตามเส้นทาง
นี่คือ DEM hillshaded ที่มีวิถี (เส้นสีดำวิ่งจากล่างขึ้นบน):
ภาพนี้มีความยาวประมาณ 17 ถึง 10 กิโลเมตร ฉันเลือกรัศมีเพียง 1 กม. มากกว่า 10 กม. เพื่อแสดงวิธีการ บัฟเฟอร์ 1 กม. จะแสดงเป็นสีเหลือง
ย่านที่สูงที่สุดของ DEM มักจะดูแปลก ๆ อยู่เสมอเพราะมันมีแนวโน้มที่จะกระโดดตามจุดที่มีค่าสูงสุดที่หนึ่ง (บนเนินเขาอาจ) ตกหลุมเกิน 10 กม. และสูงสุดอีกระดับที่แตกต่างกันมาเพียงภายใน 10 กม. . โดยเฉพาะอย่างยิ่งยอดเขาที่อยู่เหนือสภาพแวดล้อมของพวกเขาจะช่วยให้เกิดวงกลมที่สมบูรณ์แบบของค่าที่มีศูนย์กลางอยู่ที่จุดที่ระดับความสูงสูงสุดในท้องถิ่น:
เข้มขึ้นบนแผนที่นี้
นี่คือพล็อตของโปรไฟล์ของ DEM ดั้งเดิม (สีน้ำเงิน) และพื้นที่ใกล้เคียงสูงสุด (สีแดง):
มันคำนวณโดยการแบ่งวิถีเป็นจุดเว้นระยะห่างอย่างสม่ำเสมอที่ระยะทาง 0.1 กม. (เริ่มต้นที่ปลายด้านใต้) การแยกระดับความสูงที่จุดเหล่านั้น ระยะห่างระหว่างจุด 0.1 กม. ได้รับเลือกให้มีขนาดเล็กกว่ารัศมีบัฟเฟอร์อย่างมาก แต่ใหญ่พอที่จะทำให้การคำนวณเป็นไปอย่างรวดเร็ว (มันเป็นแบบทันที)
ฉันมีปัญหาเดียวกันและลองวิธีแก้ปัญหาของ James S แต่ไม่สามารถทำให้ GDAL ทำงานร่วมกับ Fiona ได้
จากนั้นฉันก็ค้นพบอัลกอริทึม SAGA "Cross Profiles" ใน QGIS 2.4 และได้ผลลัพธ์ที่ฉันต้องการและฉันคิดว่าคุณกำลังมองหาด้วย (ดูด้านล่าง)
สำหรับใครที่มีความสนใจต่อไปนี้เป็นโค้ด 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()