Example #1
0
 def test_polygon(self):
     polygon = Polygon([(0, 0), (0, 1), (1, 0)])
     polygon_reversed = Polygon(
         polygon.exterior.coords[::-1]
     )
     assert (orient(polygon, 1)) == polygon_reversed
     assert (orient(polygon, -1)) == polygon
Example #2
0
 def test_geometrycollection(self):
     polygon = Polygon([(0, 0), (0, 1), (1, 0)])
     polygon_reversed = Polygon(
         polygon.exterior.coords[::-1]
     )
     collection = GeometryCollection([polygon])
     assert orient(collection, 1) == GeometryCollection(
         [polygon_reversed]
     )
     assert orient(collection, -1) == GeometryCollection(
         [polygon]
     )
Example #3
0
 def orient(geom, sign=1.0):
     if isinstance(geom, BaseMultipartGeometry):
         return geom.__class__(
             list(map(lambda geom: orient(geom, sign), geom.geoms)))
     if isinstance(geom, (Polygon, )):
         return orient_(geom, sign)
     return geom
Example #4
0
def winding_order(geom, order="CW_CCW"):
    """
    Function that force a certain winding order on the resulting output geometries. One
    can choose between `CCW_CW` and `CW_CCW`.

    `CW_CCW` implies clockwise for exterior polygons and counterclockwise for interior
    polygons (aka the geographical right-hand-rule where the right hand is in the area
    of interest as you walk the line).

    `CCW_CW` implies counterclockwise for exterior polygons and clockwise for interior
    polygons (aka the mathematical right-hand-rule where the right hand curls around
    the polygon's exterior with your thumb pointing "up" (toward space), signing a
    positive area for the polygon in the signed area sense).

    TopoJSON, and so this package, defaults to `CW_CCW`, but depending on the
    application you might decide differently.

    * https://bl.ocks.org/mbostock/a7bdfeb041e850799a8d3dce4d8c50c8

    Only applies to Polygons and MultiPolygons.

    Parameters
    ----------
    geom : geometry or shapely.geometry.GeometryCollection
        Geometry objects where the winding order will be forced upon.
    order : str, optional
        Choose `CW_CCW` for clockwise for exterior- and counterclockwise for
        interior polygons or `CCW_CW` for counterclockwise for exterior- and clockwise
        for interior polygons, by default `CW_CCW`.

    Returns
    -------
    geometry or shapely.geometry.GeometryCollection
        Geometry objects where the chosen winding order is forced upon.
    """

    # CW_CWW will orient the outer polygon clockwise and the inner polygon counter-
    # clockwise to conform TopoJSON standard

    if order == "CW_CCW":
        geom = orient(geom, sign=-1.0)
    elif order == "CCW_CW":
        geom = orient(geom, sign=1.0)
    else:
        raise NameError("parameter {} was not recognized".format(order))

    return geom
Example #5
0
 def test_multipolygon(self):
     polygon1 = Polygon([(0, 0), (0, 1), (1, 0)])
     polygon2 = Polygon([(1, 0), (2, 0), (2, 1)])
     polygon1_reversed = Polygon(
         polygon1.exterior.coords[::-1]
     )
     polygon2_reversed = Polygon(
         polygon2.exterior.coords[::-1]
     )
     multipolygon = MultiPolygon([polygon1, polygon2])
     assert not polygon1.exterior.is_ccw
     assert polygon2.exterior.is_ccw
     assert orient(multipolygon, 1) == MultiPolygon(
         [polygon1_reversed, polygon2]
     )
     assert orient(multipolygon, -1) == MultiPolygon(
         [polygon1, polygon2_reversed]
     )
Example #6
0
    def as_geojson(self, hold_crs=False):
        """ Return Feature as GeoJSON formatted dict
        Args:
            hold_crs (bool): serialize with current projection, that could be not ESPG:4326 (which is standards violation)
        Returns:
            GeoJSON formatted dict
        """
        if self.crs != CRS_LATLON and not hold_crs:
            f = self.reproject(CRS_LATLON)
        else:
            f = self

        shape = f.shape
        if shape.is_empty:
            # Empty geometries are not allowed in FeatureCollections,
            # but here it may occur due to reprojection which can eliminate small geiometries
            # This case is processed separately as orient(POLYGON_EMPTY) raises an exception
            # TODO: do not return anything on empty polygon and ignore such features in FeatureCollection.geojson
            shape = Polygon()
        else:
            try:
                shape = orient(shape)
            except Exception as e:
                # Orientation is really not a crucial step, it follows the geojson standard,
                # but not oriented polygons can be read by any instrument. So, ni case of any troubles with orientation
                # we just fall back to not-oriented version of the same geometry
                warnings.warn(
                    f'Polygon orientation failed: {str(e)}. Returning initial shape instead',
                    RuntimeWarning)
                shape = f.shape

        f = Feature(shape, properties=f.properties)
        data = {
            'type': 'Feature',
            'geometry': f.geometry,
            'properties': f.properties
        }
        return data
Example #7
0
 def test_linearring(self):
     linearring = LinearRing([(0, 0), (0, 1), (1, 0)])
     assert orient(linearring, 1) == linearring
     assert orient(linearring, -1) == linearring
Example #8
0
 def test_multilinestring(self):
     multilinestring = MultiLineString(
         [[(0, 0), (1, 1)], [(1, 0), (0, 1)]]
     )
     assert orient(multilinestring, 1) == multilinestring
     assert orient(multilinestring, -1) == multilinestring
Example #9
0
 def test_linestring(self):
     linestring = LineString(((0, 0), (1, 1)))
     assert orient(linestring, 1) == linestring
     assert orient(linestring, -1) == linestring
Example #10
0
 def test_multipoint(self):
     multipoint = MultiPoint([(0, 0), (1, 1)])
     assert orient(multipoint, 1) == multipoint
     assert orient(multipoint, -1) == multipoint
Example #11
0
 def test_point(self):
     point = Point(0, 0)
     assert orient(point, 1) == point
     assert orient(point, -1) == point