2

I have made a function who calculate area polygon with Shoelace way.

That's works perfectly but right now I wonder if there is not a faster way to have the same result. I want to know that because this function must work faster with polygon with a lot of coordinates.

My function :

def shoelace_formula(polygonBoundary, absoluteValue = True):
    nbCoordinates = len(polygonBoundary)
    nbSegment = nbCoordinates - 1

    l = [(polygonBoundary[i+1][0] - polygonBoundary[i][0]) * (polygonBoundary[i+1][1] + polygonBoundary[i][1]) for i in xrange(nbSegment)]

    if absoluteValue:
        return abs(sum(l) / 2.)
    else:
        return sum(l) / 2.

My polygon :

polygonBoundary = ((5, 0), (6, 4), (4, 5), (1, 5), (1, 0))

Result :

22.

Any ideas?

I try with Numpy : It's speedest but you have to convert your coordinates first.

import numpy as np
x, y = zip(*polygonBoundary)

def shoelace_formula_3(x, y, absoluteValue = True):

    result = 0.5 * np.array(np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1)))
    if absoluteValue:
        return abs(result)
    else:
        return result
Sen Jacob
  • 3,384
  • 3
  • 35
  • 61
Guilhain
  • 107
  • 1
  • 9
  • 1
    Using `numpy` should help. http://stackoverflow.com/a/30408825/5666087 – jkr Dec 10 '16 at 15:36
  • @Jakub. I try with numpy, same performance. – Guilhain Dec 10 '16 at 15:54
  • Try it with many coordinates. When I tried both of your functions with 500 coordinate pairs, `shoelace_formula_3` was twice as fast (115 microseconds) as `shoelace_formula` (321 microseconds). – jkr Dec 10 '16 at 16:15
  • 1
    And if you do `x, y = zip(*polygonBoundary)` outside of the function and include `x` and `y` as function parameters, it runs in 93.7 microseconds. And import `numpy` outside of the function. – jkr Dec 10 '16 at 16:17
  • Faster: `0.5 * np.abs(np.dot(x[:-1], y[1:]) + x[-1]*y[0] - np.dot(y[:-1], x[1:]) - y[-1]*x[0])` – Ariel Aug 08 '19 at 07:23
  • MikeD in John Cook's [blog](https://www.johndcook.com/blog/2018/09/26/polygon-area/#comment-950196) suggested that the coordinates should be translated to be relative to one of the points in the polygon, in order to minimise precision loss when the absolute coordinate values are much larger than the area to be calculated. – syockit Jan 26 '21 at 09:13

7 Answers7

5

For me the fastest way would be using numpy where you have to send a numpy array of (x,y) cordinates as an argument in shoelace method:

import numpy as np
def shoelace(x_y):
    x_y = np.array(x_y)
    x_y = x_y.reshape(-1,2)

    x = x_y[:,0]
    y = x_y[:,1]

    S1 = np.sum(x*np.roll(y,-1))
    S2 = np.sum(y*np.roll(x,-1))

    area = .5*np.absolute(S1 - S2)

    return area
Mustaqim Khan
  • 51
  • 1
  • 3
1

Take a look at the Rosetta Code example using Numpy. Numpy gives a fast solution.

For your convenience, I paste below the Rosetta Code snippet:

import numpy as np
# x,y are arrays containing coordinates of the polygon vertices
x=np.array([3,5,12,9,5]) 
y=np.array([4,11,8,5,6]) 
i=np.arange(len(x))
#Area=np.sum(x[i-1]*y[i]-x[i]*y[i-1])*0.5 # signed area, positive if the vertex sequence is counterclockwise
Area=np.abs(np.sum(x[i-1]*y[i]-x[i]*y[i-1])*0.5) # one line of code for the shoelace formula

EDIT

You can now find the Shoelace formula implemented in the Scikit-Spatial library.

blunova
  • 2,122
  • 3
  • 9
  • 21
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jan 29 '22 at 19:42
  • I tested this answer, the one from @Mustaqim Khan and another implementation. I have not investigate why but this one gives a very different from the 2 other method I will suggest using Mustaqim Khan's answer – Timothee W Jul 27 '23 at 22:56
0

Here's a version that uses 1/2 as many multiplications: https://stackoverflow.com/a/717367/763269

If you need even greater performance, you could consider doing this in a Python C extension. C can be dramatically faster than Python, especially for math operations -- sometimes 100-1000x.

Community
  • 1
  • 1
Chris Johnson
  • 20,650
  • 6
  • 81
  • 80
0

Another interesting approach (although slower)

m = np.vstack([x,y])
result = 0.5 * np.abs(np.linalg.det(as_strided(m, (m.shape[1]-1, 2, 2), (m.itemsize, m.itemsize*m.shape[1], m.itemsize))).sum())
Ariel
  • 327
  • 2
  • 6
0
class Point //a new class for an any point a(X,Y), b(X,Y), c(X,Y), d(X,Y)
{
    //private int x, y;
    public int X { get; set; }
    public int Y { get; set; }

}

static class Geometry
{       

    public static void GerArea(Point a, Point b, Point c)
    {

        double area = 0.5 * ( (a.X * b.Y) + (b.X * c.Y) + (c.X * a.Y) - (b.X * a.Y) - (c.X * b.Y) - (a.X * c.Y) );

        Console.WriteLine(Math.Abs(area));
    }
    public static void GerArea(Point a, Point b, Point c, Point d)
    {
        double area = 0.5 * ((a.X * b.Y) + (b.X * c.Y) + (c.X * d.Y) + (d.X * a.Y) - (b.X * a.Y) - (c.X * b.Y) - (d.X * c.Y) - (a.X * d.Y ) );

        Console.WriteLine(Math.Abs(area));
    }
}
class Program
{
    static void Main(string[] args)
    {

        Point a = new Point() { X = -12, Y = 12 }; 
        Point b = new Point() { X = 15, Y = 15 };
        Point c = new Point() { X = -15, Y = -16 };
        Point d = new Point() { X = 16, Y = -15 };

        Console.WriteLine("****Shoelace formula****\n");


        Console.Write("Area of tringle: ");
        Geometry.GerArea(a, b, c);
        Console.Write("Area of quad: ");
        Geometry.GerArea(a, b, c, d);


        Console.ReadLine();

    }
}
Pobaranchuk
  • 839
  • 9
  • 13
0

This is a very simple implementation of shoelace formula in python

class Polygon:
  def __init__(self,arr):
    self.arr = arr
  def area(self):
    total=0
    i = 0
    while i != len(self.arr)-1:
      total+=self.arr[i][0]*self.arr[i+1][1]
      total-=self.arr[i+1][0]*self.arr[i][1]
      i+=1
    return abs(total +self.arr[-1][0]*self.arr[0][1] -self.arr[-1][-1]*self.arr[0][0])/2
harshit bhalla
  • 111
  • 1
  • 3
-1

Try simplest way, raw shoelace formula for triangles and polygons:

def shoelace_formula(x1, y1, x2, y2, x3, y3, x4, y4, x5, y5):
      return abs(0.5 * (x1*y2 + x2*y3 + x3*y4 + x4*y5 + x5*y1
                        - x2*y1 - x3*y2 - x4*y3 - x5*y4 - y1*y5))

print(shoelace_formula(5, 0, 6, 4, 4, 5, 1, 5, 1, 0))
2RMalinowski
  • 357
  • 5
  • 3