Profil ketinggian 10 km di setiap sisi garis

15

Bagaimana saya bisa mendapatkan profil ketinggian untuk pita medan?

Ketinggian tertinggi dalam 10 km (di setiap sisi dari garis yang ditentukan) harus diperhitungkan.

Saya harap pertanyaan saya jelas. Terima kasih banyak sebelumnya.

Kara
sumber
Apakah garis mendefinisikan profil Anda garis lurus sederhana, atau apakah itu terdiri dari beberapa segmen dengan sudut?
Jake
Garis terdiri dari beberapa segmen. Tetapi semua segmen adalah garis lurus. :) Maksud saya tidak ada kurva.
Kara
Hanya ... seperti yang mereka katakan ... bermain bola ... tapi bisakah Anda buffer garis dengan buffer 10 km. lalu pilih semua fitur dalam buffer ... lalu pilih nilai tertinggi?
Ger
1
Bisakah Anda memberikan gambar dari apa yang ingin Anda capai?
Alexandre Neto
@ Alex: hasil yang saya inginkan adalah Grafik Elevasi biasa. Tetapi dengan buffer 10km agar nilai tertinggi 10km setiap sisi dari jalur yang dipilih ditampilkan pada grafik.
Kara

Jawaban:

14

Sebagai lanjutan dari komentar, inilah versi yang bekerja dengan segmen garis tegak lurus. Silakan gunakan dengan hati-hati karena saya belum mengujinya secara menyeluruh!

Metode ini jauh lebih kikuk daripada jawaban @ whuber - sebagian karena saya bukan programmer yang sangat baik, dan sebagian karena pemrosesan vektor sedikit faff. Saya harap ini setidaknya akan membantu Anda jika segmen garis tegak lurus adalah yang Anda butuhkan.

Anda harus memiliki Shapely , Fiona dan Numpy Python paket diinstal (bersama dengan dependensi mereka) untuk menjalankan ini.

#-------------------------------------------------------------------------------
# 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()

Gambar di bawah ini menunjukkan contoh output dari skrip. Anda memberi makan di shapefile yang mewakili garis tengah Anda, dan menentukan panjang garis tegak lurus dan jaraknya. Outputnya adalah shapefile baru yang berisi garis merah pada gambar ini, yang masing-masing memiliki atribut terkait yang menentukan jaraknya dari awal profil.

Keluaran skrip contoh

Seperti yang dikatakan @whuber dalam komentar, begitu Anda sampai pada tahap ini sisanya cukup mudah. Gambar di bawah ini menunjukkan contoh lain dengan output ditambahkan ke ArcMap.

masukkan deskripsi gambar di sini

Gunakan alat Feature to Raster untuk mengubah garis tegak lurus menjadi raster kategorikal. Atur raster VALUEuntuk menjadi Distbidang dalam shapefile keluaran. Juga ingat untuk mengatur alat Environmentsitu Extent, Cell sizedan Snap rastersama dengan DEM Anda yang mendasarinya. Anda harus berakhir dengan representasi raster dari baris Anda, sesuatu seperti ini:

masukkan deskripsi gambar di sini

Terakhir, konversikan raster ini ke kisi integer (menggunakan alat Int atau kalkulator raster), dan gunakan sebagai zona input untuk Statistik Zonal sebagai alat Tabel . Anda harus berakhir dengan tabel output seperti ini:

masukkan deskripsi gambar di sini

The VALUElapangan di tabel ini memberikan jarak dari awal garis profil asli. Kolom lain memberikan berbagai statistik (maksimum, rata-rata dll.) Untuk nilai-nilai dalam setiap transek. Anda dapat menggunakan tabel ini untuk memplot profil ringkasan Anda.

NB: Satu masalah yang jelas dengan metode ini adalah bahwa, jika jalur asli Anda sangat goyah, beberapa garis transek mungkin tumpang tindih. Alat statistik zona di ArcGIS tidak dapat menangani zona yang tumpang tindih, jadi ketika ini terjadi salah satu jalur transek Anda akan diutamakan daripada yang lain. Ini mungkin atau mungkin bukan masalah bagi apa yang Anda lakukan.

Semoga berhasil!

JamesS
sumber
3
+1 Itu awal yang bagus untuk kontribusi yang bagus! Jika Anda perhatikan gambar kedua dengan seksama, Anda akan melihat beberapa transek yang lebih pendek: transek yang melintasi tikungan. Ini karena algoritme Anda untuk menghitung transek yang salah mengasumsikan bahwa perpindahan setiap segmen akan sama spc, tetapi membengkokkan pemangkasan yang lebih pendek. Sebagai gantinya, Anda harus menormalkan vektor arah transversal (membaginya dengan panjang vektor) dan kemudian mengalikannya dengan jari-jari transek yang diinginkan.
whuber
Anda benar - terima kasih atas umpan baliknya @whuber! Semoga diperbaiki sekarang ...
JamesS
James yang terkasih, saya akan coba terima kasih banyak. Solusi ini sangat cocok.
Kara
11

Ketinggian tertinggi dalam 10 km adalah nilai maksimum lingkungan dihitung dengan radius 10 km melingkar, jadi cukup ekstrak profil grid maksimum lingkungan ini di sepanjang lintasan.

Contoh

Berikut adalah DEM hillshaded dengan lintasan (garis hitam berjalan dari bawah ke atas):

DEM

Gambar ini sekitar 17 kali 10 kilometer. Saya memilih radius hanya 1 km daripada 10 km untuk menggambarkan metode ini. Buffer 1 km ditunjukkan dengan warna kuning.

