0

Download shapes.py from the website and extend it with class Circle, class Polygon, and method area. Classes Circle and Polygon should inherit from Shape. For circles, at creation the radius has to be specified and the str method should return a string in the same style as for Line and Rectangle: For rectangles, at creation the list of vertices has to be follow the example below precisely. Add method area to classes Line, Rectangle, Circle. The area of a line is zero and the areas of rectangles and circles should be computed in the usual way. For this, import pi from math. The area of a non-self-intersecting polygon with vertices is defined ashere

where each with represents a vertex (“corner”) of a polygon, and and . Note that the area of a convex polygon is positive if the points are in counterclockwise order and negative if they are in clockwise order. (The Wikipedia page takes the absolute value to avoid negative areas. Don’t do this here.)

Here are some test cases:

   g = Group()
    l = Line(1, 2, 5, 6); g.add(l); print(l)
 Line from (1, 2) to (5, 6)
    r = Rectangle(-2, -3, 2, 3); g.add(r); print(r)
 Rectangle at (-2, -3), width 2, height 3
    c = Circle(1, -5, 1); g.add(c); print(c)
 Circle at (1, -5), radius 1
    p = Polygon([(0, 0), (4, 0), (2, 2), (4, 4), (0, 4), (2, 2)]); g.add(p);     print(p)
 Polygon with [(0, 0), (4, 0), (2, 2), (4, 4), (0, 4), (2, 2)]
    print(g); g.move(1, 1); print(g)
 Group with:
  Circle at (1, -5), radius 1
  Rectangle at (-2, -3), width 2, height 3
  Line from (1, 2) to (5, 6)
  Polygon with [(0, 0), (4, 0), (2, 2), (4, 4), (0, 4), (2, 2)]
 Group with:
  Circle at (2, -4), radius 1
  Rectangle at (-1, -2), width 2, height 3
  Line from (2, 3) to (6, 7)
  Polygon with [(1, 1), (5, 1), (3, 3), (5, 5), (1, 5), (3, 3)]
    l.area(), r.area(), c.area(), p.area(), g.area()
  (0, 6, 3.141592653589793, 8.0, 17.141592653589793)

I'm very new at Python and don't know how to approach this whatsoever. Any help would be appreciated! Thanks!

Levon
  • 138,105
  • 33
  • 200
  • 191
spookyblack
  • 43
  • 2
  • 10
  • Read this: https://en.m.wikibooks.org/wiki/A_Beginner%27s_Python_Tutorial/Classes The examples given are similar to your problem. – samgak Jun 06 '16 at 03:58
  • See http://stackoverflow.com/questions/34326728/how-do-i-calculate-the-area-of-a-non-convex-polygon – Mark Ransom Jun 06 '16 at 04:05
  • To be honest, I'm not really going to read that question until the formatting is better, but the answer to the title is to use the shapely library. Probably won't get you a good homework grade though :) – en_Knight Jun 06 '16 at 06:14

2 Answers2

1

I haven't finished the code. But created a structure for you to learn and start filling in.

Shape is a base class and Line, Circle, Rectangle and Polygon are children classes (inheritance). [Assuming you have idea of OOP]

class Group(object):
    """docstring for Group"""

    def __init__(self):
        super(Group, self).__init__()
        self.shapes = list()

    def add(self, shape):
        self.shapes.append(shape)

    def move(self, dx, dy):
        for shape in self.shapes:
            print "Moving %s by (%d, %d)" % (shape, dx, dy)
            shape.move(dx, dy)

    def __str__(self):
        return "Group with: " + ", ".join((str(shape) for shape in self.shapes))


class Shape(object):
    """docstring for Shape"""

    def __init__(self):
        super(Shape, self).__init__()

    def print_details(self):
        pass

    def area(self):
        pass


