def test_coverage_union_reduce_axis(): # shape = (3, 2), all polygons - none of them overlapping data = [[pygeos.box(i, j, i + 1, j + 1) for i in range(2)] for j in range(3)] actual = pygeos.coverage_union_all(data) assert actual.shape == (2, ) actual = pygeos.coverage_union_all(data, axis=0) # default assert actual.shape == (2, ) actual = pygeos.coverage_union_all(data, axis=1) assert actual.shape == (3, ) actual = pygeos.coverage_union_all(data, axis=-1) assert actual.shape == (3, )
def test_coverage_union_reduce_1dim(n): """ This is tested seperately from other set operations as it differs in two ways: 1. It expects only non-overlapping polygons 2. It expects GEOS 3.8.0+ """ test_data = [ pygeos.box(0, 0, 1, 1), pygeos.box(1, 0, 2, 1), pygeos.box(2, 0, 3, 1), ] actual = pygeos.coverage_union_all(test_data[:n]) # perform the reduction in a python loop and compare expected = test_data[0] for i in range(1, n): expected = pygeos.coverage_union(expected, test_data[i]) assert pygeos.equals(actual, expected)
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)
# state outer boundaries, NOT analysis boundaries bnd_df = gp.read_feather(out_dir / "region_boundary.feather") bnd = bnd_df.loc[bnd_df.id == "total"].geometry.values.data[0] sarp_bnd = bnd_df.loc[bnd_df.id == "se"].geometry.values.data[0] state_df = gp.read_feather(out_dir / "region_states.feather", columns=["STATEFIPS", "geometry"]) states = state_df.STATEFIPS.unique() sarp_state_df = gp.read_feather(out_dir / "sarp_states.feather", columns=["STATEFIPS", "geometry"]) sarp_states = sarp_state_df.STATEFIPS.unique() # Clip HUC4 areas outside state boundaries; these are remainder state_merged = pg.coverage_union_all(state_df.geometry.values.data) # find all that intersect but are not contained tree = pg.STRtree(huc4_df.geometry.values.data) intersects_ix = tree.query(state_merged, predicate="intersects") contains_ix = tree.query(state_merged, predicate="contains") ix = np.setdiff1d(intersects_ix, contains_ix) outer_huc4 = huc4_df.iloc[ix].copy() outer_huc4["km2"] = pg.area(outer_huc4.geometry.values.data) / 1e6 # calculate geometric difference, explode, and keep non-slivers outer_huc4["geometry"] = pg.difference(outer_huc4.geometry.values.data, state_merged) outer_huc4 = explode(outer_huc4) outer_huc4["clip_km2"] = pg.area(outer_huc4.geometry.values.data) / 1e6