def test_polygonize_full(): lines = [ None, pygeos.Geometry("LINESTRING (0 0, 1 1)"), pygeos.Geometry("LINESTRING (0 0, 0 1)"), pygeos.Geometry("LINESTRING (0 1, 1 1)"), pygeos.Geometry("LINESTRING (1 1, 1 0)"), None, pygeos.Geometry("LINESTRING (1 0, 0 0)"), pygeos.Geometry("LINESTRING (5 5, 6 6)"), pygeos.Geometry("LINESTRING (1 1, 100 100)"), pygeos.Geometry("POINT (0 0)"), None, ] result = pygeos.polygonize_full(lines) assert len(result) == 4 assert all(pygeos.get_type_id(geom) == 7 for geom in result) # GeometryCollection polygons, cuts, dangles, invalid = result expected_polygons = pygeos.Geometry( "GEOMETRYCOLLECTION (POLYGON ((0 0, 1 1, 1 0, 0 0)), POLYGON ((1 1, 0 0, 0 1, 1 1)))" ) assert polygons == expected_polygons assert cuts == pygeos.Geometry("GEOMETRYCOLLECTION EMPTY") expected_dangles = pygeos.Geometry( "GEOMETRYCOLLECTION (LINESTRING (1 1, 100 100), LINESTRING (5 5, 6 6))" ) assert dangles == expected_dangles assert invalid == pygeos.Geometry("GEOMETRYCOLLECTION EMPTY")
def _dense_point_array(self, geoms, distance, index): """ geoms - array of pygeos lines """ # interpolate lines to represent them as points for Voronoi points = np.empty((0, 2)) ids = [] if pygeos.get_type_id(geoms[0]) not in [1, 2, 5]: lines = pygeos.boundary(geoms) else: lines = geoms lengths = pygeos.length(lines) for ix, line, length in zip(index, lines, lengths): if length > distance: # some polygons might have collapsed pts = pygeos.line_interpolate_point( line, np.linspace(0.1, length - 0.1, num=int((length - 0.1) // distance)), ) # .1 offset to keep a gap between two segments points = np.append(points, pygeos.get_coordinates(pts), axis=0) ids += [ix] * len(pts) return points, ids
def merge_lines(df, by): """Use GEOS line merge to merge MultiLineStrings into LineStrings (where possible). This uses aggregate_lines first to aggregate lines to MultiLineStrings. WARNING: this can be a bit slow. Parameters ---------- df : GeoDataFrame by : string or list-like field(s) to aggregate by Returns ------- GeoDataFrame of LineStrings or MultiLinestrings (if required) """ agg = aggregate_lines(df, by) agg["geometry"] = pg.line_merge(agg.geometry.values.data) geom_type = pg.get_type_id(agg["geometry"].values.data) ix = geom_type == 5 if ix.sum() > 0: agg.loc[~ix, "geometry"] = pg.multilinestrings( agg.loc[~ix].geometry.values.data, np.arange((~ix).sum())) return agg
def transform_geometry(self, geom, rs, max_points=5): """Transforms a geometry embedding new points. In case geom is (multi)line or (multi)polygon, it adds points collinear to their neighbours, so that an equivalent geometry is generated. The number of extra points depends on the number of vertices in the geometry. Arguments: geom (pygeos.Geometry): Geometry rs (numpy.RandomState): Random State max_points (int): Maximum value of extra points. Returns: (pygeos.Geometry) Raises: ValueError: When geometry type is not supported. """ type_ = pg.get_type_id(geom) if type_ == 1 or type_ == 3: # LINESTRING or POLYGON vertices = pg.get_coordinates(geom) size = min(max_points, math.ceil(len(vertices) / 6)) vert_ids = rs.randint(1, len(vertices), size) vert_ids.sort() new = [] for idx in vert_ids: xa, ya = vertices[idx - 1] xb, yb = vertices[idx] if xa == xb: x = xa y = self._random_float(rs, ya, yb) else: x = self._random_float(rs, xa, xb) y = (yb - ya) * (x - xa) / (xb - xa) + ya x = _round(x, [xa, xb]) y = _round(y, [ya, yb]) new.append((idx, [x, y])) offset = 0 extended = [] for idx, entry in new: extended.extend(vertices[offset:idx]) extended.append(entry) offset = idx extended.extend(vertices[offset:]) extended = np.array(extended) result = pg.linestrings(extended) if type_ == 1 else pg.polygons( extended) elif type_ == 5 or type_ == 6: # MULTILINESTRING or MULTIPOLYGON parts = pg.get_parts(geom) part_idx = rs.randint(0, len(parts)) parts[part_idx] = self.transform_geometry(parts[part_idx], rs) result = pg.multilinestrings( parts) if type_ == 5 else pg.multipolygons(parts) else: raise ValueError( 'geom should be linestring, polygon, multilinestring, or multipolygon.' ) return result
def cut_line_at_points(line, cut_points, tolerance=1e-6): """Cut a pygeos line geometry at points. If there are no interior points, the original line will be returned. Parameters ---------- line : pygeos Linestring cut_points : list-like of pygeos Points will be projected onto the line; those interior to the line will be used to cut the line in to new segments. tolerance : float, optional (default: 1e-6) minimum distance from endpoints to consider the points interior to the line. Returns ------- MultiLineStrings (or LineString, if unchanged) """ if not pg.get_type_id(line) == 1: raise ValueError("line is not a single linestring") vertices = pg.get_point(line, range(pg.get_num_points(line))) offsets = pg.line_locate_point(line, vertices) cut_offsets = pg.line_locate_point(line, cut_points) # only keep those that are interior to the line and ignore those very close # to endpoints or beyond endpoints cut_offsets = cut_offsets[(cut_offsets > tolerance) & (cut_offsets < offsets[-1] - tolerance)] if len(cut_offsets) == 0: # nothing to cut, return original return line # get coordinates of new vertices from the cut points (interpolated onto the line) cut_offsets.sort() # add in the last coordinate of the line cut_offsets = np.append(cut_offsets, offsets[-1]) # TODO: convert this to a pygos ufunc coords = pg.get_coordinates(line) cut_coords = pg.get_coordinates( pg.line_interpolate_point(line, cut_offsets)) lines = [] orig_ix = 0 for cut_ix in range(len(cut_offsets)): offset = cut_offsets[cut_ix] segment = [] if cut_ix > 0: segment = [cut_coords[cut_ix - 1]] while offsets[orig_ix] < offset: segment.append(coords[orig_ix]) orig_ix += 1 segment.append(cut_coords[cut_ix]) lines.append(pg.linestrings(segment)) return pg.multilinestrings(lines)
def test_pickle(geom): if pygeos.get_type_id(geom) == 2: # Linearrings get converted to linestrings expected = pygeos.linestrings(pygeos.get_coordinates(geom)) else: expected = geom pickled = pickle.dumps(geom) assert pygeos.equals_exact(pickle.loads(pickled), expected)
def _harmonize_to_multitype(geoseries: gpd.GeoSeries, dest_geometrytype: GeometryType) -> gpd.GeoSeries: # Copy geoseries to pygeos array geometries_arr = geoseries.array.data.copy() # type: ignore # Set empty geometries to None empty_idxs = pg.get_type_id(geometries_arr) == 7 if empty_idxs.sum(): geometries_arr[empty_idxs] = None # Cast all geometries that are not of the correct multitype yet # Remark: all rows need to be retained, so the same indexers exist in the # returned geoseries if dest_geometrytype is GeometryType.MULTIPOLYGON: # Convert polygons to multipolygons single_idxs = pg.get_type_id(geometries_arr) == 3 if single_idxs.sum(): geometries_arr[single_idxs] = np.apply_along_axis( pg.multipolygons, arr=(np.expand_dims(geometries_arr[single_idxs], 1)), axis=1) elif dest_geometrytype is GeometryType.MULTILINESTRING: # Convert linestrings to multilinestrings single_idxs = pg.get_type_id(geometries_arr) == 1 if single_idxs.sum(): geometries_arr[single_idxs] = np.apply_along_axis( pg.multilinestrings, arr=(np.expand_dims(geometries_arr[single_idxs], 1)), axis=1) elif dest_geometrytype is GeometryType.MULTIPOINT: single_idxs = pg.get_type_id(geometries_arr) == 0 if single_idxs.sum(): geometries_arr[single_idxs] = np.apply_along_axis( pg.multipoints, arr=(np.expand_dims(geometries_arr[single_idxs], 1)), axis=1) else: raise Exception( f"Unsupported destination GeometryType: {dest_geometrytype}") # Prepare result to return geoseries_result = geoseries.copy() geoseries_result.array.data = geometries_arr # type: ignore assert isinstance(geoseries_result, gpd.GeoSeries) return geoseries_result
def test_float_arg_array(geometry, func): if func is pygeos.offset_curve and pygeos.get_type_id(geometry) not in [1, 2]: with pytest.raises(GEOSException, match="only accept linestrings"): func([geometry, geometry], 0.0) return actual = func([geometry, geometry], 0.0) assert actual.shape == (2,) assert isinstance(actual[0], Geometry)
def test_get_rings(geom): if (pygeos.get_type_id(geom) != pygeos.GeometryType.POLYGON) or pygeos.is_empty(geom): rings = pygeos.get_rings(geom) assert len(rings) == 0 else: rings = pygeos.get_rings(geom) assert len(rings) == 1 assert rings[0] == pygeos.get_exterior_ring(geom)
def geom_type(self): idx = pygeos.get_type_id(self.g) return [ "None", "Point", "LineString", "LinearRing", "Polygon", "MultiPoint", "MultiLineString", "MultiPolygon", "GeometryCollection", ][idx]
def is_type(x, *geometry_types): """Return whether geometries in the provided array are of one of the provided types. Args: x (numpy.ndarray): A 1D ``[geometry]`` array. *geometry_types (GeometryType): A geometry type. Returns: numpy.ndarray: A boolean array of whether geometries are of any of the provided``geometry_types``. """ types = get_type_id(x) return sum(types == geometry_type.value for geometry_type in geometry_types).astype(bool)
def _pygeos_to_shapely(geom): if geom is None: return None if compat.PYGEOS_SHAPELY_COMPAT: geom = shapely.geos.lgeos.GEOSGeom_clone(geom._ptr) return shapely.geometry.base.geom_factory(geom) # fallback going through WKB if pygeos.is_empty(geom) and pygeos.get_type_id(geom) == 0: # empty point does not roundtrip through WKB return shapely.wkt.loads("POINT EMPTY") else: return shapely.wkb.loads(pygeos.to_wkb(geom))
def test_polygonize(): lines = [ pygeos.Geometry("LINESTRING (0 0, 1 1)"), pygeos.Geometry("LINESTRING (0 0, 0 1)"), pygeos.Geometry("LINESTRING (0 1, 1 1)"), pygeos.Geometry("LINESTRING (1 1, 1 0)"), pygeos.Geometry("LINESTRING (1 0, 0 0)"), pygeos.Geometry("LINESTRING (5 5, 6 6)"), pygeos.Geometry("POINT (0 0)"), None, ] result = pygeos.polygonize(lines) assert pygeos.get_type_id(result) == 7 # GeometryCollection expected = pygeos.Geometry( "GEOMETRYCOLLECTION (POLYGON ((0 0, 1 1, 1 0, 0 0)), POLYGON ((1 1, 0 0, 0 1, 1 1)))" ) assert result == expected
def to_dict(geometry): """Convert pygeos Geometry object to a dictionary representation. Equivalent to structure of GeoJSON. Parameters ---------- geometry : pygeos Geometry object (singular) Returns ------- dict GeoJSON dict representation of geometry """ geometry = pg.normalize(geometry) def get_ring_coords(polygon): # outer ring must be reversed to be counterclockwise[::-1] coords = [pg.get_coordinates(pg.get_exterior_ring(polygon)).tolist()] for i in range(pg.get_num_interior_rings(polygon)): # inner rings must be reversed to be clockwise[::-1] coords.append( pg.get_coordinates(pg.get_interior_ring(polygon, i)).tolist()) return coords geom_type = GEOJSON_TYPE[pg.get_type_id(geometry)] coords = [] if geom_type == "MultiPolygon": coords = [] geoms = pg.get_geometry(geometry, range(pg.get_num_geometries(geometry))) for geom in geoms: coords.append(get_ring_coords(geom)) elif geom_type == "Polygon": coords = get_ring_coords(geometry) else: raise NotImplementedError("Not built") return {"type": geom_type, "coordinates": coords}
def test_subclasses(with_point_in_registry): for point in [Point("POINT (1 1)"), pygeos.points(1, 1)]: assert isinstance(point, Point) assert pygeos.get_type_id(point) == pygeos.GeometryType.POINT assert point.x == 1
def type_id(self): return pygeos.get_type_id(self)
def union_or_combine(geometries, grid_size=None, op="union"): """First does a check for overlap of geometries according to STRtree intersects. If any overlap, then will use union_all on all of them; otherwise will return as a multipolygon. If only one polygon is present, it will be returned in a MultiPolygon. If coverage_union op is provided, geometries must be polygons and topologically related or this will produce bad output or fail outright. See docs for coverage_union in GEOS. Parameters ---------- geometries : ndarray of single part polygons grid_size : [type], optional (default: None) provided to union_all; otherwise no effect op : str, one of {'union', 'coverage_union'} Returns ------- MultiPolygon """ if not (pg.get_type_id(geometries) == 3).all(): print("Inputs to union or combine must be single-part geometries") if len(geometries) == 1: return pg.multipolygons(geometries) tree = pg.STRtree(geometries) left, right = tree.query_bulk(geometries, predicate="intersects") # drop self intersections ix = left != right left = left[ix] right = right[ix] # no intersections, just combine parts if len(left) == 0: return pg.multipolygons(geometries) # find groups of contiguous geometries and union them together individually contiguous = np.sort(np.unique(np.concatenate([left, right]))) discontiguous = np.setdiff1d(np.arange(len(geometries), dtype="uint"), contiguous) groups = find_adjacent_groups(left, right) parts = [] if op == "coverage_union": for group in groups: parts.extend( pg.get_parts(pg.coverage_union_all(geometries[list(group)]))) else: for group in groups: parts.extend( pg.get_parts( pg.union_all(geometries[list(group)], grid_size=grid_size))) parts.extend(pg.get_parts(geometries[discontiguous])) return pg.multipolygons(parts)
def rotate(self, *angles, origin): r""" Performs a 2D or 3D rotation on all the coordinates. 2D .. math:: \begin{bmatrix} x' \\ y' \\ 1 \end{bmatrix} &= \begin{bmatrix} cos(a) & -sin(a) & x_{off} \\ sin(a) & cos(a) & y_{off} \\ 0 & 0 & 1 \\ \end{bmatrix} \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} \\ x_{off} &= x_{origin} - x_{origin}*cos(a) + y_{origin}*sin(a) \\ y_{off} &= y_{origin} - x_{origin}*sin(a) - y_{origin}*cos(a) 3D .. math:: \begin{bmatrix} x' \\ y' \\ z' \\ 1 \end{bmatrix} &= \begin{bmatrix} cos(a_z)*cos(a_y) & cos(a_z)*sin(a_y)*sin(a_x) - sin(a_z)*cos(a_x) & cos(a_z)*sin(a_y)*cos(a_x) + sin(a_z)*sin(a_x) & x_{off} \\ sin(a_z)*cos(a_y) & sin(a_z)*sin(a_y)*sin(a_x) + cos(a_z)*cos(a_x) & sin(a_z)*sin(a_y)*cos(a_x) - cos(a_z)*sin(a_x) & y_{off} \\ -sin(a_y) & cos(a_y)*sin(a_x) & cos(a_y)*cos(a_x) & z_{off} \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} \\ x_{off} &= x_{origin} - (a)*x_{origin} - (b)*y_{origin} - (c)*z_{origin} \\ y_{off} &= y_{origin} - (d)*x_{origin} - (e)*y_{origin} - (f)*z_{origin} \\ z_{off} &= z_{origin} - (g)*x_{origin} - (h)*y_{origin} - (i)*z_{origin} Args: angles (float): 2D rotation angle or X,Y,Z 3D rotation angles in radians. origin (pygeos.lib.Geometry or list-like): Origin point for the transformation. Returns: pandas.Series: Transformed geometries. """ if origin is None: origin = (0, 0, 0) elif isinstance(origin, pygeos.lib.Geometry): if pygeos.get_type_id(origin) != 0: raise TypeError('Origin should be a single point geometry') origin = np.nan_to_num(pygeos.get_coordinates(origin, True)).squeeze() if len(angles) == 1: x0, y0 = origin[:2] ca = cos(angles[0]) sa = sin(angles[0]) result = self._obj.array.affine(( ca, -sa, sa, ca, x0 - x0 * ca + y0 * sa, y0 - x0 * sa - y0 * ca, )) elif len(angles) == 3: x0, y0, z0 = origin[:3] cx, cy, cz = (cos(a) for a in angles) sx, sy, sz = (sin(a) for a in angles) a = cz * cy b = cz * sy * sx - sz * cx c = cz * sy * cx + sz * sx d = sz * cy e = sz * sy * sx + cz * cx f = sz * sy * cx - cz * sx g = -sy h = cy * sx i = cy * cx result = self._obj.array.affine(( a, b, c, d, e, f, g, h, i, x0 - a * x0 - b * y0 - c * z0, y0 - d * x0 - e * y0 - f * z0, z0 - g * x0 - h * y0 - i * z0, )) else: raise ValueError( 'The rotate transformation requires 1 or 3 angles') return pd.Series(result, index=self._obj.index, name='rotate')
def test_get_exterior_ring(): actual = pygeos.get_exterior_ring([polygon, polygon_with_hole]) assert (pygeos.get_type_id(actual) == 2).all()
def test_get_type_id(): actual = pygeos.get_type_id(all_types).tolist() assert actual == [0, 1, 2, 3, 4, 5, 6, 7, 7]
def scale(self, x, y, z=None, *, origin=None): r""" Performs a 2D or 3D scaling on all the coordinates. 2D .. math:: \begin{bmatrix} x' \\ y' \\ 1 \end{bmatrix} &= \begin{bmatrix} x_s & 0 & x_{off} \\ 0 & y_s & y_{off} \\ 0 & 0 & 1 \\ \end{bmatrix} \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} \\ x_{off} &= x_{origin} - x_{origin}*x_s \\ y_{off} &= y_{origin} - y_{origin}*y_s 3D .. math:: \begin{bmatrix} x' \\ y' \\ z' \\ 1 \end{bmatrix} &= \begin{bmatrix} x_s & 0 & 0 & x_{off} \\ 0 & y_s & 0 & y_{off} \\ 0 & 0 & z_s & z_{off} \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} \\ x_{off} &= x_{origin} - x_{origin}*x_s \\ y_{off} &= y_{origin} - y_{origin}*y_s \\ z_{off} &= z_{origin} - z_{origin}*z_s Args: x (float): Scaling value in the X direction. y (float): Scaling value in the Y direction. z (float, optional): Scaling value in the Z direction; Default **None**. origin (pygeos.lib.Geometry or list-like): Origin point for the transformation. Returns: pandas.Series: Transformed geometries. """ if origin is None: origin = (0, 0, 0) elif isinstance(origin, pygeos.lib.Geometry): if pygeos.get_type_id(origin) != 0: raise TypeError('Origin should be a single point geometry') origin = np.nan_to_num(pygeos.get_coordinates(origin, True)).squeeze() if z is None: x0, y0 = origin[:2] result = self._obj.array.affine(( x, 0, 0, y, x0 - x * x0, y0 - y * y0, )) else: x0, y0, z0 = origin[:3] result = self._obj.array.affine(( x, 0, 0, 0, y, 0, 0, 0, z, x0 - x * x0, y0 - y * y0, z0 - z * z0, )) return pd.Series(result, index=self._obj.index, name='scale')
def skew(self, *angles, origin=None): r""" Performs a 2D or 3D skew/shear transformation on all the coordinates. 2D .. math:: \begin{bmatrix} x' \\ y' \\ 1 \end{bmatrix} &= \begin{bmatrix} 1 & a_{xy} & x_{off} \\ a_{yx} & 1 & y_{off} \\ 0 & 0 & 1 \\ \end{bmatrix} \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} \\ x_{off} &= -(y_{origin}*a_{xy}) \\ y_{off} &= -(x_{origin}*a_{yx}) 3D .. math:: \begin{bmatrix} x' \\ y' \\ z' \\ 1 \end{bmatrix} &= \begin{bmatrix} 1 & a_{xy} & a_{xz} & x_{off} \\ a_{yx} & 1 & a_{yz} & y_{off} \\ a_{zx} & a_{zy} & 1 & z_{off} \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} \\ x_{off} &= -(y_{origin}*a_{xy} + z_{origin}*a_{xz}) \\ y_{off} &= -(x_{origin}*a_{yx} + z_{origin}*a_{yz}) \\ z_{off} &= -(x_{origin}*a_{zx} + y_{origin}*a_{zy}) Args: angles (float): skewing angles (2D: ``[x, y]`` ; 3D: ``[xy, xz, yx, yz, zx, zy]``) origin (pygeos.lib.Geometry or list-like): Origin point for the transformation. Returns: pandas.Series: Transformed geometries. """ if origin is None: origin = (0, 0, 0) elif isinstance(origin, pygeos.lib.Geometry): if pygeos.get_type_id(origin) != 0: raise TypeError('Origin should be a single point geometry') origin = np.nan_to_num(pygeos.get_coordinates(origin, True)).squeeze() if len(angles) == 2: x0, y0 = origin[:2] x, y = (tan(a) for a in angles) result = self._obj.array.affine(( 1, x, y, 1, -(y0 * x), -(x0 * y), )) elif len(angles) == 6: x0, y0, z0 = origin[:3] xy, xz, yx, yz, zx, zy = (tan(a) for a in angles) result = self._obj.array.affine(( 1, xy, xz, yx, 1, yz, zx, zy, 1, -(y0 * xy + z0 * xz), -(x0 * yx + z0 * yz), -(x0 * zx + y0 * zy), )) else: raise ValueError('The skew transformation requires 2 or 6 angles') return pd.Series(result, index=self._obj.index, name='skew')
pd.DataFrame( sjoin_geometry( pd.Series(dams.geometry.values.data, index=dams.index), pd.Series(flowlines.geometry.values.data, flowlines.index), ).rename("lineID") ) .reset_index() .join(dams.geometry, on="damID") .join(flowlines.geometry.rename("flowline"), on="lineID") ).reset_index(drop=True) print(f"Found {len(dams):,} joins in {time() - join_start:.2f}s") print("Extracting intersecting flowlines...") # Only keep the joins for lines that significantly cross (have a line / multiline and not a point) clipped = pg.intersection(dams.geometry.values.data, dams.flowline.values.data) t = pg.get_type_id(clipped) dams = dams.loc[(t == 1) | (t == 5)].copy() # find all joins for lines that start or end at these dams j = find_joins( joins, dams.lineID.unique(), downstream_col="downstream_id", upstream_col="upstream_id", ) def find_upstreams(ids): if len(ids) == 1: return ids # multiple segments, find the dowstream ones
{"break_geometry": breaks.take(left)}, index=df.index.take(right) ) grouped = pairs.groupby(level=0).break_geometry.apply( lambda g: pg.multipolygons(g.values.data) ) df.loc[grouped.index, "geometry"] = pg.difference( df.loc[grouped.index].geometry.values.data, grouped.values ) df = explode(df).reset_index(drop=True) # make sure all polygons are valid ix = ~pg.is_valid(df.geometry.values.data) if ix.sum(): print(f"Repairing {ix.sum()} invalid waterbodies") df.loc[ix, "geometry"] = pg.make_valid(df.loc[ix].geometry.values.data) df = explode(explode(df)) df = df.loc[pg.get_type_id(df.geometry.values.data) == 3].reset_index() # assign a new unique wbID df["wbID"] = df.index.values.astype("uint32") + 1 + int(huc2) * 1000000 df["km2"] = pg.area(df.geometry.values.data) / 1e6 df.to_feather(huc2_dir / "waterbodies.feather") write_dataframe(df, huc2_dir / "waterbodies.gpkg") print("--------------------") print(f"HUC2: {huc2} done in {time() - huc2_start:.0f}s\n\n") print(f"Done in {time() - start:.2f}s\n============================")
def test_from_wkb_point_empty(wkb, expected_type): geom = pygeos.from_wkb(wkb) # POINT (nan nan) transforms to an empty point # Note that the dimensionality (2D/3D) is GEOS-version dependent assert pygeos.is_empty(geom) assert pygeos.get_type_id(geom) == expected_type
# set the CRS, it is same as 5070 but not recognized properly df = df.set_crs(DATA_CRS) # drop BOEM lease block groups df = df.loc[df.Agg_Src != "USGS_PADUS2_0Marine_BOEM_Block_Dissolve"].drop( columns=["Agg_Src"]) tree = pg.STRtree(df.geometry.values.data) ix = tree.query(bnd_df.geometry.values.data[0], predicate="intersects") df = df.iloc[ix].copy() print("making valid...") df["geometry"] = pg.make_valid(df.geometry.values.data) df = explode(df).reset_index() # there are some geometry errors after cleaning up above, keep only polys df = df.loc[pg.get_type_id(df.geometry.values.data) == 3].copy() print("Writing files") df.to_feather(out_dir / "ownership.feather") write_dataframe(df, data_dir / "boundaries/ownership.gpkg", driver="GPKG") # Write for tiles print("Writing GeoJSON for tiles") write_dataframe( df[["geometry", "Own_Type", "GAP_Sts"]].to_crs(GEO_CRS), tile_dir / "ownership.geojson", driver="GeoJSONSeq", )
def geom_type(data): if compat.USE_PYGEOS: res = pygeos.get_type_id(data) return geometry_type_values[np.searchsorted(geometry_type_ids, res)] else: return _unary_op("geom_type", data, null_value=None)
import numpy as np import pytest import pygeos from pygeos import Geometry from pygeos.decorators import UnsupportedGEOSOperation from .common import point, all_types, polygon, multi_polygon # fixed-precision operations raise GEOS exceptions on mixed dimension geometry collections all_single_types = [g for g in all_types if not pygeos.get_type_id(g) == 7] SET_OPERATIONS = ( pygeos.difference, pygeos.intersection, pygeos.symmetric_difference, pygeos.union, # pygeos.coverage_union is tested seperately ) REDUCE_SET_OPERATIONS = ( (pygeos.intersection_all, pygeos.intersection), (pygeos.symmetric_difference_all, pygeos.symmetric_difference), (pygeos.union_all, pygeos.union), # (pygeos.coverage_union_all, pygeos.coverage_union) is tested seperately ) # operations that support fixed precision REDUCE_SET_OPERATIONS_PREC = ((pygeos.union_all, pygeos.union), ) reduce_test_data = [ pygeos.box(0, 0, 5, 5),
def test_get_type_id(): assert pygeos.get_type_id(all_types).tolist()[:-1] == list(range(8))
def _pandas(cls, column, **kwargs): column_shape_format = kwargs.get("column_shape_format") place = kwargs.get("place") geocoder = kwargs.get("geocoder") geocoder_config = kwargs.get("geocoder_config") min_value = kwargs.get("min_value") max_value = kwargs.get("max_value") strict_min = kwargs.get("strict_min") strict_max = kwargs.get("strict_max") units = kwargs.get("units") if min_value is None and max_value is None: raise ValueError("min_value and max_value cannot both be None") if min_value is not None and max_value is not None and min_value > max_value: raise ValueError("min_value cannot be greater than max_value") if geocoder not in ["nominatim", "pickpoint", "openmapquest"]: raise NotImplementedError( "The geocoder is not implemented for this method.") # find the reference shape with the geocoder. if geocoder is not None: try: # Specify the default parameters for Nominatim and run query. User is responsible for config and query params otherwise. query_params = dict(exactly_one=True, geometry="wkt") location = cls.geocode(geocoder, geocoder_config, place, query_params) except: raise Exception( "Geocoding configuration and query failed to produce a valid result." ) else: raise Exception( "A valid geocoder must be provided for this method. See GeoPy for reference." ) # Load the column into a pygeos Geometry vector from numpy array (Series not supported). if column_shape_format == "wkt": shape_test = geos.from_wkt(column.to_numpy(), on_invalid="ignore") elif column_shape_format == "wkb": shape_test = geos.from_wkb(column.to_numpy(), on_invalid="ignore") elif column_shape_format == "lonlat": shape_df = pd.DataFrame(column.to_list(), columns=("lon", "lat")) shape_test = geos.points(shape_df.lon, y=shape_df.lat) elif column_shape_format == "latlon": shape_df = pd.DataFrame(column.to_list(), columns=("lat", "lon")) shape_test = geos.points(shape_df.lon, y=shape_df.lat) else: raise NotImplementedError( "Column values shape format not implemented.") # verify that all shapes are points and if not, convert to centroid point. points_test = pd.Series(shape_test) if not points_test.apply(lambda x: geos.get_type_id(x) == 0).all(): points_test = points_test.map(geos.centroid) # convert the geos point to a geopy point. points_test = points_test.apply( lambda x: lonlat(geos.get_x(x), geos.get_y(x))) if location is None: raise Exception("Geocoding failed to return a result.") else: point_ref = lonlat(location.longitude, location.latitude) # calculate the distance between the points using geopy if units in [ "km", "kilometers", "kilometres", "kilometer", "kilometre" ]: column_dist = points_test.apply( lambda p: distance(p, point_ref).km) elif units in ["m", "meters", "metres", "meter", "metre"]: column_dist = points_test.apply(lambda p: distance(p, point_ref).m) elif units in ["mi", "miles", "mile"]: column_dist = points_test.apply( lambda p: distance(p, point_ref).mi) elif units in ["ft", "feet", "foot"]: column_dist = points_test.apply( lambda p: distance(p, point_ref).ft) else: raise NotImplementedError( "Unit conversion has not yet been implemented. Please use one of km, m, mi, ft" ) # Evaluate the between statement (from column_values_between.py) if min_value is None: if strict_max: return column_dist < max_value else: return column_dist <= max_value elif max_value is None: if strict_min: return min_value < column_dist else: return min_value <= column_dist else: if strict_min and strict_max: return (min_value < column_dist) & (column_dist < max_value) elif strict_min: return (min_value < column_dist) & (column_dist <= max_value) elif strict_max: return (min_value <= column_dist) & (column_dist < max_value) else: return (min_value <= column_dist) & (column_dist <= max_value)