How to calculate relative position of each edge of rectangle

2584
14
03-19-2018 12:46 PM
deleted-user-kYCocWVdqfTG
New Contributor II

Hi everyone, here is a question, how can I know relative position of each edge of rectangle with arcpy script, i.e, top edge, bottom edge, left edge and right edge. The rectangle is defined at least 4 points,maybe 8 points,that means uncertain points. and then I need to know which edge any point  is on. thank you.

0 Kudos
14 Replies
DanPatterson_Retired
MVP Emeritus

polygon points are ordered in clockwise order with the first and last point repeated (lets leave out holes and multipart features for the moment)

Get the points, parse them into pairs and determine which ones belong to and form the 'extent' rectangle The first point will either belong to the left, top, right bottom of the extent.  You can proceed from there

deleted-user-kYCocWVdqfTG
New Contributor II

Hi Dan, 

Thank you very much for your ideas for retrieve the edges of polgyline. I tried to use the extent to calculate the distance between each point and the edge of extent, it works very well except the situation in which points are very close in the corners because I just used simply a constant distance as the valve. 

0 Kudos
DanPatterson_Retired
MVP Emeritus

Another approach for convex (or gently concave) features, is to calculate the direction to each vertex from the point cloud centroid.

  If the point cloud is sorted in a radial fashion (there is code to do this) relative to the x-axis in descending order (ie 180 to -180) then you will have your points sorted in clockwise order.

Perhaps if you could elaborate on what it is you are trying to do, a direct solution could be given.

It appears you are exploring for solutions that you may not know already exist or for variants of solutions you, have tried.  There are always 'corner cases' to any solution but the rate of new algorithm discovery over the last 40 years has pretty-well tapered off

deleted-user-kYCocWVdqfTG
New Contributor II

Thank you so much for your reply. Now I am trying to use distance and direction at the same time to fix the corner case. Actually, in my case all polylines are "rectangle" shape which has four edges, 2 vertical and 2 horizontal, each edge is composed of 5 points, regularly distributed. Also, the rectangle has similar size, that is why I tried to use the distance to the extent of the rectangle to group points into four edges. Now, the question is how to fix the question in case of a rectangle with an uncertain number of points and locations.

Many Thanks.

0 Kudos
DanPatterson_Retired
MVP Emeritus

