from geo.shapefile import signed_area
from geo.shapefile.types import POINT, POINTM, POINTZ
from geo.shapefile.types import MULTIPOINT, MULTIPOINTM, MULTIPOINTZ
from geo.shapefile.types import POLYLINE, POLYLINEM, POLYLINEZ
from geo.shapefile.types import POLYGON, POLYGONM, POLYGONZ


class Shape:
    def __init__(self, shapeType=None):
        """Stores the geometry of the different shape types
        specified in the Shapefile spec. Shape types are
        usually point, polyline, or polygons. Every shape type
        except the "Null" type contains points at some level for
        example verticies in a polygon. If a shape type has
        multiple shapes containing points within a single
        geometry record then those shapes are called parts. Parts
        are designated by their starting index in geometry record's
        list of shapes."""
        self.shapeType = shapeType
        self.points = []

    @property
    def __geo_interface__(self):
        if self.shapeType in [POINT, POINTM, POINTZ]:
            return {
            'type': 'Point',
            'coordinates': tuple(self.points[0])
            }
        elif self.shapeType in [MULTIPOINT, MULTIPOINTM, MULTIPOINTZ]:
            return {
            'type': 'MultiPoint',
            'coordinates': tuple([tuple(p) for p in self.points])
            }
        elif self.shapeType in [POLYLINE, POLYLINEM, POLYLINEZ]:
            if len(self.parts) == 1:
                return {
                'type': 'LineString',
                'coordinates': tuple([tuple(p) for p in self.points])
                }
            else:
                ps = None
                coordinates = []
                for part in self.parts:
                    if ps == None:
                        ps = part
                        continue
                    else:
                        coordinates.append(tuple([tuple(p) for p in self.points[ps:part]]))
                        ps = part
                else:
                    coordinates.append(tuple([tuple(p) for p in self.points[part:]]))
                return {
                'type': 'MultiLineString',
                'coordinates': tuple(coordinates)
                }
        elif self.shapeType in [POLYGON, POLYGONM, POLYGONZ]:
            if len(self.parts) == 1:
                return {
                'type': 'Polygon',
                'coordinates': (tuple([tuple(p) for p in self.points]),)
                }
            else:
                ps = None
                coordinates = []
                for part in self.parts:
                    if ps == None:
                        ps = part
                        continue
                    else:
                        coordinates.append(tuple([tuple(p) for p in self.points[ps:part]]))
                        ps = part
                else:
                    coordinates.append(tuple([tuple(p) for p in self.points[part:]]))
                polys = []
                poly = [coordinates[0]]
                for coord in coordinates[1:]:
                    if signed_area(coord) < 0:
                        polys.append(poly)
                        poly = [coord]
                    else:
                        poly.append(coord)
                polys.append(poly)
                if len(polys) == 1:
                    return {
                    'type': 'Polygon',
                    'coordinates': tuple(polys[0])
                    }
                elif len(polys) > 1:
                    return {
                    'type': 'MultiPolygon',
                    'coordinates': polys
                    }

@@ -290,16 +290,24 @@
 ss Shape
+(object)
 :%0A    de
@@ -1049,38 +1049,26 @@
 urn 
-%7B%0A            '
+dict(
 type
-': 
+=
 'Point',
 %0A   
@@ -1059,38 +1059,25 @@
 ype='Point',
-%0A            '
+ 
 coordinates'
@@ -1071,27 +1071,25 @@
  coordinates
-': 
+=
 tuple(self.p
@@ -1097,30 +1097,17 @@
 ints%5B0%5D)
-%0A            %7D
+)
 %0A       
@@ -1193,30 +1193,18 @@
 urn 
-%7B%0A            '
+dict(
 type
-': 
+=
 'Mul
@@ -1216,22 +1216,9 @@
 nt',
-%0A            '
+ 
 coor
@@ -1216,35 +1216,33 @@
 nt', coordinates
-': 
+=
 tuple(%5Btuple(p) 
@@ -1267,22 +1267,9 @@
 ts%5D)
-%0A            %7D
+)
 %0A   
@@ -1394,34 +1394,18 @@
 urn 
-%7B%0A                '
+dict(
 type
-': 
+=
 'Lin
@@ -1413,34 +1413,17 @@
 String',
-%0A                '
+ 
 coordina
@@ -1421,27 +1421,25 @@
  coordinates
-': 
+=
 tuple(%5Btuple
@@ -1464,34 +1464,17 @@
 points%5D)
-%0A                %7D
+)
 %0A       
@@ -1972,34 +1972,18 @@
 urn 
-%7B%0A                '
+dict(
 type
-': 
+=
 'Mul
@@ -1996,34 +1996,17 @@
 String',
-%0A                '
+ 
 coordina
@@ -2004,27 +2004,25 @@
  coordinates
-': 
+=
 tuple(coordi
@@ -2027,34 +2027,17 @@
 dinates)
-%0A                %7D
+)
 %0A       
@@ -2155,34 +2155,18 @@
 urn 
-%7B%0A                '
+dict(
 type
-': 
+=
 'Pol
@@ -2175,26 +2175,9 @@
 on',
-%0A                '
+ 
 coor
@@ -2183,19 +2183,17 @@
 rdinates
-': 
+=
 (tuple(%5B
@@ -2229,26 +2229,9 @@
 %5D),)
-%0A                %7D
+)
 %0A   
@@ -3119,38 +3119,18 @@
 urn 
-%7B%0A                    '
+dict(
 type
-': 
+=
 'Pol
@@ -3135,38 +3135,17 @@
 olygon',
-%0A                    '
+ 
 coordina
@@ -3147,19 +3147,17 @@
 rdinates
-': 
+=
 tuple(po
@@ -3163,38 +3163,17 @@
 olys%5B0%5D)
-%0A                    %7D
+)
 %0A       
@@ -3233,38 +3233,18 @@
 urn 
-%7B%0A                    '
+dict(
 type
-': 
+=
 'Mul
@@ -3258,30 +3258,9 @@
 on',
-%0A                    '
+ 
 coor
@@ -3270,35 +3270,12 @@
 ates
-': 
+=
 polys
-%0A                    %7D
+)
 %0A

Trim down shape geo_interface returns