Maksimum lingkungan DEM akan selalu terlihat sedikit aneh, karena akan cenderung melonjak nilainya di titik-titik di mana satu maksimum (puncak bukit, mungkin) jatuh tepat di atas 10 km dan maksimum lain di ketinggian yang berbeda hanya berjarak 10 km . Secara khusus, puncak bukit yang mendominasi lingkungan mereka akan berkontribusi lingkaran nilai sempurna yang berpusat pada titik ketinggian maksimum lokal:

Lingkungan maks

Gelap lebih tinggi di peta ini.

Berikut adalah plot profil DEM asli (biru) dan maksimum lingkungan (Merah):

Profil

Itu dihitung dengan membagi lintasan menjadi titik-titik yang berjarak secara teratur pada jarak 0,1 km (mulai dari ujung selatan), mengekstraksi ketinggian pada titik-titik itu, dan membuat sebaran gabungan dari tripel yang dihasilkan (jarak dari awal, ketinggian, ketinggian maksimum). Jarak titik 0,1 km dipilih untuk secara substansial lebih kecil dari jari-jari penyangga tetapi cukup besar untuk membuat perhitungan berjalan cepat (itu instan).

whuber
sumber
Tapi itu tidak sepenuhnya akurat, kan? Alih-alih buffer melingkar di sekitar setiap titik, bukankah seharusnya garis ortogonal dengan panjang 20 km digunakan untuk sampel raster yang mendasarinya? Setidaknya begitulah cara saya mengartikan persyaratan Kara tentang "nilai tertinggi dalam jarak 10 km di setiap sisi jalur" dipertimbangkan.
Jake
4
@ membuat saya tidak akan mengatakan "tidak akurat": Anda hanya menawarkan interpretasi alternatif. "Di setiap sisi" adalah istilah yang tidak jelas yang dapat menggunakan kualifikasi yang lebih baik. Saya dapat mengusulkan solusi untuk interpretasi seperti milik Anda; satu metode menggunakan zona maksimum. Namun, ini lebih rumit dan lebih lambat dalam eksekusi. Mengapa kita tidak pertama-tama melihat pendapat OP tentang solusi sederhana ini?
whuber
Pilihan kata yang salah, saya seharusnya tidak menggunakan "akurat" - maaf tentang itu
Jake
1
Karena Anda tahu cara menggunakan alat Profil, Anda hampir selesai. QGIS memiliki antarmuka ke GRASS yang mencakup operasi lingkungan. Hanya menerapkan operasi maksimum lingkungan menggunakan r.neighbors dan profil hasilnya.
whuber
1
@JamesS Anda tidak ingin melakukan pergeseran paralel, Anda ingin membuat setiap profil melintang tegak lurus dengan garis tengah. (Pendekatan pergeseran paralel dapat diimplementasikan persis seperti yang saya jelaskan di sini dengan menggunakan lingkungan panjang dan kurus yang sesuai untuk perhitungan max lingkungan.) Saya cukup yakin Anda dapat menemukan kode di situs ini untuk membangun rangkaian segmen garis tegak lurus yang berjarak sama sepanjang polyline; itu bagian yang sulit. Yang lainnya hanyalah masalah mengekstraksi nilai DEM di sepanjang segmen tersebut dan merangkumnya.
whuber
6

Saya memiliki masalah yang sama dan mencoba solusi James S, tetapi tidak dapat membuat GDAL bekerja dengan Fiona.

Kemudian saya menemukan algoritma SAGA "Cross Profiles" di QGIS 2.4, dan mendapatkan hasil yang saya inginkan dan saya kira Anda juga sedang mencari (lihat di bawah).

masukkan deskripsi gambar di sini

Don Richardo
sumber
Hai, saya baru saja menemukan posting ini dari beberapa tahun yang lalu. Saya menghadapi masalah yang sama dengan thread starter, dan, karena masih sangat baru untuk (Q) GIS, saya agak senang mendapatkan sejauh gambar di atas. Bagaimana cara saya bekerja dengan data? Lapisan Profil Silang menunjukkan ketinggian untuk setiap titik sampel, tetapi saya akan dengan senang hati meminta bantuan dalam 1) menemukan ketinggian maksimum untuk setiap garis silang 2) menemukan koordinat untuk persimpangan dengan jalur asli 3) mengaitkan ketinggian maksimum dari 1 dengan koordinat dari 2. Adakah yang bisa membantu? Terima kasih banyak sebelumnya! Mal
Cpt Reynolds
6

Bagi siapa pun yang tertarik, berikut ini adalah versi modifikasi dari kode JamesS yang membuat garis tegak lurus menggunakan pustaka numpy dan osgeo saja. Berkat JamesS, jawabannya sangat membantu saya hari ini!

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
sumber
Terima kasih ket - Saya sudah mencoba ini, tetapi sayangnya tidak berfungsi sepenuhnya untuk saya. Saya telah memberi skrip shapefile dengan fitur polyline tunggal tetapi output saya hanyalah tabel atribut dengan banyak nol untuk nilai "M100" - tidak ada fitur yang ditampilkan di peta. Ide ide?
davehughes87
Sudahlah - sadari sekarang bahwa skrip Anda tampaknya menghitung garis tegak lurus pada ENDS dari setiap segmen polyline, bukan setiap "spc" meter. Ini berarti bahwa saya kehabisan polyline untuk bekerja dengan sebelum n_prof tercapai dalam loop dan nilai "nan" sedang diproduksi.
davehughes87