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
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] )
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
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
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] )
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
def test_linearring(self): linearring = LinearRing([(0, 0), (0, 1), (1, 0)]) assert orient(linearring, 1) == linearring assert orient(linearring, -1) == linearring
def test_multilinestring(self): multilinestring = MultiLineString( [[(0, 0), (1, 1)], [(1, 0), (0, 1)]] ) assert orient(multilinestring, 1) == multilinestring assert orient(multilinestring, -1) == multilinestring
def test_linestring(self): linestring = LineString(((0, 0), (1, 1))) assert orient(linestring, 1) == linestring assert orient(linestring, -1) == linestring
def test_multipoint(self): multipoint = MultiPoint([(0, 0), (1, 1)]) assert orient(multipoint, 1) == multipoint assert orient(multipoint, -1) == multipoint
def test_point(self): point = Point(0, 0) assert orient(point, 1) == point assert orient(point, -1) == point