TransWikia.com

What fraction of a mountain parcel has slopes over 15%?

Mathematica Asked by Matt F. on May 26, 2021

Update: The original error was not from the elevation data, but from the use of "Degree". See below.

An agency interested in purchasing heavily sloped land has decided to use a criterion that "slopes must be ≥ 15 percent on more than 50 percent of the parcel". How can I calculate whether a parcel meets this criterion?

The answer may depend on the distance over which one measures the slopes, and on the data source, but I’d expect that any reasonable choice will approximate the agency’s calculations well enough for now.

Unfortunately, I got nonsensical results from using GeoElevationData naively (in Mathematica 11.3, on Windows). For instance, I calculated slopes at twelve angles and three distances from a point on the ski trail called Which Way Glade:

whichwayglade = GeoPosition[{42.203, -74.241}];
slopes[x_, radius_] := 
  Table[(GeoElevationData[GeoDestination[x, {radius, 30 i Degree}]] - 
      GeoElevationData[x]), {i, 12}]/radius;
Round[{slopes[whichwayglade, Quantity[100, "Feet"]], 
  slopes[whichwayglade, Quantity[100, "Yards"]],
  slopes[whichwayglade, Quantity[0.4, "Kilometer"]]}, .001]

The output is

{{0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.},
 {-0.022, -0.022, -0.022, -0.022, -0.022, -0.022,
  -0.022, -0.022, -0.022, -0.022, -0.022, -0.022},
 {-0.128, -0.128, -0.128, -0.128, -0.115, -0.115,
  -0.115, -0.115, -0.115, -0.115, -0.115, -0.115}}

The slope at the point should be roughly the maximum absolute value in one of the lists.

Update: Removing the word "Degree" from the code above leads to reasonable output, shown with pretty spaces as:

{{ 0.,     0.295,  0.295,  0.394,  0.066, 0.066,
  -0.23,  -0.23,  -0.295, -0.295, -0.295, 0.},
 { 0.175,  0.241,  0.295,  0.405,  0.284, 0.098,
  -0.109, -0.131, -0.284, -0.317, -0.23, -0.022},
 {-0.013,  0.187,  0.395,  0.402,  0.3,   0.337,
   0.135,  0.002,  0.032,  0.002, -0.1,  -0.128}}

So the slope at Which Way Glade is about 40% using any of these radii.

But compare these outputs with the topo map, GeoDisk[whichwayglade, Quantity[1, "Kilometer"]] // GeoElevationData // QuantityMagnitude // ListContourPlot

enter image description here

The 100-foot slopes would suggest a flat area. The 100-yard slopes would suggest the top of a perfectly conical mountain. The last outputs would suggest that there is no way to go 1/4-km uphill. So I don’t think these results are reliable.

How can I calculate the slope at a point in a reasonable way? Or how can I efficiently calculate the percent of a multi-acre parcel with slopes over 15%?

2 Answers

whichwayglade = GeoPosition[{42.203, -74.241}]

parcel = GeoBoundingBox[whichwayglade, Sqrt[Quantity[100, "Acres"]]]

We'll get our elevation data as GeoPositions so we can accurately measure the distance between the points (GeoDistance doesn't take height into account, according to the documentation). We can modify GeoArraySize here to get as much data as we have computing power - although be aware that the maximum resolution of the SRTM data GeoElevationData is based on is Not That Great)