A radial sort about the point cloud centroid would be a goo start... since the rectangle is convex, it will have a minimum of 5 points with the first and last the same, but it could be densified (ie extraneous vertices that don't change the implicit shape)

The following uses numpy, I don't know what you are using, but vectorized operations on shape geometry is much easier than trying to code pure python solutions.  I have dozens of examples on my blog Py... blog

def _center(a, remove_dup=True):
    """Return the center of an array. If the array represents a polygon, then
    a check is made for the duplicate first and last point to remove one.
    """
    if remove_dup:
        if np.all(a[0] == a[-1]):
            a = a[:-1]
    return a.mean(axis=0)
def radial_sort(pnts, cent=None):
    """Sort about the point cloud center or from a given point

    `pnts` : points
        An array of points (x,y) as array or list
    `cent` : coordinate
        list, tuple, array of the center's x,y coordinates
        ::
            cent = [0, 0] or np.array([0, 0])

    Returns:
    -------
        The angles in the range -180, 180 x-axis oriented

    """
    pnts = np.asarray(pnts, dtype=np.float64)
    if cent is None:
        cent = _center(pnts, remove_dup=False)
    ba = pnts - cent
    ang_ab = np.arctan2(ba[:, 1], ba[:, 0])
    ang_ab = np.degrees(ang_ab)
    sort_order = np.argsort(ang_ab)
    return ang_ab, sort_order‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
JoshuaBixby
MVP Esteemed Contributor

As they say, a picture is worth a thousand words, and I think a picture would be most helpful here.

DanPatterson_Retired
MVP Emeritus

any movement on the image?

related questions are popping up elsewhere 

https://stackoverflow.com/questions/49783322/how-to-check-the-position-of-a-point-with-the-respect-t...

0 Kudos
deleted-user-kYCocWVdqfTG
New Contributor II

A case for this question: need to group all vertexes (gree and red points, the red is starting point) into four edges, top , bottom, lef and right edge.

0 Kudos
RichardFairhurst
MVP Honored Contributor

The typical way I have dealt with section, township and range polygon edges is to start with a polygon layer with all section polygons that are hopefully topologically correct so that gaps and overlaps are avoided.  Then I use the Polygon to Line tool with the Identify Neighbor option checked.  I then add fields to the lines for the Left and Right Section, Township and Range and Line_Side_Type.  I join the LEFT_FID field of the Lines to the polygon ObjectID and calculate all of the left fields I created for Section, Township and Range.  Then I break the join.  Then I join the RIGHT_FID field of the Lines to the polygon ObjectID and calculate all of the right fields I created for Section, Township and Range.  Then I break the join.

Now I can select for logical relationships of sections to determine what the side types are.  For example, the LEFT_LINE_SIDE_TYPE for the polygon is BOTTOM and the RIGHT_LINE_SIDE_TYPE for the polygon is TOP because the line is pointing right which can be found for normal sections using the following query:

(LEFT_SECTION = 1 AND RIGHT_SECTION = 12) OR (LEFT_SECTION = 2 AND RIGHT_SECTION = 11) OR (LEFT_SECTION = 3 AND RIGHT_SECTION = 10) OR (LEFT_SECTION = 4 AND RIGHT_SECTION = 9) OR (LEFT_SECTION = 5 AND RIGHT_SECTION = 😎 OR (LEFT_SECTION = 6 AND RIGHT_SECTION = 7) OR

(LEFT_SECTION = 7 AND RIGHT_SECTION = 18) OR (LEFT_SECTION = 8 AND RIGHT_SECTION = 17) OR (LEFT_SECTION = 9 AND RIGHT_SECTION = 16) OR (LEFT_SECTION = 10 AND RIGHT_SECTION = 15) OR (LEFT_SECTION = 11 AND RIGHT_SECTION = 14) OR (LEFT_SECTION = 12 AND RIGHT_SECTION = 13) OR

(LEFT_SECTION = 13 AND RIGHT_SECTION = 24) OR (LEFT_SECTION = 14 AND RIGHT_SECTION = 23) OR (LEFT_SECTION = 15 AND RIGHT_SECTION = 22) OR (LEFT_SECTION = 16 AND RIGHT_SECTION = 21) OR (LEFT_SECTION = 17 AND RIGHT_SECTION = 20) OR (LEFT_SECTION = 18 AND RIGHT_SECTION = 19) OR

(LEFT_SECTION = 19 AND RIGHT_SECTION = 30) OR (LEFT_SECTION = 20 AND RIGHT_SECTION = 29) OR (LEFT_SECTION = 21 AND RIGHT_SECTION = 28) OR (LEFT_SECTION = 22 AND RIGHT_SECTION = 27) OR (LEFT_SECTION = 23 AND RIGHT_SECTION = 26) OR (LEFT_SECTION = 24 AND RIGHT_SECTION = 25) OR

(LEFT_SECTION = 25 AND RIGHT_SECTION = 30) OR (LEFT_SECTION = 26 AND RIGHT_SECTION = 35) OR (LEFT_SECTION = 27 AND RIGHT_SECTION = 34) OR (LEFT_SECTION = 28 AND RIGHT_SECTION = 33) OR (LEFT_SECTION = 29 AND RIGHT_SECTION = 32) OR (LEFT_SECTION = 30 AND RIGHT_SECTION = 31) OR

(LEFT_SECTION = 31 AND RIGHT_SECTION = 6) OR (LEFT_SECTION = 32 AND RIGHT_SECTION = 5) OR (LEFT_SECTION = 33 AND RIGHT_SECTION = 4) OR (LEFT_SECTION = 34 AND RIGHT_SECTION = 3) OR (LEFT_SECTION = 35 AND RIGHT_SECTION = 2) OR (LEFT_SECTION = 36 AND RIGHT_SECTION = 1)

The LEFT_LINE_SIDE_TYPE for the polygon is TOP and  the RIGHT_LINE_SIDE_TYPE for the polygon is BOTTOM because the line is pointing left which can be found for normal sections using the following query:

(RIGHT_SECTION = 1 AND LEFT_SECTION = 12) OR (RIGHT_SECTION = 2 AND LEFT_SECTION = 11) OR (RIGHT_SECTION = 3 AND LEFT_SECTION = 10) OR (RIGHT_SECTION = 4 AND LEFT_SECTION = 9) OR (RIGHT_SECTION = 5 AND LEFT_SECTION = 😎 OR (RIGHT_SECTION = 6 AND LEFT_SECTION = 7) OR
(RIGHT_SECTION = 7 AND LEFT_SECTION = 18) OR (RIGHT_SECTION = 8 AND LEFT_SECTION = 17) OR (RIGHT_SECTION = 9 AND LEFT_SECTION = 16) OR (RIGHT_SECTION = 10 AND LEFT_SECTION = 15) OR (RIGHT_SECTION = 11 AND LEFT_SECTION = 14) OR (RIGHT_SECTION = 12 AND LEFT_SECTION = 13) OR (RIGHT_SECTION = 13 AND LEFT_SECTION = 24) OR (RIGHT_SECTION = 14 AND LEFT_SECTION = 23) OR (RIGHT_SECTION = 15 AND LEFT_SECTION = 22) OR (RIGHT_SECTION = 16 AND LEFT_SECTION = 21) OR (RIGHT_SECTION = 17 AND LEFT_SECTION = 20) OR (RIGHT_SECTION = 18 AND LEFT_SECTION = 19) OR
(RIGHT_SECTION = 19 AND LEFT_SECTION = 30) OR (RIGHT_SECTION = 20 AND LEFT_SECTION = 29) OR (RIGHT_SECTION = 21 AND LEFT_SECTION = 28) OR (RIGHT_SECTION = 22 AND LEFT_SECTION = 27) OR (RIGHT_SECTION = 23 AND LEFT_SECTION = 26) OR (RIGHT_SECTION = 24 AND LEFT_SECTION = 25) OR
(RIGHT_SECTION = 25 AND LEFT_SECTION = 30) OR (RIGHT_SECTION = 26 AND LEFT_SECTION = 35) OR (RIGHT_SECTION = 27 AND LEFT_SECTION = 34) OR (RIGHT_SECTION = 28 AND LEFT_SECTION = 33) OR (RIGHT_SECTION = 29 AND LEFT_SECTION = 32) OR (RIGHT_SECTION = 30 AND LEFT_SECTION = 31) OR
(RIGHT_SECTION = 31 AND LEFT_SECTION = 6) OR (RIGHT_SECTION = 32 AND LEFT_SECTION = 5) OR (RIGHT_SECTION = 33 AND LEFT_SECTION = 4) OR (RIGHT_SECTION = 34 AND LEFT_SECTION = 3) OR (RIGHT_SECTION = 35 AND LEFT_SECTION = 2) OR (RIGHT_SECTION = 36 AND LEFT_SECTION = 1)

The LEFT_LINE_SIDE_TYPE for the polygon is LEFT and the RIGHT_LINE_SIDE_TYPE for the polygon is RIGHT when the line is pointing down which can be found for normal sections using the following query.

(LEFT_SECTION = 1 AND RIGHT_SECTION = 2) OR (LEFT_SECTION = 2 AND RIGHT_SECTION = 3) OR (LEFT_SECTION = 3 AND RIGHT_SECTION = 4) OR (LEFT_SECTION = 4 AND RIGHT_SECTION = 5) OR (LEFT_SECTION = 5 AND RIGHT_SECTION = 6) OR (LEFT_SECTION = 6 AND RIGHT_SECTION = 1) OR (LEFT_SECTION = 8 AND RIGHT_SECTION = 7) OR (LEFT_SECTION = 9 AND RIGHT_SECTION = 😎 OR (LEFT_SECTION = 10 AND RIGHT_SECTION = 9) OR (LEFT_SECTION = 11 AND RIGHT_SECTION = 10) OR (LEFT_SECTION = 12 AND RIGHT_SECTION = 11) OR (LEFT_SECTION = 7 AND RIGHT_SECTION = 12) OR (LEFT_SECTION = 13 AND RIGHT_SECTION = 14) OR (LEFT_SECTION = 14 AND RIGHT_SECTION = 15) OR (LEFT_SECTION = 15 AND RIGHT_SECTION = 16) OR (LEFT_SECTION = 16 AND RIGHT_SECTION = 17) OR (LEFT_SECTION = 17 AND RIGHT_SECTION = 18) OR (LEFT_SECTION = 18 AND RIGHT_SECTION = 13) OR (LEFT_SECTION = 20 AND RIGHT_SECTION = 19) OR (LEFT_SECTION = 21 AND RIGHT_SECTION = 20) OR (LEFT_SECTION = 22 AND RIGHT_SECTION = 21) OR (LEFT_SECTION = 23 AND RIGHT_SECTION = 22) OR (LEFT_SECTION = 24 AND RIGHT_SECTION = 23) OR (LEFT_SECTION = 19 AND RIGHT_SECTION = 24) OR (LEFT_SECTION = 25 AND RIGHT_SECTION = 26) OR (LEFT_SECTION = 26 AND RIGHT_SECTION = 27) OR (LEFT_SECTION = 27 AND RIGHT_SECTION = 28) OR (LEFT_SECTION = 28 AND RIGHT_SECTION = 29) OR (LEFT_SECTION = 29 AND RIGHT_SECTION = 30) OR (LEFT_SECTION = 30 AND RIGHT_SECTION = 15) OR (LEFT_SECTION = 32 AND RIGHT_SECTION = 31) OR (LEFT_SECTION = 33 AND RIGHT_SECTION = 32) OR (LEFT_SECTION = 34 AND RIGHT_SECTION = 33) OR (LEFT_SECTION = 35 AND RIGHT_SECTION = 34) OR (LEFT_SECTION = 36 AND RIGHT_SECTION = 35) OR (LEFT_SECTION = 31 AND RIGHT_SECTION = 36)

The LEFT_LINE_SIDE_TYPE for the polygon is RIGHT and the RIGHT_LINE_SIDE_TYPE for the polygon is LEFT when the line is pointing up which can be found for normal sections using the following query.

(RIGHT_SECTION = 1 AND LEFT_SECTION = 2) OR (RIGHT_SECTION = 2 AND LEFT_SECTION = 3) OR (RIGHT_SECTION = 3 AND LEFT_SECTION = 4) OR (RIGHT_SECTION = 4 AND LEFT_SECTION = 5) OR (RIGHT_SECTION = 5 AND LEFT_SECTION = 6) OR (RIGHT_SECTION = 6 AND LEFT_SECTION = 1) OR (RIGHT_SECTION = 8 AND LEFT_SECTION = 7) OR (RIGHT_SECTION = 9 AND LEFT_SECTION = 😎 OR (RIGHT_SECTION = 10 AND LEFT_SECTION = 9) OR (RIGHT_SECTION = 11 AND LEFT_SECTION = 10) OR (RIGHT_SECTION = 12 AND LEFT_SECTION = 11) OR (RIGHT_SECTION = 7 AND LEFT_SECTION = 12) OR (RIGHT_SECTION = 13 AND LEFT_SECTION = 14) OR (RIGHT_SECTION = 14 AND LEFT_SECTION = 15) OR (RIGHT_SECTION = 15 AND LEFT_SECTION = 16) OR (RIGHT_SECTION = 16 AND LEFT_SECTION = 17) OR (RIGHT_SECTION = 17 AND LEFT_SECTION = 18) OR (RIGHT_SECTION = 18 AND LEFT_SECTION = 13) OR (RIGHT_SECTION = 20 AND LEFT_SECTION = 19) OR (RIGHT_SECTION = 21 AND LEFT_SECTION = 20) OR (RIGHT_SECTION = 22 AND LEFT_SECTION = 21) OR (RIGHT_SECTION = 23 AND LEFT_SECTION = 22) OR (RIGHT_SECTION = 24 AND LEFT_SECTION = 23) OR (RIGHT_SECTION = 19 AND LEFT_SECTION = 24) OR (RIGHT_SECTION = 25 AND LEFT_SECTION = 26) OR (RIGHT_SECTION = 26 AND LEFT_SECTION = 27) OR (RIGHT_SECTION = 27 AND LEFT_SECTION = 28) OR (RIGHT_SECTION = 28 AND LEFT_SECTION = 29) OR (RIGHT_SECTION = 29 AND LEFT_SECTION = 30) OR (RIGHT_SECTION = 30 AND LEFT_SECTION = 15) OR (RIGHT_SECTION = 32 AND LEFT_SECTION = 31) OR (RIGHT_SECTION = 33 AND LEFT_SECTION = 32) OR (RIGHT_SECTION = 34 AND LEFT_SECTION = 33) OR (RIGHT_SECTION = 35 AND LEFT_SECTION = 34) OR (RIGHT_SECTION = 36 AND LEFT_SECTION = 35) OR (RIGHT_SECTION = 31 AND LEFT_SECTION = 36)

All other lines are either on the outer boundaries of all sections, at offsets of adjoining townships or are portions of non-standard sections that do not align with the normal section grid pattern.

You can use the Intersect Tool or Spatial Join tool on your points with these lines to assign them with the side types of the polygons that touch them.  With some relatively simple manipulations of record selections, field calculations and the append tool you could create the set of lines that cover each side of each section separately and use them for the intersect.