Skip to content

Oblique frames in Python

An oblique frame is a grid whose x axis runs along a feature (a fault, a river, a range), so a long diagonal strip is analysed on a grid that holds the strip and not its bounding box. The concepts, and the same work with the command-line tools, are in Analysing along a feature, and the method at the end of that page. The Python calls run the same C++ routines as the command-line tools, so the same frame and settings give the same rasters.

A fault strip, start to finish

The feature is an approximate trace of the San Andreas Fault across the Carrizo Plain, California, saved as carrizo_fault.geojson (any vector format and CRS; the file is listed on the command-line page).

import lsdtt3

# 1. The frame of an 8 km wide strip along the fault, and the DEM fetched
#    straight into it (one resampling pass from the source tiles).
frame = lsdtt3.RotationFrame.from_feature("carrizo_fault.geojson", width=8000.0,
                                          grid_spacing=30.0)
print(frame)
# RotationFrame(line, azimuth -45.428948 deg, 2024 x 268 cells of 30.000000 m)
print(frame.target_epsg)          # 32611: products rotate back onto UTM 11N
frame.write("carrizo_rotation_frame.txt")

dem = lsdtt3.fetch_dem(frame=frame, grid_spacing=30.0)

# 2. Analysis in the frame: the fault runs along +x.
filled = lsdtt3.fill_pits(dem)
flow = lsdtt3.FlowInfo(filled)
network = flow.channel_network(flow.channel_scope(threshold_contributing_pixels=500))
links = network.link_bearings()          # bearing_deg clockwise from grid up
links_true = network.link_bearings(bearings_relative_to_true_north=True)

junctions = network.junctions()
outlets = junctions[junctions.stream_order == 2][["row", "col"]].to_numpy()
basins = flow.basins(outlets)
labels = basins.labels                   # basin_id + 1, 0 outside every basin
slope = lsdtt3.slope(filled)

# 3. Bring the products back onto UTM: IDs with "near", values with "bilinear".
labels_utm = lsdtt3.rotate_back(labels, frame, resampling="near")
slope_utm = lsdtt3.rotate_back(slope, frame)
print(labels_utm.shape)                  # (1648, 1597) cells of 30 m on EPSG:32611
labels_utm.write("carrizo_basins_utm.tif")

fetch_dem always returns a Raster; keep the RotationFrame to rotate products back. fetch_dem(fetch_from_linear_feature=True, fetch_feature_fname=..., linear_feature_fetch_width=...) builds the same frame from the command-line tool's keywords, but does not return it, so build it with RotationFrame.from_feature when you will rotate back.

Rotating a DEM you already have

dem = lsdtt3.Raster.read("windriver_DEM.tif")       # a UTM DEM

frame = lsdtt3.RotationFrame.from_feature("wind_river_range.geojson", source=dem)
rotated = lsdtt3.rotate_to_frame(dem, frame)         # resampling="cubic" by default

# ... analysis on `rotated` ...

back = lsdtt3.rotate_back(product, frame, resampling="near")

With source=, the frame is built as lsdtt-raster-preprocessing builds it:

  • on the DEM's own geographic datum (a NAD83 DEM gets a NAD83 frame);
  • at the DEM's cell size when it is projected in metres (grid_spacing=0, the default), or at the cell size reprojection would choose for a geographic DEM;
  • with the DEM's own grid as the rotate-back target, so products land on its pixels.

Without source=, the frame is for fetch_dem: on WGS84, at grid_spacing (30 m when 0), rotating back to the UTM (or polar stereographic beyond 75°) zone of the frame centre, or to target_epsg.

RotationFrame.from_valid_data(dem) frames the minimum-area rectangle of the DEM's valid cells instead of a feature file.

Frame files

RotationFrame.write and RotationFrame.read use the same <name>_rotation_frame.txt file as the command-line tools, so a frame made in Python can rotate products back with lsdtt-raster-preprocessing (run_rotate_back) and the other way round. If the file is lost, RotationFrame.from_raster(raster) rebuilds the frame from a frame raster's own CRS and warns that the rotate-back target falls back to the UTM zone of the frame centre on the lattice anchored at (0, 0).

Properties

Property Meaning
azimuth_degrees Axis azimuth at the centre, clockwise from true north, in (−90, 90]
centre_longitude, centre_latitude Frame centre, in the base geographic CRS
axis_length_metres, width_metres, end_buffer_metres Frame dimensions
cell_size, n_cols, n_rows, extent The frame grid; extent is (x_min, x_max, y_min, y_max) in frame metres
crs_wkt, proj_string, base_crs_wkt The frame CRS and its geographic CRS
frame_source line, two_points, polygon, data_footprint or crs
target_epsg, target_crs_wkt, target_cell_size, target_origin The rotate-back grid

frame.grid_convergence_degrees(x, y) gives the angle of true north clockwise from grid up at a point (frame metres): a grid bearing g there is the true bearing g − c.

Bearings

Every bound routine that returns a bearing, aspect or orientation takes bearings_relative_to_true_north (default False): aspect, polyfit_metrics, ChannelNetwork.link_bearings, ChannelNetwork.basin_com_bearings, BasinCollection.statistics (orientation_degrees), basin_surface_metrics, find_roads, find_road_flow_discrimination and detect_directional_periodicity. With False the values are clockwise from grid up, so in a frame 90° is along the feature; with True each value is converted with the grid convergence at its own location. Angles between two directions (junction_angles) are unchanged.