class Line(Shape):
    """docstring for Line"""

    def __init__(self, x1, y1, x2, y2):
        super(Line, self).__init__()
        self.x1 = x1
        self.y1 = y1
        self.x2 = x2
        self.y2 = y2

    def area(self):
        return 0

    def __str__(self):
        return "Line from (%s, %s) to (%s, %s)" % (self.x1, self.y1, self.x2, self.y2)


class Rectangle(Shape):
    """docstring for Line"""

    def __init__(self, x1, y1, x2, y2):
        super(Rectangle, self).__init__()
        self.x1 = x1
        self.y1 = y1
        self.x2 = x2
        self.y2 = y2

    def area(self):
        return self._width * self._height

    def __str__(self):
        return "Rectangle at (%s, %s), width %s, height %s" % (
          self.x1, self.y1, self._width(), self._height())

    def _width(self):
        return abs(self.x1 - self.x2)

    def _height(self):
        return abs(self.y1 - self.y2)

class Circle(Shape):
  """docstring for Circle"""
  def __init__(self, x, y, radius):
    super(Circle, self).__init__()
    self.x = x
    self.y = y
    self.radius = radius

  def area(self):
    pass

  def __str__(self):
    return "Circle ....."

class Polygon(Shape):
    """docstring for Line"""

    def __init__(self, vertices):
        super(Polygon, self).__init__()
        if type(vertices) is list and len(vertices) > 0 and type(vertices[0]) is tuple:
            self.vertices = vertices
        else:
            raise Exception("Invalid syntax")

    def area(self):
        area = 0
        for index in range(len(self.vertices) - 1):
            v1 = self.vertices[index]
            v2 = self.vertices[index + 1]
            area += ((v1[0] * v2[1]) - (v2[0] * v1[1]))
        area = 0.5 * area
        return area

    def __str__(self):
        return "Polygon with %s" % str(self.vertices)

g = Group()

l = Line(1, 2, 5, 6)
g.add(l)
print(l) #Line from (1, 2) to (5, 6) 

r = Rectangle(-2, -3, 2, 3)
g.add(r)
print(r) #Rectangle at (-2, -3), width 2, height 3

c = Circle(1, -5, 1)
g.add(c)
print(c) #Circle at (1, -5), radius 1

p = Polygon([(0, 0), (4, 0), (2, 2), (4, 4), (0, 4), (2, 2)])
g.add(p)
print(p) #Polygon with [(0, 0), (4, 0), (2, 2), (4, 4), (0, 4), (2, 2)] 

print(g)
g.move(1, 1)
print(g) #Group with: Circle at (1, -5), radius 1 Rectangle at (-2, -3), width 2, height 3 Line from (1, 2) to (5, 6) Polygon with [(0, 0), (4, 0), (2, 2), (4, 4), (0, 4), (2, 2)] Group with: Circle at (2, -4), radius 1 Rectangle at (-1, -2), width 2, height 3 Line from (2, 3) to (6, 7) Polygon with [(1, 1), (5, 1), (3, 3), (5, 5), (1, 5), (3, 3)] l.area(), r.area(), c.area(), p.area(), g.area() (0, 6, 3.141592653589793, 8.0, 17.141592653589793)

Feel free to ask more questions if you have doubts

