def polygonize_full(self, lines): """Creates polygons from a source of lines, returning the polygons and leftover geometries. The source may be a MultiLineString, a sequence of LineString objects, or a sequence of objects than can be adapted to LineStrings. Returns a tuple of objects: (polygons, dangles, cut edges, invalid ring lines). Each are a geometry collection. Dangles are edges which have one or both ends which are not incident on another edge endpoint. Cut edges are connected at both ends but do not form part of polygon. Invalid ring lines form rings which are invalid (bowties, etc). """ source = getattr(lines, "geoms", None) or lines try: source = iter(source) except TypeError: source = [source] finally: obs = [self.shapeup(l) for l in source] L = len(obs) subs = (c_void_p * L)() for i, g in enumerate(obs): subs[i] = g._geom collection = lgeos.GEOSGeom_createCollection(5, subs, L) dangles = c_void_p() cuts = c_void_p() invalids = c_void_p() product = lgeos.GEOSPolygonize_full(collection, byref(dangles), byref(cuts), byref(invalids)) return (geom_factory(product), geom_factory(dangles), geom_factory(cuts), geom_factory(invalids))
def polygonize_full(self, lines): """Creates polygons from a source of lines, returning the polygons and leftover geometries. The source may be a MultiLineString, a sequence of LineString objects, or a sequence of objects than can be adapted to LineStrings. Returns a tuple of objects: (polygons, dangles, cut edges, invalid ring lines). Each are a geometry collection. Dangles are edges which have one or both ends which are not incident on another edge endpoint. Cut edges are connected at both ends but do not form part of polygon. Invalid ring lines form rings which are invalid (bowties, etc). """ source = getattr(lines, 'geoms', None) or lines try: source = iter(source) except TypeError: source = [source] finally: obs = [self.shapeup(l) for l in source] L = len(obs) subs = (c_void_p * L)() for i, g in enumerate(obs): subs[i] = g._geom collection = lgeos.GEOSGeom_createCollection(5, subs, L) dangles = c_void_p() cuts = c_void_p() invalids = c_void_p() product = lgeos.GEOSPolygonize_full(collection, byref(dangles), byref(cuts), byref(invalids)) return (geom_factory(product), geom_factory(dangles), geom_factory(cuts), geom_factory(invalids))
def polygonize(iterator): """Creates polygons from a list of LineString objects. """ lines = [shapeup(ob) for ob in iterator] geom_array_type = c_void_p * len(lines) geom_array = geom_array_type() for i, line in enumerate(lines): geom_array[i] = line._geom product = lgeos.GEOSPolygonize(byref(geom_array), len(lines)) collection = geom_factory(product) for g in collection.geoms: clone = lgeos.GEOSGeom_clone(g._geom) g = geom_factory(clone) g._owned = False yield g
def parallel_offset(self, distance, side, resolution=16, join_style=JOIN_STYLE.round, mitre_limit=1.0): """Returns a LineString or MultiLineString geometry at a distance from the object on its right or its left side. Distance must be a positive float value. The side parameter may be 'left' or 'right'. The resolution of the buffer around each vertex of the object increases by increasing the resolution keyword parameter or third positional parameter. The join style is for outside corners between line segments. Accepted values are JOIN_STYLE.round (1), JOIN_STYLE.mitre (2), and JOIN_STYLE.bevel (3). The mitre ratio limit is used for very sharp corners. It is the ratio of the distance from the corner to the end of the mitred offset corner. When two line segments meet at a sharp angle, a miter join will extend far beyond the original geometry. To prevent unreasonable geometry, the mitre limit allows controlling the maximum length of the join corner. Corners with a ratio which exceed the limit will be beveled.""" try: return geom_factory(self.impl['parallel_offset']( self, distance, resolution, join_style, mitre_limit, bool(side == 'left'))) except OSError: raise TopologicalError()
def make_valid(ob): """ Make the input geometry valid according to the GEOS MakeValid algorithm. If the input geometry is already valid, then it will be returned. If the geometry must be split into multiple parts of the same type to be made valid, then a multi-part geometry will be returned. If the geometry must be split into multiple parts of different types to be made valid, then a GeometryCollection will be returned. Parameters ---------- ob : Geometry A shapely geometry object which should be made valid. If the object is already valid, it will be returned as-is. Returns ------- Geometry The input geometry, made valid according to the GEOS MakeValid algorithm. """ if ob.is_valid: return ob return geom_factory(lgeos.GEOSMakeValid(ob._geom))
def snap(g1, g2, tolerance): """Snap one geometry to another with a given tolerance Vertices of the first geometry are snapped to vertices of the second geometry. The resulting snapped geometry is returned. The input geometries are not modified. Parameters ---------- g1 : geometry The first geometry g2 : geometry The second geometry tolerence : float The snapping tolerance Example ------- >>> square = Polygon([(1,1), (2, 1), (2, 2), (1, 2), (1, 1)]) >>> line = LineString([(0,0), (0.8, 0.8), (1.8, 0.95), (2.6, 0.5)]) >>> result = snap(line, square, 0.5) >>> result.wkt 'LINESTRING (0 0, 1 1, 2 1, 2.6 0.5)' """ return (geom_factory(lgeos.methods['snap'](g1._geom, g2._geom, tolerance)))
def clip_by_rect(geom, xmin, ymin, xmax, ymax): """Returns the portion of a geometry within a rectangle The geometry is clipped in a fast but possibly dirty way. The output is not guaranteed to be valid. No exceptions will be raised for topological errors. Parameters ---------- geom : geometry The geometry to be clipped xmin : float Minimum x value of the rectangle ymin : float Minimum y value of the rectangle xmax : float Maximum x value of the rectangle ymax : float Maximum y value of the rectangle Notes ----- Requires GEOS >= 3.5.0 New in 1.7. """ if geom.is_empty: return geom result = geom_factory(lgeos.methods['clip_by_rect'](geom._geom, xmin, ymin, xmax, ymax)) return result
def loads(data): """Load a geometry from a WKT string.""" from shapely.geometry.base import geom_factory geom = lgeos.GEOSGeomFromWKT(c_char_p(data.encode('utf-8'))) if not geom: raise ReadingError("Could not create geometry because of errors while reading input.") return geom_factory(geom)
def dumps(ob, hex=False, srid=None, **kw): """Dump a WKB representation of a geometry to a byte string, or a hex-encoded string if ``hex=True``. Parameters ---------- ob : geometry The geometry to export to well-known binary (WKB) representation hex : bool If true, export the WKB as a hexidecimal string. The default is to return a binary string/bytes object. srid : int Spatial reference system ID to include in the output. The default value means no SRID is included. **kw : kwargs See available keyword output settings in ``shapely.geos.WKBWriter``.""" if srid is not None: # clone the object and set the SRID before dumping geom = lgeos.GEOSGeom_clone(ob._geom) lgeos.GEOSSetSRID(geom, srid) ob = geom_factory(geom) kw["include_srid"] = True writer = WKBWriter(lgeos, **kw) if hex: return writer.write_hex(ob) else: return writer.write(ob)
def parallel_offset(self, distance, side, resolution=16, join_style=1, mitre_limit=1.0): """Returns a LineString or MultiLineString geometry at a distance from the object on its right or its left side. Distance must be a positive float value. The side parameter may be 'left' or 'right'. The resolution of the buffer around each vertex of the object increases by increasing the resolution keyword parameter or third positional parameter. The join style is for outside corners between line segments. Accepted values are 1 => ROUND, 2 => MITRE, 3 => BEVEL. The mitre ratio limit is used for very sharp corners. It is the ratio of the distance from the corner to the end of the mitred offset corner. When two line segments meet at a sharp angle, a miter join will extend far beyond the original geometry. To prevent unreasonable geometry, the mitre limit allows controlling the maximum length of the join corner. Corners with a ratio which exceed the limit will be beveled.""" try: return geom_factory( self.impl["parallel_offset"](self, distance, resolution, join_style, mitre_limit, bool(side == "left")) ) except WindowsError: raise TopologicalError()
def snap(g1, g2, tolerance): """Snap one geometry to another with a given tolerance Vertices of the first geometry are snapped to vertices of the second geometry. The resulting snapped geometry is returned. The input geometries are not modified. Parameters ---------- g1 : geometry The first geometry g2 : geometry The second geometry tolerence : float The snapping tolerance Example ------- >>> square = Polygon([(1,1), (2, 1), (2, 2), (1, 2), (1, 1)]) >>> line = LineString([(0,0), (0.8, 0.8), (1.8, 0.95), (2.6, 0.5)]) >>> result = snap(line, square, 0.5) >>> result.wkt 'LINESTRING (0 0, 1 1, 2 1, 2.6 0.5)' """ return(geom_factory(lgeos.methods['snap'](g1._geom, g2._geom, tolerance)))
def parallel_offset( self, distance, side='right', resolution=16, join_style=JOIN_STYLE.round, mitre_limit=5.0): """Returns a LineString or MultiLineString geometry at a distance from the object on its right or its left side. The side parameter may be 'left' or 'right' (default is 'right'). The resolution of the buffer around each vertex of the object increases by increasing the resolution keyword parameter or third positional parameter. If the distance parameter is negative the side is inverted, e.g. distance=5.0, side='left' is the same as distance=-5.0, side='right'. The join style is for outside corners between line segments. Accepted values are JOIN_STYLE.round (1), JOIN_STYLE.mitre (2), and JOIN_STYLE.bevel (3). The mitre ratio limit is used for very sharp corners. It is the ratio of the distance from the corner to the end of the mitred offset corner. When two line segments meet at a sharp angle, a miter join will extend far beyond the original geometry. To prevent unreasonable geometry, the mitre limit allows controlling the maximum length of the join corner. Corners with a ratio which exceed the limit will be beveled.""" if mitre_limit == 0.0: raise ValueError( 'Cannot compute offset from zero-length line segment') try: return geom_factory(self.impl['parallel_offset']( self, distance, resolution, join_style, mitre_limit, side)) except OSError: raise TopologicalError()
def loads(data): """Load a geometry from a WKT string.""" geom = lgeos.GEOSGeomFromWKT(c_char_p(data)) if not geom: raise ReadingError, \ "Could not create geometry because of errors while reading input." return geom_factory(geom)
def loads(data): """Load a geometry from a WKT string.""" from shapely.geometry.base import geom_factory geom = lgeos.GEOSGeomFromWKT(c_char_p(data)) if not geom: raise ReadingError, \ "Could not create geometry because of errors while reading input." return geom_factory(geom)
def linemerge(shape): """ Returns a geometry with lines merged using GEOSLineMerge. """ if shape.type != 'MultiLineString': return shape # copied from shapely.ops.linemerge at http://github.com/sgillies/shapely result = lgeos.GEOSLineMerge(shape._geom) return geom_factory(result)
def voronoi_diagram(geom, envelope=None, tolerance=0.0, edges=False): """ Constructs a Voronoi Diagram [1] from the given geometry. Returns a list of geometries. Parameters ---------- geom: geometry the input geometry whose vertices will be used to calculate the final diagram. envelope: geometry, None clipping envelope for the returned diagram, automatically determined if None. The diagram will be clipped to the larger of this envelope or an envelope surrounding the sites. tolerance: float, 0.0 sets the snapping tolerance used to improve the robustness of the computation. A tolerance of 0.0 specifies that no snapping will take place. edges: bool, False If False, return regions as polygons. Else, return only edges e.g. LineStrings. GEOS documentation can be found at [2] Returns ------- GeometryCollection geometries representing the Voronoi regions. Notes ----- The tolerance `argument` can be finicky and is known to cause the algorithm to fail in several cases. If you're using `tolerance` and getting a failure, try removing it. The test cases in tests/test_voronoi_diagram.py show more details. References ---------- [1] https://en.wikipedia.org/wiki/Voronoi_diagram [2] https://geos.osgeo.org/doxygen/geos__c_8h_source.html (line 730) """ func = lgeos.methods['voronoi_diagram'] envelope = envelope._geom if envelope else None try: result = geom_factory(func(geom._geom, envelope, tolerance, int(edges))) except ValueError: errstr = "Could not create Voronoi Diagram with the specified inputs." if tolerance: errstr += " Try running again with default tolerance value." raise ValueError(errstr) if result.type != 'GeometryCollection': return GeometryCollection([result]) return result
def polygonize(self, lines): """Creates polygons from a source of lines The source may be a MultiLineString, a sequence of LineString objects, or a sequence of objects than can be adapted to LineStrings. """ source = getattr(lines, 'geoms', None) or lines obs = [self.shapeup(l) for l in source] geom_array_type = c_void_p * len(obs) geom_array = geom_array_type() for i, line in enumerate(obs): geom_array[i] = line._geom product = lgeos.GEOSPolygonize(byref(geom_array), len(obs)) collection = geom_factory(product) for g in collection.geoms: clone = lgeos.GEOSGeom_clone(g._geom) g = geom_factory(clone) g._owned = False yield g
def unary_union(self, geoms): """Returns the union of a sequence of geometries This is the most efficient method of dissolving many polygons. """ L = len(geoms) subs = (c_void_p * L)() for i, g in enumerate(geoms): subs[i] = g._geom collection = lgeos.GEOSGeom_createCollection(6, subs, L) return geom_factory(lgeos.GEOSUnaryUnion(collection))
def read(self, text): """Returns geometry from WKT""" if sys.version_info[0] >= 3: text = text.encode('ascii') geom = self._lgeos.GEOSWKTReader_read(self._reader, c_char_p(text)) if not geom: raise ReadingError("Could not create geometry because of errors " "while reading input.") # avoid circular import dependency from shapely.geometry.base import geom_factory return geom_factory(geom)
def cascaded_union(self, geoms): """Returns the union of a sequence of geometries This is the most efficient method of dissolving many polygons. """ L = len(geoms) subs = (c_void_p * L)() for i, g in enumerate(geoms): subs[i] = g._geom collection = lgeos.GEOSGeom_createCollection(6, subs, L) return geom_factory(lgeos.methods['cascaded_union'](collection))
def unary_union(self, geoms): """Returns the union of a sequence of geometries This method replaces :meth:`cascaded_union` as the prefered method for dissolving many polygons. """ L = len(geoms) subs = (c_void_p * L)() for i, g in enumerate(geoms): subs[i] = g._geom collection = lgeos.GEOSGeom_createCollection(6, subs, L) return geom_factory(lgeos.methods['unary_union'](collection))
def read(self, text): """Returns geometry from WKT""" if not isinstance(text, str): raise TypeError("Only str is accepted.") text = text.encode() c_string = c_char_p(text) geom = self._lgeos.GEOSWKTReader_read(self._reader, c_string) if not geom: raise WKTReadingError( "Could not create geometry because of errors " "while reading input.") # avoid circular import dependency from shapely.geometry.base import geom_factory return geom_factory(geom)
def _shapely_normalize(geom): """ Small helper function for now because it is not yet available in Shapely. """ from shapely.geos import lgeos from shapely.geometry.base import geom_factory from ctypes import c_void_p, c_int lgeos._lgeos.GEOSNormalize_r.restype = c_int lgeos._lgeos.GEOSNormalize_r.argtypes = [c_void_p, c_void_p] geom_cloned = lgeos.GEOSGeom_clone(geom._geom) lgeos._lgeos.GEOSNormalize_r(lgeos.geos_handle, geom_cloned) return geom_factory(geom_cloned)
def cascaded_union(self, geoms): """Returns the union of a sequence of geometries This method was superseded by :meth:`unary_union`. """ try: L = len(geoms) except TypeError: geoms = [geoms] L = 1 subs = (c_void_p * L)() for i, g in enumerate(geoms): subs[i] = g._geom collection = lgeos.GEOSGeom_createCollection(6, subs, L) return geom_factory(lgeos.methods['cascaded_union'](collection))
def cascaded_union(self, geoms): """Returns the union of a sequence of geometries This is the most efficient method of dissolving many polygons. """ try: L = len(geoms) except TypeError: geoms = [geoms] L = 1 subs = (c_void_p * L)() for i, g in enumerate(geoms): subs[i] = g._geom collection = lgeos.GEOSGeom_createCollection(6, subs, L) return geom_factory(lgeos.methods["cascaded_union"](collection))
def read(self, text): """Returns geometry from WKT""" if not isinstance(text, text_types): raise TypeError("Only str is accepted.") if sys.version_info[0] >= 3: text = text.encode() c_string = c_char_p(text) geom = self._lgeos.GEOSWKTReader_read(self._reader, c_string) if not geom: raise WKTReadingError( "Could not create geometry because of errors " "while reading input.") # avoid circular import dependency from shapely.geometry.base import geom_factory return geom_factory(geom)
def triangulate(geom, tolerance=0.0, edges=False): """Creates the Delaunay triangulation and returns a list of geometries The source may be any geometry type. All vertices of the geometry will be used as the points of the triangulation. From the GEOS documentation: tolerance is the snapping tolerance used to improve the robustness of the triangulation computation. A tolerance of 0.0 specifies that no snapping will take place. If edges is False, a list of Polygons (triangles) will be returned. Otherwise the list of LineString edges is returned. """ func = lgeos.methods["delaunay_triangulation"] gc = geom_factory(func(geom._geom, tolerance, int(edges))) return [g for g in gc.geoms]
def unary_union(self, geoms): """Returns the union of a sequence of geometries This method replaces :meth:`cascaded_union` as the prefered method for dissolving many polygons. """ try: if isinstance(geoms, BaseMultipartGeometry): geoms = geoms.geoms L = len(geoms) except TypeError: geoms = [geoms] L = 1 subs = (c_void_p * L)() for i, g in enumerate(geoms): subs[i] = g._geom collection = lgeos.GEOSGeom_createCollection(6, subs, L) return geom_factory(lgeos.methods['unary_union'](collection))
def triangulate(geom, tolerance=0.0, edges=False): """Creates the Delaunay triangulation and returns a list of geometries The source may be any geometry type. All vertices of the geometry will be used as the points of the triangulation. From the GEOS documentation: tolerance is the snapping tolerance used to improve the robustness of the triangulation computation. A tolerance of 0.0 specifies that no snapping will take place. If edges is False, a list of Polygons (triangles) will be returned. Otherwise the list of LineString edges is returned. """ func = lgeos.methods['delaunay_triangulation'] gc = geom_factory(func(geom._geom, tolerance, int(edges))) return [g for g in gc.geoms]
def linemerge(self, lines): """Merges all connected lines from a source The source may be a MultiLineString, a sequence of LineString objects, or a sequence of objects than can be adapted to LineStrings. Returns a LineString or MultiLineString when lines are not contiguous. """ source = None if hasattr(lines, 'type') and lines.type == 'MultiLineString': source = lines elif hasattr(lines, '__iter__'): try: source = asMultiLineString([ls.coords for ls in lines]) except AttributeError: source = asMultiLineString(lines) if source is None: raise ValueError("Cannot linemerge %s" % lines) result = lgeos.GEOSLineMerge(source._geom) return geom_factory(result)
def linemerge(self, lines): """Merges all connected lines from a source The source may be a MultiLineString, a sequence of LineString objects, or a sequence of objects than can be adapted to LineStrings. Returns a LineString or MultiLineString when lines are not contiguous. """ source = None if hasattr(lines, "type") and lines.type == "MultiLineString": source = lines elif hasattr(lines, "__iter__"): try: source = asMultiLineString([ls.coords for ls in lines]) except AttributeError: source = asMultiLineString(lines) if source is None: raise ValueError("Cannot linemerge %s" % lines) result = lgeos.GEOSLineMerge(source._geom) return geom_factory(result)
def shared_paths(g1, g2): """Find paths shared between the two given lineal geometries Returns a GeometryCollection with two elements: - First element is a MultiLineString containing shared paths with the same direction for both inputs. - Second element is a MultiLineString containing shared paths with the opposite direction for the two inputs. Parameters ---------- g1 : geometry The first geometry g2 : geometry The second geometry """ if not isinstance(g1, LineString): raise TypeError("First geometry must be a LineString") if not isinstance(g2, LineString): raise TypeError("Second geometry must be a LineString") return (geom_factory(lgeos.methods['shared_paths'](g1._geom, g2._geom)))
def single_sided_buffer(self, distance, leftSide, resolution=16, joinStyle=1, mitreLimit=1.0): """Returns a LineString or MultiLineString geometry at a distance from the object on its right or its left side. Distance must be positive. The side is given by the leftSide parameter. True => LEFT, False => RIGHT The resolution of the buffer around each vertex of the object increases by increasing the resolution keyword parameter or third positional parameter. The join style is for outside corners between line segments. Values are 1 => ROUND, 2 => MITRE, 3 => BEVEL. The mitre ratio limit is used for very sharp corners. It is the ratio of the distance from the corner to the end of the mitred offset corner. When two line segments meet at a sharp angle, a miter join will extend far beyond the original geometry. To prevent unreasonable geometry, the mitre limit allows controlling the maximum length of the join corner. Corners with a ratio which exceed the limit will be beveled. """ try: return geom_factory(self.impl['single_sided_buffer'](self, distance, resolution, joinStyle, mitreLimit, leftSide)) except WindowsError: raise TopologicalError()
def shared_paths(g1, g2): """Find paths shared between the two given lineal geometries Returns a GeometryCollection with two elements: - First element is a MultiLineString containing shared paths with the same direction for both inputs. - Second element is a MultiLineString containing shared paths with the opposite direction for the two inputs. Parameters ---------- g1 : geometry The first geometry g2 : geometry The second geometry """ if not isinstance(g1, LineString): raise TypeError("First geometry must be a LineString") if not isinstance(g2, LineString): raise TypeError("Second geometry must be a LineString") return(geom_factory(lgeos.methods['shared_paths'](g1._geom, g2._geom)))
def cascaded_union(self, geoms): """Returns the union of a sequence of geometries This function is deprecated, as it was superseded by :meth:`unary_union`. """ warn( "The 'cascaded_union()' function is deprecated. " "Use 'unary_union()' instead.", ShapelyDeprecationWarning, stacklevel=2) try: if isinstance(geoms, BaseMultipartGeometry): geoms = geoms.geoms L = len(geoms) except TypeError: geoms = [geoms] L = 1 subs = (c_void_p * L)() for i, g in enumerate(geoms): subs[i] = g._geom collection = lgeos.GEOSGeom_createCollection(6, subs, L) return geom_factory(lgeos.methods['cascaded_union'](collection))
def loads(data): """Load a geometry from a WKB string.""" from shapely.geometry.base import geom_factory return geom_factory(deserialize(data))