Hồ sơ độ cao 10 km mỗi bên của một dòng


15

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à dòng xác định hồ sơ của bạn là một đường thẳng đơn giản, hoặc nó bao gồm một số phân đoạn với các góc?
Jake

Dòng bao gồm một số phân khúc. Nhưng tất cả các phân khúc là đường thẳng. :) Ý tôi là không có đường cong.
Kara

Chỉ là ... như họ nói..spitballing ... nhưng bạn có thể đệm dòng với bộ đệm 10km. sau đó chọn tất cả các tính năng trong bộ đệm ... sau đó chọn giá trị cao nhất?
Ger

1
Bạn có thể cung cấp một hình ảnh của những gì bạn muốn đạt được?
Alexandre Neto

@ Alex: kết quả tôi muốn là một đồ thị Độ cao thông thường. Nhưng với bộ đệm 10km để giá trị cao nhất 10km mỗi bên của đường dẫn đã chọn được hiển thị trên biểu đồ.
Kara

Câu trả lời:


14

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 , FionaNumpy 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.

Ví dụ đầu ra tập lệ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.

nhập mô tả hình ảnh ở đây

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 VALUEthành Disttrường trong shapefile đầu ra. Cũng nên nhớ để thiết lập các công cụ Environmentsđể Extent, Cell sizeSnap rastercũ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:

nhập mô tả hình ảnh ở đâ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:

nhập mô tả hình ảnh ở đây

Các VALUElĩ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!


3
+1 Đó là một khởi đầu tốt đẹp cho một đóng góp lớn! Nếu bạn nhìn kỹ vào hình thứ hai, bạn sẽ nhận thấy một số đường cắt ngắn hơn: chúng là những đường cắt ngang gần các khúc cua. Điều này là do thuật toán của bạn để tính toán các mặt cắt không chính xác giả định độ dịch chuyển của từng đoạn sẽ bằng nhau 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.
whuber

Bạn hoàn toàn đúng - cảm ơn phản hồi @whuber! Hy vọng đã được sửa chữa ngay bây giờ ...
JamesS

James thân mến, tôi sẽ cố gắng cảm ơn bạn rất nhiều. Giải pháp này phù hợp hoàn hảo.
Kara

11

Độ 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.

Thí dụ

Đâ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):

DEM

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ộ:

Vùng lân cận tối đa

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 đỏ):

Hồ sơ

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).


Điều đó không hoàn toàn chính xác, phải không? Thay vì bộ đệm tròn xung quanh mỗi điểm, không nên sử dụng đường thẳng trực giao có chiều dài 20 km để lấy mẫu raster bên dưới? Ít nhất đó là cách tôi giải thích yêu cầu của Kara về "giá trị cao nhất trong vòng 10km ở mỗi bên của dòng" được tính đến.
Jake

4
@jake Tôi sẽ không nói "không chính xác": bạn chỉ đưa ra một cách giải thích khác. "Trên mỗi mặt của" là một thuật ngữ mơ hồ có thể sử dụng trình độ tốt hơn. Tôi có thể đề xuất giải pháp cho các diễn giải như của bạn; một phương pháp sử dụng tối đa vùng. Tuy nhiên, nó phức tạp hơn và chậm hơn nhiều trong thực thi. Tại sao trước tiên chúng ta không thấy OP nghĩ gì về giải pháp đơn giản này?
whuber

Lựa chọn từ sai, tôi không nên sử dụng "chính xác" - xin lỗi về điều đó
Jake

1
Vì bạn biết cách sử dụng công cụ Hồ sơ, bạn gần như đã hoàn thành. QGIS có giao diện với GRASS bao gồm các hoạt động lân cận. Chỉ cần áp dụng các hoạt động tối đa của khu phố bằng cách sử dụng r.neighbor và hồ sơ kết quả của nó.
whuber

1
@JamesS Bạn không muốn thực hiện một ca làm việc song song, bạn muốn làm cho mỗi cấu hình chéo vuông góc với đường trung tâm. (Cách tiếp cận dịch chuyển song song có thể được thực hiện chính xác như tôi mô tả ở đây bằng cách sử dụng vùng lân cận dài và mỏng phù hợp để tính toán tối đa vùng lân cận.) Tôi khá chắc chắn rằng bạn có thể tìm thấy mã trên trang web này để xây dựng các phân đoạn đường vuông góc cách đều nhau dọc theo một đường đa tuyến; đó là phần khó khăn Mọi thứ khác chỉ là vấn đề trích xuất các giá trị DEM dọc theo các phân đoạn đó và tóm tắt chúng.
whuber

6

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).

nhập mô tả hình ảnh ở đây


Xin chào, tôi mới bắt gặp bài đăng này từ vài năm trước. Tôi đang đối mặt với cùng một vấn đề như bộ khởi động luồng, và, rất mới đối với (Q) GIS, tôi rất vui khi nhận được như hình ảnh trên. Làm thế nào để tôi làm việc với dữ liệu, mặc dù? Lớp Cross Profiles hiển thị độ cao cho từng điểm được lấy mẫu, nhưng tôi vui lòng yêu cầu trợ giúp trong 1) tìm độ cao tối đa cho mỗi đường chéo 2) tìm tọa độ cho giao lộ với đường dẫn ban đầu 3) liên kết độ cao tối đa từ 1 với tọa độ từ 2. Ai có thể giúp đỡ không? Cảm ơn rất nhiều trước! Mal
Cpt Reynold

6

Đố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()

Cảm ơn ket - Tôi đã thử điều này nhưng thật không may cho tôi. Tôi đã cung cấp cho tập lệnh một shapefile với một tính năng đa tuyến duy nhất nhưng đầu ra của tôi chỉ là bảng thuộc tính có nhiều số không cho giá trị "M100" - không có tính năng nào hiển thị trên bản đồ. Ý tưởng?
davehughes87 28/03/2015

Đừng bận tâm - bây giờ nhận ra rằng tập lệnh của bạn dường như tính toán các đường vuông góc ở KẾT THÚC của từng đoạn của đa tuyến, không phải mỗi mét "spc". Điều này có nghĩa là tôi đã hết polyline để làm việc trước khi đạt được n_prof trong vòng lặp và các giá trị "nan" đang được tạo ra.
davehughes87
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.