elev = GeoElevationData[parcel, Automatic, "GeoPosition", 
  GeoArraySize -> {20, 20}

Now we'll split our positions into 2x2 blocks, and get the difference between the minimum and maximum altitude change, and the distance between the points with the minimum and maximum altitude.

mat = Partition[Thread /@ Thread@elev, {2, 2}];

minmax[b_] := SortBy[Flatten@b, #["Elevation"] &][[{1, 4}]]

Now we get the slope percentage by getting the "rise" - the difference in minimum and maximum elevation - and putting it over the "run" - the GeoDistance between those points that have the minimum and maximum elevation.

percents = 
 MapAt[(Quantity[(#[[2]]["Elevation"] - #[[1]]["Elevation"]), 
       "Meters"]/GeoDistance[#])*100 &, 
  MapAt[minmax, mat, {All, All}], {All, All}]

Now plotting these slope percents, we can see which boxes are flatter than others:

enter image description here

And we can trivially determine how many are over a certain percent:

Length@Select[Flatten@percents, GreaterThan[15]]

88

(of our total Length@Flatten@percents 100).

Side note: we get a really cool animation by incrasing the resolution over time:

slopeMap[bbox_, res_] := Module[{
   mat = Partition[
     Thread /@ 
      Thread@GeoElevationData[bbox, Automatic, "GeoPosition", 
        GeoArraySize -> {res, res}], {2, 2}],
   minmaxes
   },
  minmaxes = 
   MapAt[b |-> SortBy[Flatten@b, #["Elevation"] &][[{1, 4}]], 
    mat, {All, All}];
  MapAt[(Quantity[(#[[2]]["Elevation"] - #[[1]]["Elevation"]), 
        "Meters"]/GeoDistance[#])*100 &, 
   MapAt[minmax, mat, {All, All}], {All, All}]]

plots = Table[
   ArrayPlot[
    slopeMap[parcel, n]], {n, {5, 8, 10, 15, 20, 30, 40, 50, 100, 
     200}}];

ListAnimate[plots]

enter image description here

Answered by Carl Lange on May 26, 2021

You have to figure out the resolution of the xy coordinates of the data. The docs say at GeoZoomLevel of 12, the resolution is 38 meter at the equator. What it is elsewhere, it does not say. The below assumes that the resolution is in a certain number of degrees in longitute and latitude, and that remains constant. That's the idea. I'm afraid I cannot personally attest to it accuracy.

With that understanding, it's not hard to computing the gradient from an interpolation:

data = GeoDisk[whichwayglade, Quantity[1, "Kilometer"]] // 
    GeoElevationData // QuantityMagnitude;

if = Interpolation[
   Flatten[
    MapIndexed[N@Append[#2*
         (*Quantity[38.,"Meters"]/Quantity[1,"Feet"]*)
         38*3.2808398950131235`*
         {Sin[(*whichwayglade//Latitude*)
           42.203` Degree
           ], 1},
        #1] &,
     data, {2}],
    1]
   ];
myslope[x_, y_] = D[if[y, x], {{x, y}}] // Norm;

Quantity stuff is slow so I precomputed some constants above. Here's a visualization, where the area outside the red lines has slopes over 15%:

Show[
 ContourPlot[
  if[y, x],
  Evaluate[
   Sequence @@ MapThread[Prepend, {Reverse@if@"Domain", {x, y}}]
   ]
  ],
 ContourPlot[
  myslope[x, y],
  Evaluate[
   Sequence @@ MapThread[Prepend, {Reverse@if@"Domain", {x, y}}]
   ],
  Contours -> {{0.15}}, ContourStyle -> Directive[Red, Thick], 
  ContourShading -> {None, Directive[Opacity[0.3, Red]]}
  ]
 ]

enter image description here

Added by Matt F:

This method curiously solves the problem without making it easy to calculate the interpolated height or estimated slope at a particular latitude and longitude. However, the same ideas can be used in an elevation function with lat-long inputs, giving the following parallel for the code in the question:

whichwayglade = GeoPosition[{42.203, -74.241}];
sidelength = Quantity[Sqrt[Quantity[100, "Acres"]/
   Quantity[1., "Meters"]^2], "Meters"];
parcel = GeoBoundingBox[GeoDisk[whichwayglade, sidelength]];
data = parcel // GeoElevationData // QuantityMagnitude;
c2 = Quantity[38., "Meter"]/Quantity[1, "Foot"];
c1 = c2 Sin[42.203 Pi/180];
d1 = c1 (Dimensions[data][[1]] - 1)/(parcel[[2, 1, 1]] - parcel[[1, 1, 1]]);
d2 = c2 (Dimensions[data][[2]] - 1)/(parcel[[2, 1, 2]] - parcel[[1, 1, 2]]);
if = Interpolation[Flatten[MapIndexed[N@Append[#2 {c1, c2}, #1] &, data, {2}], 1]];
newelev[geopos_] := Quantity[if[c1 + d1 (geopos[[1, 1]] - parcel[[1, 1, 1]]),
  c2 + d2 (geopos[[1, 2]] - parcel[[1, 1, 2]])], "Feet"];
slopes[x_, radius_] := Table[newelev[GeoDestination[x, {radius, 30 i}]]
  - newelev[x], {i, 12}]/radius;
Round[{slopes[whichwayglade, Quantity[100, "Feet"]], 
  slopes[whichwayglade, Quantity[100, "Yards"]], 
  slopes[whichwayglade, Quantity[0.4, "Kilometer"]]}, .001]

Answered by Michael E2 on May 26, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP