def close_gaps(df, tolerance): """Close gaps in LineString geometry where it should be contiguous. Snaps both lines to a centroid of a gap in between. """ geom = df.geometry.values.data coords = pygeos.get_coordinates(geom) indices = pygeos.get_num_coordinates(geom) # generate a list of start and end coordinates and create point geometries edges = [0] i = 0 for ind in indices: ix = i + ind edges.append(ix - 1) edges.append(ix) i = ix edges = edges[:-1] points = pygeos.points(np.unique(coords[edges], axis=0)) buffered = pygeos.buffer(points, tolerance) dissolved = pygeos.union_all(buffered) exploded = [ pygeos.get_geometry(dissolved, i) for i in range(pygeos.get_num_geometries(dissolved)) ] centroids = pygeos.centroid(exploded) snapped = pygeos.snap(geom, pygeos.union_all(centroids), tolerance) return snapped
def split_edges_at_nodes_pyg(network, tolerance=1e-9): """Split network edges where they intersect node geometries """ #already initiate the spatial index, so we dont have to do that every time sindex = pygeos.STRtree(network.nodes['geometry']) grab_all_edges = [] for edge in tqdm(network.edges.itertuples(index=False), desc="split", total=len(network.edges)): hits = nodes_intersecting_pyg(edge.geometry, network.nodes['geometry'], sindex, tolerance=1e-9) if len(hits) < 3: grab_all_edges.append([[edge.osm_id], [edge.geometry], [edge.highway]]) continue # get points and geometry as list of coordinates split_points = pygeos.coordinates.get_coordinates( pygeos.snap(hits, edge.geometry, tolerance=1e-9)) coor_geom = pygeos.coordinates.get_coordinates(edge.geometry) # potentially split to multiple edges split_locs = np.argwhere(np.isin(coor_geom, split_points).all(axis=1))[:, 0] split_locs = list(zip(split_locs.tolist(), split_locs.tolist()[1:])) new_edges = [ coor_geom[split_loc[0]:split_loc[1] + 1] for split_loc in split_locs ] grab_all_edges.append( [[edge.osm_id] * len(new_edges), [pygeos.linestrings(edge) for edge in new_edges], [edge.infra_type] * len(new_edges)]) # combine all new edges edges = pd.DataFrame([ item for sublist in [list(zip(x[0], x[1], x[2])) for x in grab_all_edges] for item in sublist ], columns=['osm_id', 'geometry', 'infra_type']) # return new network with split edges return Network(nodes=network.nodes, edges=edges)
def close_gaps(gdf, tolerance): """Close gaps in LineString geometry where it should be contiguous. Snaps both lines to a centroid of a gap in between. Parameters ---------- gdf : GeoDataFrame, GeoSeries GeoDataFrame or GeoSeries containing LineString representation of a network. tolerance : float nodes within a tolerance will be snapped together Returns ------- GeoSeries See also -------- momepy.extend_lines momepy.remove_false_nodes """ geom = gdf.geometry.values.data coords = pygeos.get_coordinates(geom) indices = pygeos.get_num_coordinates(geom) # generate a list of start and end coordinates and create point geometries edges = [0] i = 0 for ind in indices: ix = i + ind edges.append(ix - 1) edges.append(ix) i = ix edges = edges[:-1] points = pygeos.points(np.unique(coords[edges], axis=0)) buffered = pygeos.buffer(points, tolerance / 2) dissolved = pygeos.union_all(buffered) exploded = [ pygeos.get_geometry(dissolved, i) for i in range(pygeos.get_num_geometries(dissolved)) ] centroids = pygeos.centroid(exploded) snapped = pygeos.snap(geom, pygeos.union_all(centroids), tolerance) return gpd.GeoSeries(snapped, crs=gdf.crs)
def constructive(arr, operation, *args, **kwargs): if operation == 'boundary': geometries = pg.boundary(pg.from_wkb(arr), **kwargs) elif operation == 'buffer': geometries = pg.buffer(pg.from_wkb(arr), *args, **kwargs) elif operation == 'build_area': geometries = pg.build_area(pg.from_wkb(arr), **kwargs) elif operation == 'centroid': geometries = pg.centroid(pg.from_wkb(arr), **kwargs) elif operation == 'clip_by_rect': geometries = pg.clip_by_rect(pg.from_wkb(arr), *args, **kwargs) elif operation == 'convex_hull': geometries = pg.convex_hull(pg.from_wkb(arr), **kwargs) elif operation == 'delaunay_triangles': geometries = pg.delaunay_triangles(pg.from_wkb(arr), **kwargs) elif operation == 'envelope': geometries = pg.envelope(pg.from_wkb(arr), **kwargs) elif operation == 'extract_unique_points': geometries = pg.extract_unique_points(pg.from_wkb(arr), **kwargs) elif operation == 'make_valid': geometries = pg.make_valid(pg.from_wkb(arr), **kwargs) elif operation == 'normalize': geometries = pg.normalize(pg.from_wkb(arr), **kwargs) elif operation == 'offset_curve': geometries = pg.offset_curve(pg.from_wkb(arr), *args, **kwargs) elif operation == 'point_on_surface': geometries = pg.point_on_surface(pg.from_wkb(arr), **kwargs) elif operation == 'reverse': geometries = pg.reverse(pg.from_wkb(arr), **kwargs) elif operation == 'simplify': geometries = pg.simplify(pg.from_wkb(arr), *args, **kwargs) elif operation == 'snap': geometries = pg.snap(pg.from_wkb(arr), *args, **kwargs) elif operation == 'voronoi_polygons': geometries = pg.voronoi_polygons(pg.from_wkb(arr), **kwargs) else: warnings.warn(f'Operation {operation} not supported.') return None return pg.to_wkb(geometries)
def test_snap(): line = pygeos.linestrings([[0, 0], [1, 0], [2, 0]]) points = pygeos.points([0, 1], [1, 0.1]) actual = pygeos.snap(points, line, 0.5) expected = pygeos.points([0, 1], [1, 0]) assert pygeos.equals(actual, expected).all()
def test_snap_none(): actual = pygeos.snap(None, point, tolerance=1.0) assert actual is None
def test_snap_array(geometry, reference): actual = pygeos.snap([geometry, geometry], [reference, reference], tolerance=1.0) assert actual.shape == (2,) assert isinstance(actual[0], Geometry)
def test_snap_nan_float(geometry): actual = pygeos.snap(geometry, point, tolerance=np.nan) assert actual is None
def test_snap_empty(none): actual = pygeos.snap(none, point, tolerance=1.0) assert actual is Empty