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 country_grid_gdp_filled(trans_network, country, data_path, rough_grid_split=100, from_main_graph=False): """[summary] Args: trans_network ([type]): [description] rough_grid_split (int, optional): [description]. Defaults to 100. Returns: [type]: [description] """ if from_main_graph == True: node_df = trans_network.copy() envelop = pygeos.envelope( pygeos.multilinestrings(node_df.geometry.values)) height = np.sqrt(pygeos.area(envelop) / rough_grid_split) else: node_df = trans_network.nodes.copy() node_df.geometry, approximate_crs = convert_crs(node_df) envelop = pygeos.envelope( pygeos.multilinestrings(node_df.geometry.values)) height = np.sqrt(pygeos.area(envelop) / rough_grid_split) gdf_admin = pd.DataFrame(create_grid(create_bbox(node_df), height), columns=['geometry']) #load data and convert to pygeos country_shape = gpd.read_file(os.path.join(data_path, 'GADM', 'gadm36_levels.gpkg'), layer=0) country_shape = pd.DataFrame( country_shape.loc[country_shape.GID_0 == country]) country_shape.geometry = pygeos.from_shapely(country_shape.geometry) gdf_admin = pygeos.intersection(gdf_admin, country_shape.geometry) gdf_admin = gdf_admin.loc[~pygeos.is_empty(gdf_admin.geometry)] gdf_admin['centroid'] = pygeos.centroid(gdf_admin.geometry) gdf_admin['km2'] = area(gdf_admin) gdf_admin['gdp'] = get_gdp_values(gdf_admin, data_path) gdf_admin = gdf_admin.loc[gdf_admin.gdp > 0].reset_index() gdf_admin['gdp_area'] = gdf_admin.gdp / gdf_admin['km2'] return gdf_admin
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 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(coords, cut_offsets): """Cut a pygeos line geometry at points. The points must be interior to the line. Parameters ---------- coords : ndarray of shape (2,n) line coordinates cut_points : list-like of offsets projected onto the line Returns ------- MultiLineStrings """ new_coords, line_ix = split_coords(coords, cut_offsets) return pg.multilinestrings(pg.linestrings(new_coords, indices=line_ix))
import numpy as np import pygeos point_polygon_testdata = ( pygeos.points(np.arange(6), np.arange(6)), pygeos.box(2, 2, 4, 4), ) point = pygeos.points(2, 3) line_string = pygeos.linestrings([(0, 0), (1, 0), (1, 1)]) linear_ring = pygeos.linearrings([(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)]) polygon = pygeos.polygons([(0, 0), (2, 0), (2, 2), (0, 2), (0, 0)]) multi_point = pygeos.multipoints([(0, 0), (1, 2)]) multi_line_string = pygeos.multilinestrings([[(0, 0), (1, 2)]]) multi_polygon = pygeos.multipolygons([ [(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)], [(2.1, 2.1), (2.2, 2.1), (2.2, 2.2), (2.1, 2.2), (2.1, 2.1)], ]) geometry_collection = pygeos.geometrycollections( [pygeos.points(51, -1), pygeos.linestrings([(52, -1), (49, 2)])]) point_z = pygeos.points(1.0, 1.0, 1.0) polygon_with_hole = pygeos.Geometry( "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0), (2 2, 2 4, 4 4, 4 2, 2 2))") all_types = ( point, line_string, linear_ring, polygon, multi_point, multi_line_string,
def test_multilinearrings(): actual = pygeos.multilinestrings([linear_ring], indices=[0]) assert_geometries_equal(actual, pygeos.multilinestrings([linear_ring]))
import numpy as np import pygeos point_polygon_testdata = ( pygeos.points(np.arange(6), np.arange(6)), pygeos.box(2, 2, 4, 4), ) point = pygeos.points(2, 2) line_string = pygeos.linestrings([[0, 0], [1, 0], [1, 1]]) linear_ring = pygeos.linearrings(((0, 0), (0, 1), (1, 1), (1, 0), (0, 0))) polygon = pygeos.polygons( ((0.0, 0.0), (0.0, 2.0), (2.0, 2.0), (2.0, 0.0), (0.0, 0.0))) multi_point = pygeos.multipoints([[0.0, 0.0], [1.0, 2.0]]) multi_line_string = pygeos.multilinestrings([[[0.0, 0.0], [1.0, 2.0]]]) multi_polygon = pygeos.multipolygons([ ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)), ((0.1, 0.1), (0.1, 0.2), (0.2, 0.2), (0.2, 0.1)), ]) geometry_collection = pygeos.geometrycollections( [pygeos.points(51, -1), pygeos.linestrings([(52, -1), (49, 2)])]) point_z = pygeos.points(1.0, 1.0, 1.0) all_types = ( point, line_string, linear_ring, polygon, multi_point, multi_line_string, multi_polygon,
def test_multilinearrings(): actual = pygeos.multilinestrings( np.array([linear_ring], dtype=object), indices=np.zeros(1, dtype=np.intp) ) assert_geometries_equal(actual, pygeos.multilinestrings([linear_ring]))