Làm thế nào tôi có thể có được một hồ sơ độ cao cho một dải địa hình?
Độ cao cao nhất trong vòng 10 km (ở mỗi bên của đường xác định) nên được tính đến.
Tôi hy vọng câu hỏi của tôi là rõ ràng. Cảm ơn bạn rất nhiều trước.
Làm thế nào tôi có thể có được một hồ sơ độ cao cho một dải địa hình?
Độ cao cao nhất trong vòng 10 km (ở mỗi bên của đường xác định) nên được tính đến.
Tôi hy vọng câu hỏi của tôi là rõ ràng. Cảm ơn bạn rất nhiều trước.
Câu trả lời:
Theo ý kiến, đây là phiên bản hoạt động với các phân đoạn đường vuông góc. Vui lòng sử dụng một cách thận trọng vì tôi chưa kiểm tra kỹ lưỡng!
Phương pháp này khó hiểu hơn nhiều so với câu trả lời của @ whuber - một phần vì tôi không phải là một lập trình viên giỏi, và một phần vì quá trình xử lý véc tơ có một chút khó khăn. Tôi hy vọng nó ít nhất sẽ giúp bạn bắt đầu nếu các đoạn đường vuông góc là những gì bạn cần.
Bạn sẽ cần phải cài đặt các gói Shapely , Fiona và Numpy Python (cùng với các phụ thuộc của chúng) để chạy gói này.
#-------------------------------------------------------------------------------
# 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()
Hình ảnh dưới đây cho thấy một ví dụ về đầu ra từ tập lệnh. Bạn cung cấp một shapefile đại diện cho đường trung tâm của bạn và chỉ định độ dài của các đường vuông góc và khoảng cách của chúng. Đầu ra là một shapefile mới chứa các dòng màu đỏ trong hình ảnh này, mỗi cái có một thuộc tính liên quan chỉ định khoảng cách của nó từ khi bắt đầu cấu hình.
Như @whuber đã nói trong các bình luận, một khi bạn đã đến giai đoạn này, phần còn lại khá dễ dàng. Hình ảnh dưới đây cho thấy một ví dụ khác với đầu ra được thêm vào ArcMap.
Sử dụng công cụ Feature to Raster để chuyển đổi các đường vuông góc thành raster phân loại. Đặt raster VALUE
thành Dist
trường trong shapefile đầu ra. Cũng nên nhớ để thiết lập các công cụ Environments
để Extent
, Cell size
và Snap raster
cũng giống như cho DEM cơ bản của bạn. Bạn nên kết thúc với một đại diện raster của các dòng của bạn, đại loại như thế này:
Cuối cùng, chuyển đổi raster này thành lưới số nguyên (sử dụng công cụ Int hoặc máy tính raster) và sử dụng nó làm vùng đầu vào cho Công cụ thống kê Zonal dưới dạng công cụ Bảng . Bạn nên kết thúc với một bảng đầu ra như thế này:
Các VALUE
lĩnh vực trong bảng này cung cấp cho các khoảng cách từ sự bắt đầu của dòng hồ sơ gốc. Các cột khác cung cấp số liệu thống kê khác nhau (tối đa, trung bình, v.v.) cho các giá trị trong mỗi mặt cắt. Bạn có thể sử dụng bảng này để vẽ sơ lược tóm tắt của bạn.
Lưu ý: Một vấn đề rõ ràng với phương pháp này là, nếu dòng ban đầu của bạn rất gượng gạo, một số dòng cắt ngang có thể trùng nhau. Các công cụ thống kê khu vực trong ArcGIS không thể xử lý các vùng chồng lấp, vì vậy khi điều này xảy ra, một trong các đường cắt ngang của bạn sẽ được ưu tiên hơn các vùng khác. Điều này có thể hoặc không thể là một vấn đề cho những gì bạn đang làm.
Chúc may mắn!
spc
, nhưng uốn cong rút ngắn các chuyển vị. Thay vào đó, bạn nên bình thường hóa vectơ hướng ngang (chia các thành phần của nó cho chiều dài của vectơ) và sau đó nhân nó với bán kính mong muốn của mặt cắt.
Độ cao cao nhất trong vòng 10 km là giá trị tối đa của vùng lân cận được tính toán với bán kính 10 km hình tròn, vì vậy chỉ cần trích xuất một cấu hình của lưới tối đa của vùng lân cận này dọc theo quỹ đạo.
Đây là một DEM trên đồi với một quỹ đạo (đường màu đen chạy từ dưới lên trên):
Hình ảnh này là khoảng 17 x 10 km. Tôi đã chọn bán kính chỉ 1 km thay vì 10 km để minh họa phương pháp. Bộ đệm 1 km của nó được hiển thị viền màu vàng.
Điểm cực đại lân cận của DEM sẽ luôn trông hơi lạ, bởi vì nó sẽ có xu hướng tăng giá trị tại các điểm mà một điểm cực đại (đỉnh đồi, có lẽ) rơi xuống chỉ hơn 10 km và một mức tối đa khác ở độ cao khác nhau chỉ trong vòng 10 km . Cụ thể, các đỉnh đồi thống trị môi trường xung quanh sẽ đóng góp các vòng tròn hoàn hảo của các giá trị tập trung tại điểm độ cao tối đa cục bộ:
Darker cao hơn trên bản đồ này.
Dưới đây là sơ đồ cấu hình của DEM gốc (màu xanh) và mức tối đa của vùng lân cận (Màu đỏ):
Nó được tính toán bằng cách chia quỹ đạo thành các điểm cách đều nhau cách nhau 0,1 km (bắt đầu từ mũi phía nam), trích xuất độ cao tại các điểm đó và tạo ra một biểu đồ tán xạ của các bộ ba kết quả (khoảng cách từ điểm bắt đầu, độ cao, độ cao tối đa). Khoảng cách điểm 0,1 km được chọn là nhỏ hơn đáng kể so với bán kính bộ đệm nhưng đủ lớn để làm cho việc tính toán diễn ra nhanh chóng (đó là tức thời).
Tôi có cùng một vấn đề và đã thử giải pháp của James S, nhưng không thể khiến GDAL hợp tác với Fiona.
Sau đó, tôi phát hiện ra thuật toán SAGA "Hồ sơ chéo" trong QGIS 2.4 và nhận được chính xác kết quả tôi muốn và tôi cho rằng bạn cũng đang tìm kiếm (xem bên dưới).
Đối với bất kỳ ai quan tâm, đây là phiên bản sửa đổi của mã JamesS tạo các đường vuông góc chỉ sử dụng các thư viện numpy và osgeo. Cảm ơn JamesS, câu trả lời của anh ấy đã giúp tôi rất nhiều ngày hôm nay!
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()