6

I would like to generate multiple line segments of a fixed length, a random bearing, and both segment endpoints within the polygon.See example image It's not clear from the example, but it's fine if the segments intersect each other or span the indented features as long as both endpoints are in the polygon.

I also want the attributes table to include lat/lon for each segment endpoint and the azimuth between the endpoints.

Is this possible?

2
  • 4
    Good question. This is certainly possible. However, could you please edit your question to include what software you're using?
    – Fezter
    May 20 '16 at 0:08
  • 1
    Also, do you have python experience?
    – Fezter
    May 20 '16 at 1:29
3

Here is a Python ArcGIS arcpy solution. I've optimized this for speed by using in_memory workspace wherever possible--it is quite fast. This is the general workflow:

  1. Negative Buffer the study area by the transect length.
  2. Create Random Points inside the negative buffer (study area). These will be the starting points for the transects. Note that you can offset the points n distance from each other if you choose.
  3. Add fields to random points attributes and use a da UpdateCursor to populate rows with x, y, distance, and bearing values.
  4. Run Bearing Distance to Line to generate the transects.

enter image description here


import arcpy, os, random

arcpy.env.overwriteOutput = 1

outws = r"C:\path\to\your\filegeodatabase.gdb" # Where the output feature class and table will go. This assumes a file geodatabase
polygon = r"C:\path\to\your\filegeodatabase.gdb\utm15\polygon" # The study area polygon (Note this is in a feature dataset)
transect_distance = 50 # The transect distance
n_lines = 20 # How many transects

# Create random points offset from study area boundary by X distance
arcpy.Buffer_analysis(polygon, "in_memory/buffer", -transect_distance) # negative buffer study area to get correct offset
arcpy.CreateRandomPoints_management("in_memory", "rand_points", "in_memory/buffer", "", n_lines)

# Add fields x, y, distance, and bearing
arcpy.AddField_management("in_memory/rand_points", "x", "DOUBLE")
arcpy.AddField_management("in_memory/rand_points", "y", "DOUBLE")
arcpy.AddField_management("in_memory/rand_points", "distance", "FLOAT")
arcpy.AddField_management("in_memory/rand_points", "bearing", "FLOAT")

# Run cursor to update attribute table with pertinent data for bearing distance tool
with arcpy.da.UpdateCursor("in_memory/rand_points", ["SHAPE@XY", "x", "y", "distance", "bearing"]) as cursor:
    for row in cursor:
        row[1] = row[0][0]
        row[2] = row[0][1]
        row[3] = transect_distance
        row[4] = random.randint(1,360)
        cursor.updateRow(row)

# Create a table to feed to Bearing Distance to line tool
arcpy.TableToTable_conversion("in_memory/rand_points",outws,"out_table")

# Generate the transects
arcpy.BearingDistanceToLine_management (os.path.join(outws, "out_table"), os.path.join(outws, "transects"), x_field = 'x', y_field = 'y', distance_field = 'distance', bearing_field = 'bearing', spatial_reference = "in_memory/rand_points")

# Clean up
arcpy.Delete_management(os.path.join(outws, "out_table"))

print "Processing complete."
0
1

Here's a rough stab with R.

library(sp)
library(raster)
## roughly digitized analog of your map (with raster::drawPoly)

d <- structure(list(x = c(0.08394638623612, 0.0758558910509947, 0.197213318827875, 
0.209349061605562, 0.268005151697721, 0.280140894475409, 0.08394638623612, 
0.08394638623612, 0.112263119384059, 0.120353614569184, 0.112263119384059, 
0.100127376606371, 0.090014257624964, 0.0920368814212453, 0.41767931262254, 
0.454086540955604, 0.454086540955604, 0.462177036140729, 0.504652135862637, 
0.520833126232887, 0.545104611788263, 0.551172483177108, 0.553195106973389, 
0.852543428823026, 0.959742490025936, 0.963787737618499, 0.907154271322622, 
0.909176895118903, 0.937493628266842, 0.955697242433374, 0.941538875859404, 
0.931425756877998, 0.927380509285435, 0.911199518915184, 0.899063776137496, 
0.0859690100324013, 0.08394638623612), y = c(0.0984799434209303, 
0.184633290921423, 0.173297324145043, 0.218641191250565, 0.218641191250565, 
0.352405599211856, 0.356939985922409, 0.522445100857566, 0.529246680923394, 
0.547384227765603, 0.567788967963088, 0.579124934739469, 0.585926514805297, 
0.789973916780149, 0.862524104148985, 0.837584977240947, 0.801309883556529, 
0.767301983227388, 0.751431629740455, 0.762767596516835, 0.776370756648492, 
0.833050590530395, 0.867058490859537, 0.871592877570089, 0.864791297504261, 
0.497505973949529, 0.474834040396767, 0.440826140067625, 0.418154206514864, 
0.418154206514864, 0.313863312172162, 0.273053831777192, 0.216373997895289, 
0.184633290921423, 0.141556617171177, 0.0962127500656542, 0.0984799434209303
)), .Names = c("x", "y"), row.names = c(NA, -37L), class = "data.frame")


## polygon

sp <- raster::spPolygons(cbind(d$x, d$y), attr = data.frame(p = 1))

pts <- spsample(sp, n = 50, type = "random")

## 25 lines
l <- split(as.data.frame(coordinates(pts)), rep(seq(length(pts)/2), each = 2))
lp <- raster::spLines(lapply(l, as.matrix))

op <- par(bg = "black")
plot(sp, border = "yellow", lwd =2 )
plot(lp, add = TRUE, col = "yellow", lty = 2)

enter image description here

2
  • Very nice! Although, the fixed length requirement does not seem to be satisfied.
    – Aaron
    May 20 '16 at 1:59
  • 1
    Ah missed that, easy enough to modify though - perhaps use this start as a way to trim to one end or the other. That runs into distribution problems maybe? Stay tuned
    – mdsumner
    May 20 '16 at 2:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.