Garima Singh
  • 233
  • 1
  • 7
  • Hi, thank you so much! I figured out most of it but can't figure out how to group the classes together. Can you help me with that? – spookyblack Jun 07 '16 at 14:39
  • @spookyblack - I have edited the group to add more details that would help you. You would still need to override the move method in subclasses of class Shape and implement the behaviour. For basics of OOP see [link](http://www.tutorialspoint.com/python/python_classes_objects.htm). You should try adding more behaviour to learn like once you finish the above try adding more functions example scale or rotate. – Garima Singh Jun 08 '16 at 01:12
  • Thanks! I figured it out. I appreciate your help! – spookyblack Jun 12 '16 at 22:32
-1

This is the shoelace formula (http://en.wikipedia.org/wiki/Shoelace_formula) and i found a possible implementation in python on http://www.cgafaq.info/wiki/Polygon_Area. This site seems to be down, but i found out that the shapely module named the same source (https://toblerity.org/shapely/shapely.algorithms.html)

So, if you want the formula impelmented in python, this seems to me a reliable one:

def signed_area(coords):
    """Return the signed area enclosed by a ring using the linear time
algorithm at http://www.cgafaq.info/wiki/Polygon_Area. A value >= 0
indicates a counter-clockwise oriented ring.
return 0, if the ring is not closed
"""
    xs, ys = map(list, zip(*coords))
    xs.append(xs[1])
    ys.append(ys[1])
    return sum(xs[i]*(ys[i+1]-ys[i-1]) for i in range(1, len(coords)))/2.0

As the doc string tells, on a closed ring, where first coordinate pair equals the last, signed_area returns the area of that. The return value is Zero, if the ring is not closed. If result is negativ, the orientation of the polygon is clockwise, if it is positive, the polygon is counter-clockwise.

If your polygon consists of multiple rings, you add the signed area of each ring, because outer rings and inner rings have a different directions which results in opposite signs.

Andreas Müller
  • 210
  • 2
  • 10
  • This implementation is wrong: with `for i in range(1, len(coords))` you only make a sum of `len(coords) - 1` terms, while you need `len(coords)` terms (as many terms as points in the list). – zezollo Dec 14 '17 at 18:21
  • No, you are wrong. First, there is an append to both lists and an i+1 is used for calculation of Sum. Second, try it, the results are correct (as far as i can tell by using the function) – Andreas Müller Dec 16 '17 at 14:49
  • `print(signed_area([(-1, 0), (0, 0), (0, 1)]))` gives `0` whereas the correct result should be `0.5`. The `i + 1` does not change the number of terms: for a triangle, `len(coords) == 3` and `print([i for i in range(1, 3)])` gives `[1, 2]`, hence you only have 2 products: `xs[1]**(ys[2]-ys[0])` and `xs[2]**(ys[3]-ys[1])`. The missing product is `xs[0]**(ys[1]-ys[3])`. `xs[0]` is never used in this sum. – zezollo Dec 16 '17 at 16:15
  • Oh, ok, I get it: for a polygon you have to put the first couple of coordinates another time at the end. Hence, for the triangle of my last comment, I should use `signed_area([(-1, 0), (0, 0), (0, 1), (-1, 0)])` instead. This way, there are 4 - 1 terms, i.e. 3 and this is correct. Well maybe this should be made more explicit in your answer. Yet, there's still a weird thing, "open rings" do not always return `0` like announced: `print(signed_area([(-1, 1), (-1, 2), (-2, -1)]))` gives `1.0`. Or am I missing something else? – zezollo Dec 16 '17 at 17:04
  • Yes, that's important, a ring must be closed, the implementation in Python is not mine, i took it from the URL given in the doc-string and yes in your example the function seems to failed. Tryed the URL recently, but it misleaded me to a wrong page then. Sorry, i'll try a different implementation next week! – Andreas Müller Dec 16 '17 at 17:45
  • Don't know what happens with your python, for me signed_area returns 0.0, under python 2.7 and python 3.6 in this case... – Andreas Müller Dec 19 '17 at 10:09
  • Well, it's a pure copy-paste. Plus, I've written the sum "manually": `print((-1*(-1 - 1) + -2*(2 - 2))/2.0)` and it indeed gives `1.0`. Don't know how you get `0` (where is the difference?). An another hand, I've been overlooking that the question is about polygons and your answer is about rings and maybe I misunderstand something about his. What is their exact definition (a "ring" in maths is not about polygons, as far as I know)? – zezollo Dec 19 '17 at 15:13
  • To keep it short: a ring is a (non-self-intersecting) polygon without holes. The definition for a polygon, e.g. is described in the Simple Feature Definition: http://www.opengeospatial.org/standards/sfa which are used by many dbms or in GIS generally. – Andreas Müller Dec 19 '17 at 15:48