Wie kann ich ein Höhenprofil für ein Geländeband erhalten?
Die höchste Erhebung innerhalb von 10 km (auf jeder Seite der definierten Linie) sollte berücksichtigt werden.
Ich hoffe meine Frage ist klar. Vielen Dank im Voraus.
Wie kann ich ein Höhenprofil für ein Geländeband erhalten?
Die höchste Erhebung innerhalb von 10 km (auf jeder Seite der definierten Linie) sollte berücksichtigt werden.
Ich hoffe meine Frage ist klar. Vielen Dank im Voraus.
Antworten:
Nach den Kommentaren folgt hier eine Version, die mit senkrechten Liniensegmenten arbeitet. Bitte mit Vorsicht verwenden, da ich es nicht gründlich getestet habe!
Diese Methode ist weitaus umständlicher als die Antwort von @ whuber - teils, weil ich kein sehr guter Programmierer bin, und teils, weil die Vektorverarbeitung ein bisschen kompliziert ist. Ich hoffe, es wird Ihnen zumindest den Einstieg erleichtern, wenn Sie senkrechte Liniensegmente benötigen.
Hier finden Sie die haben müssen Shapely , Fiona und Numpy Python - Pakete installiert (zusammen mit ihren Abhängigkeiten) dies auszuführen.
#-------------------------------------------------------------------------------
# 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()
Das folgende Bild zeigt ein Beispiel für die Ausgabe des Skripts. Sie fügen ein Shapefile ein, das Ihre Mittellinie darstellt, und geben die Länge der senkrechten Linien und deren Abstand an. Die Ausgabe ist ein neues Shapefile mit den roten Linien in diesem Bild, denen jeweils ein Attribut zugeordnet ist, das den Abstand vom Anfang des Profils angibt.
Wie @whuber in den Kommentaren gesagt hat, ist der Rest ziemlich einfach, sobald Sie zu diesem Zeitpunkt sind. Das folgende Bild zeigt ein weiteres Beispiel, bei dem die Ausgabe zu ArcMap hinzugefügt wurde.
Verwenden Sie das Werkzeug Feature zu Raster , um die senkrechten Linien in ein kategoriales Raster zu konvertieren. Legen Sie das Raster VALUE
als Dist
Feld im Ausgabe-Shapefile fest. Denken Sie auch daran das Werkzeug zu setzen , Environments
so dass Extent
, Cell size
und Snap raster
sind die gleichen wie für die zugrunde liegende DEM. Am Ende sollten Sie eine Rasterdarstellung Ihrer Linien erhalten, etwa so:
Konvertieren Sie dieses Raster in ein ganzzahliges Raster (mit dem Int- Tool oder dem Raster-Rechner) und verwenden Sie es als Eingabezone für das Zonal Statistics- Tool als Tabelle . Sie sollten eine Ausgabetabelle wie diese erhalten:
Das VALUE
Feld in dieser Tabelle gibt den Abstand vom Anfang der ursprünglichen Profillinie an. Die anderen Spalten geben verschiedene Statistiken (Maximum, Mittelwert usw.) für die Werte in jedem Transekt an. Sie können diese Tabelle verwenden, um Ihr Zusammenfassungsprofil zu zeichnen.
NB: Ein offensichtliches Problem bei dieser Methode ist, dass sich einige der Transect-Linien überlappen können, wenn Ihre ursprüngliche Linie sehr verwackelt ist. Die Tools für Zonenstatistiken in ArcGIS können keine überlappenden Zonen verarbeiten. In diesem Fall hat eine Ihrer Transaktionslinien Vorrang vor der anderen. Dies kann ein Problem für das sein, was Sie tun.
Viel Glück!
spc
ist, Biegungen jedoch die Verschiebungen verkürzen. Stattdessen sollten Sie den Querrichtungsvektor normalisieren (seine Komponenten durch die Länge des Vektors dividieren) und diesen dann mit dem gewünschten Radius des Transektors multiplizieren.
Die höchste Erhebung innerhalb von 10 km ist der mit einem kreisförmigen Radius von 10 km berechnete Nachbarschaftsmaximalwert. Extrahieren Sie daher einfach ein Profil dieses Nachbarschaftsmaximalrasters entlang der Flugbahn.
Hier ist ein hügeliger DEM mit einer Flugbahn (schwarze Linie von unten nach oben):
Dieses Bild ist ungefähr 17 mal 10 Kilometer groß. Ich habe einen Radius von nur 1 km anstelle von 10 km gewählt, um die Methode zu veranschaulichen. Der 1 km lange Puffer ist gelb umrandet.
Das Nachbarschaftsmaximum eines DEM sieht immer etwas seltsam aus, da es dazu neigt, an Punkten an Wert zu gewinnen, an denen ein Maximum (vielleicht eine Bergspitze) knapp über 10 km und ein anderes Maximum auf einer anderen Höhe knapp unter 10 km liegt . Insbesondere Hügel, die ihre Umgebung dominieren, tragen zu perfekten Wertekreisen bei, die auf dem Punkt der lokalen Maximalhöhe zentriert sind:
Dunkler ist höher auf dieser Karte.
Hier ist eine Darstellung der Profile des ursprünglichen DEM (blau) und des Nachbarschaftsmaximums (rot):
Es wurde berechnet, indem die Flugbahn in regelmäßig beabstandete Punkte mit einem Abstand von 0,1 km (beginnend an der Südspitze) aufgeteilt wurde, die Höhen an diesen Punkten extrahiert wurden und ein verbundenes Streudiagramm der resultierenden Dreifachwerte (Abstand vom Anfang, Höhe, maximale Höhe) erstellt wurde. Der Punktabstand von 0,1 km wurde so gewählt, dass er wesentlich kleiner als der Pufferradius ist, aber groß genug, um die Berechnung schnell durchzuführen (es war augenblicklich).
Ich hatte das gleiche Problem und versuchte es mit der Lösung von James S. Aber ich brachte die GDAL nicht dazu, mit Fiona zusammenzuarbeiten.
Dann habe ich den SAGA-Algorithmus "Cross Profiles" in QGIS 2.4 entdeckt und genau das Ergebnis erhalten, das ich wollte und von dem ich annehme, dass Sie auch danach suchen (siehe unten).
Für alle Interessierten ist hier eine modifizierte Version von JamesS-Code, mit der senkrechte Linien nur mit Numpy- und Osgeo-Bibliotheken erstellt werden. Dank JamesS hat mir seine Antwort heute sehr geholfen!
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()