{"id":158,"date":"2007-08-23T08:32:35","date_gmt":"2007-08-23T13:32:35","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/2007\/08\/23\/upslope-area-handling-plateaus-revisited\/"},"modified":"2007-09-28T13:57:00","modified_gmt":"2007-09-28T18:57:00","slug":"upslope-area-handling-plateaus-revisited","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2007\/08\/23\/upslope-area-handling-plateaus-revisited\/","title":{"rendered":"Upslope area &#8211; handling plateaus, revisited"},"content":{"rendered":"<p>\r\nEarlier in this series I have discussed the problem of plateaus.  Specifically, how do you assign a flow direction to a DEM pixel that has no downhill neighbors?  I proposed in <a href=\"https:\/\/blogs.mathworks.com\/steve\/2007\/06\/08\/upslope-area-part-5\/\">part 5<\/a> that the problem could be addressed by using the interpolation technique in <tt>roifill<\/tt> to modify plateau values so that they are no longer equal, and then recomputing the flow direction based on the modified values.\r\n<\/p>\r\n\r\n<p>\r\nOK, true confessions time&mdash;I could never get this idea to really work! Here's a small set of DEM values to illustrate the typical problem:\r\n<\/p>\r\n\r\n<p>\r\n<img decoding=\"async\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/158\/arrowing_a.png\" \/>\r\n<\/p>\r\n\r\n<p>\r\nThe pixels labeled A and B in the right column have no downhill neighbors.  Modifying those values using <tt>roifill<\/tt> so they were no longer flat had the effect of raising pixel A above the value of pixel C.  As a result, my recomputed flow directions had the amusing property that pixel C flowed to pixel A, and pixel A flowed to pixel C!\r\n<\/p>\r\n\r\n<p>\r\n<img decoding=\"async\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/158\/arrowing_b.png\" \/>\r\n<\/p>\r\n\r\n<p>\r\nI discovered this problem while trying to understand why, in some cases, I was getting flow matrices that were singular.  \r\n<\/p>\r\n\r\n<p>\r\nSo it was back to the drawing board. I eventually found a good solution in F. Meyer, \"Skeletons and Perceptual Graphs,\" Signal Processing 16 (1989) 335-363. This paper has a couple of algorithms for determining ordering relationships among pixels on a plateau. One of the algorithms, called <em>arrowing<\/em>, turned to be suitable for computing flow matrix weights for plateau pixels. (Meyer credits Maisonneuve for the original idea.)\r\n<\/p>\r\n\r\n<blockquote>\r\n<em>First scan.<\/em> During the first scan every pair of neighbors <em>(x,y)<\/em> is considered. If the pixel <em>x<\/em> is higher than the pixel <em>y<\/em>, it is given an arrow towards <em>y<\/em>. During this scan all descending borders of all plateaus receive arrows.\r\n\r\n<p>\r\n<em>Next scans.<\/em> Until convergence, the following operation is repeated: if <em>x<\/em> and <em>y<\/em> are two neighbours sharing the same altitude, and if <em>x<\/em> possesses an arrow but not <em>y<\/em>, then <em>y<\/em> is given an arrow pointing towards <em>x<\/em>.\r\n<\/p>\r\n\r\n<p>\r\n&ndash;pages 360-361\r\n<\/p>\r\n<\/blockquote>\r\n\r\n<p>\r\nHere's an example showing how to compute flow directions using arrowing.  Below, on the left, is a 4-by-4 matrix of DEM pixels.  Some of these DEM pixels have no downhill neighbors.  These pixels are indicated via NaNs in the matrix on the right.\r\n<\/p>\r\n\r\n<p>\r\n<img decoding=\"async\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/158\/arrowing_c.png\" \/>\r\n<\/p>\r\n\r\n<p>\r\nWith our earlier method for calculating flow directions, the descending borders of plateaus already have flow directions computed for them, which means we can skip the <em>first scan<\/em> step of the arrowing algorithm. In each subsequent scan, we identify the pixels that do not have a flow direction yet, and which have equal-altitude neighbors that do. Here is the first set of such pixels:\r\n<\/p>\r\n\r\n<p>\r\n<img decoding=\"async\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/158\/arrowing_d.png\" \/>\r\n<\/p>\r\n\r\n<p>\r\nWe'll say that each shaded pixel flows equally to all of its equal-altitude neighbors that already have flow directions established, like this:\r\n<\/p>\r\n\r\n<p>\r\n<img decoding=\"async\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/158\/arrowing_e.png\" \/>\r\n<\/p>\r\n\r\n<p>\r\nOn the second scan, we find only one pixel without a flow direction, and which has equal-altitude neighbors that do have flow directions:\r\n<\/p>\r\n\r\n<p>\r\n<img decoding=\"async\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/158\/arrowing_f.png\" \/>\r\n<\/p>\r\n\r\n<p>\r\nAnd so we assign flow directions like this:\r\n<\/p>\r\n\r\n<p>\r\n<img decoding=\"async\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/158\/arrowing_g.png\" \/>\r\n<\/p>\r\n\r\n<p>The third scan finds no pixels satisfying the criteria, so we know we're done.\r\n<\/p>\r\n\r\n<p>\r\nRevisiting the example I started with, here's how the arrowing procedure assigns flow directions to pixels A and B:\r\n<\/p>\r\n\r\n<p>\r\n<img decoding=\"async\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/158\/arrowing_h.png\" \/>\r\n<\/p>\r\n\r\n<p>\r\nThe MATLAB code is a little too involved to show in detail here. If you care to see exactly how it works, look at the <tt>plateau_flow_weights<\/tt> subfunction of <tt>flow_matrix.m<\/tt> in my <a title=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/loadFile.do?objectId=15818&objectType=FILE (link no longer works)\">upslope area code on the MATLAB Central File Exchange<\/a>.\r\n<\/p>","protected":false},"excerpt":{"rendered":"<p>\r\nEarlier in this series I have discussed the problem of plateaus.  Specifically, how do you assign a flow direction to a DEM pixel that has no downhill neighbors?  I proposed in part 5 that the... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2007\/08\/23\/upslope-area-handling-plateaus-revisited\/\">read more >><\/a><\/p>","protected":false},"author":42,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[9],"tags":[],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/158"}],"collection":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/users\/42"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/comments?post=158"}],"version-history":[{"count":0,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/158\/revisions"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=158"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=158"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=158"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}