def test_polygons_not_enough_points_in_shell(shape): coords = np.ones(shape) with pytest.raises(pygeos.GEOSException): pygeos.polygons(coords) # make sure the first coordinate != second coordinate coords[..., 1] += 1 with pytest.raises(pygeos.GEOSException): pygeos.polygons(coords)
def test_polygons_not_enough_points_in_holes(shape): coords = np.ones(shape) with pytest.raises(ValueError): pygeos.polygons(np.ones((1, 4, 2)), coords) # make sure the first coordinate != second coordinate coords[..., 1] += 1 with pytest.raises(ValueError): pygeos.polygons(np.ones((1, 4, 2)), coords)
def default_filter( tile: mercantile.Tile, dataset: Sequence[Dict], geoms: Sequence[polygons], minimum_tile_cover=None, tile_cover_sort=False, maximum_items_per_tile: Optional[int] = None, ) -> List: """Filter and/or sort dataset per intersection coverage.""" indices = list(range(len(dataset))) if minimum_tile_cover or tile_cover_sort: tile_geom = polygons( mercantile.feature(tile)["geometry"]["coordinates"][0]) int_pcts = _intersect_percent(tile_geom, geoms) if minimum_tile_cover: indices = [ ind for ind in indices if int_pcts[ind] > minimum_tile_cover ] if tile_cover_sort: # https://stackoverflow.com/a/9764364 indices, _ = zip(*sorted(zip(indices, int_pcts), reverse=True)) if maximum_items_per_tile: indices = indices[:maximum_items_per_tile] return [dataset[ind] for ind in indices]
def __init__(self, tessellation, edges, buildings, id_name, unique_id, **kwargs): self.tessellation = tessellation self.edges = edges self.buildings = buildings self.id_name = id_name self.unique_id = unique_id if id_name in buildings.columns: raise ValueError( "'{}' column cannot be in the buildings GeoDataFrame".format( id_name)) cut = gpd.overlay( tessellation, gpd.GeoDataFrame(geometry=edges.buffer(0.001)), how="difference", ).explode() W = libpysal.weights.Queen.from_dataframe(cut, silence_warnings=True) cut["component"] = W.component_labels buildings_c = buildings.copy() buildings_c.geometry = buildings_c.representative_point( ) # make points centroids_tempID = gpd.sjoin( buildings_c, cut[[cut.geometry.name, "component"]], how="left", op="intersects", ) cells_copy = tessellation[[ unique_id, tessellation.geometry.name ]].merge(centroids_tempID[[unique_id, "component"]], on=unique_id, how="left") blocks = cells_copy.dissolve(by="component").explode().reset_index( drop=True) blocks[id_name] = range(len(blocks)) blocks[blocks.geometry.name] = gpd.GeoSeries(pygeos.polygons( blocks.exterior.values.data), crs=blocks.crs) blocks = blocks[[id_name, blocks.geometry.name]] centroids_w_bl_ID2 = gpd.sjoin(buildings_c, blocks, how="left", op="intersects") bl_ID_to_uID = centroids_w_bl_ID2[[unique_id, id_name]] buildings_m = buildings[[unique_id]].merge(bl_ID_to_uID, on=unique_id, how="left") self.buildings_id = buildings_m[id_name] cells_m = tessellation[[unique_id]].merge(bl_ID_to_uID, on=unique_id, how="left") self.tessellation_id = cells_m[id_name] self.blocks = blocks
def pg_polygons_wgs84(): size = 1000 x1, y1 = generate_lon_lat(size) x2, y2 = generate_lon_lat(size) # generate some fields in the data frame f = random.sample(size) * 360 - 180 i = random.randint(-32767, 32767, size=size) ui = random.randint(0, 65535, size=size).astype("uint64") df = DataFrame( data={ "x1": x1, "y1": y1, "x2": x2, "y2": y2, "f": f, "i": i, "ui": ui, "labels": i.astype("str"), }) # Generate random triangles df["geometry"] = df[["x1", "y1", "x2", "y2"]].apply( lambda row: pg.polygons([[row.x1, row.y1], [row.x2, row.y1], [row.x2, row.y2], [row.x1, row.y1]]), axis=1, ) return df
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 setup(self): self.multipolygons = np.array( [ pygeos.multipolygons(pygeos.polygons(np.random.random((2, 100, 2)))) for i in range(10000) ], dtype=object, )
def test_polygon_with_none_hole(): actual = pygeos.polygons( pygeos.linearrings(box_tpl(0, 0, 10, 10)), [ pygeos.linearrings(box_tpl(1, 1, 2, 2)), None, pygeos.linearrings(box_tpl(3, 3, 4, 4)), ], ) assert pygeos.area(actual) == 98.0
def assets_for_tile(self, x: int, y: int, z: int) -> List[str]: """Retrieve assets for tile.""" bbox = self.tms.bounds(x, y, z) geom = polygons( [ [bbox[0], bbox[3]], [bbox[0], bbox[1]], [bbox[2], bbox[1]], [bbox[2], bbox[3]], [bbox[0], bbox[3]], ] ) return self.get_assets(geom)
def __init__(self, bdy): """ Construct rich "Boundary" type. Input "bdy" can either be: (1) GlobalSmoothBoundary type (from pybie2d) (2) complex form of (x, y) points of boundary """ if type(bdy) == GSB: self.GSB = bdy else: self.GSB = GSB(c=bdy) self.c = self.GSB.c self.x = self.GSB.x self.y = self.GSB.y self.SH = pygeos.polygons([*zip(self.x, self.y)]) pygeos.prepare(self.SH)
def __init__(self, gdf, areas=None): self.gdf = gdf gdf = gdf.copy() if areas is None: areas = gdf.geometry.area if not isinstance(areas, str): gdf["mm_a"] = areas areas = "mm_a" self.areas = gdf[areas] exts = pygeos.area(pygeos.polygons(gdf.geometry.exterior.values.data)) self.series = pd.Series(exts - gdf[areas], index=gdf.index)
def create_grid(bbox,height): """Create a vector-based grid Args: bbox ([type]): [description] height ([type]): [description] Returns: [type]: [description] """ # set xmin,ymin,xmax,and ymax of the grid xmin, ymin = pygeos.total_bounds(bbox)[0],pygeos.total_bounds(bbox)[1] xmax, ymax = pygeos.total_bounds(bbox)[2],pygeos.total_bounds(bbox)[3] #estimate total rows and columns rows = int(numpy.ceil((ymax-ymin) / height)) cols = int(numpy.ceil((xmax-xmin) / height)) # set corner points x_left_origin = xmin x_right_origin = xmin + height y_top_origin = ymax y_bottom_origin = ymax - height # create actual grid res_geoms = [] for countcols in range(cols): y_top = y_top_origin y_bottom = y_bottom_origin for countrows in range(rows): res_geoms.append(( ((x_left_origin, y_top), (x_right_origin, y_top), (x_right_origin, y_bottom), (x_left_origin, y_bottom) ))) y_top = y_top - height y_bottom = y_bottom - height x_left_origin = x_left_origin + height x_right_origin = x_right_origin + height return pygeos.polygons(res_geoms)
def _regions(self, voronoi_diagram, unique_id, ids, crs): """ Generate GeoDataFrame of Voronoi regions from scipy.spatial.Voronoi. """ vertices = pd.Series(voronoi_diagram.regions).take( voronoi_diagram.point_region) polygons = [] for region in vertices: if -1 not in region: polygons.append( pygeos.polygons(voronoi_diagram.vertices[region])) else: polygons.append(None) regions_gdf = gpd.GeoDataFrame({ unique_id: ids }, geometry=polygons, crs=crs).dropna() regions_gdf = regions_gdf.loc[regions_gdf[unique_id] != -1] # delete hull-based cells return regions_gdf
def occult(lines: LineCollection, tolerance: float) -> LineCollection: """ Remove occulted lines. The order of the geometries in 'lines' matters, see example below. 'tolerance' controls the distance tolerance between the first and last points of a geometry to consider it closed. Examples: $ vpype line 0 0 5 5 rect 2 2 1 1 occult show # line is occulted by rect $ vpype rect 2 2 1 1 line 0 0 5 5 occult show # line is NOT occulted by rect, as the line is drawn after the rectangle. """ line_arr = np.array( [pygeos.linestrings(list(zip(line.real, line.imag))) for line in lines] ) for i, line in enumerate(line_arr): coords = pygeos.get_coordinates(line) if math.hypot(coords[-1, 0] - coords[0, 0], coords[-1, 1] - coords[0, 1]) < tolerance: tree = pygeos.STRtree(line_arr[:i]) p = pygeos.polygons(coords) geom_idx = tree.query(p, predicate="intersects") line_arr[geom_idx] = pygeos.set_operations.difference(line_arr[geom_idx], p) new_lines = LineCollection() for geom in line_arr: for i in range(pygeos.get_num_geometries(geom)): coords = pygeos.get_coordinates(pygeos.get_geometry(geom, i)) new_lines.append(coords[:, 0] + coords[:, 1] * 1j) return new_lines
class _TestPolygons(_TestSimilarity): SIMILAR = [ box(0, 0, 1, 1), polygons([[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]), polygons([[0.5, 0], [1, 0], [1, 1], [0, 1], [0, 0.5], [0, 0]]) ] DISSIMILAR = [ box(10, 10, 11, 11), polygons([[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]), polygons([[8, 8], [8, 13], [13, 13], [13, 8], [8, 8]], holes=[[[9, 9], [9, 12], [12, 12], [12, 9], [9, 9]]]), polygons([[4.5, 4], [5, 4], [5, 5], [4, 5], [4, 4.5], [4, 4]]), points([0.5, 0.5]) ] VALUE_GEOMETRIES = [[ box(0, 0, 2, 2), polygons([[8, 8], [8, 13], [13, 13], [13, 8], [8, 8]], holes=[[[9, 9], [9, 12], [12, 12], [12, 9], [9, 9]]]) ], [box(-1, -1, 1, 1), box(8, 8, 13, 13)]] VALUE = [0.14285714285714285, 0.64]
def test_polygon_from_linearring(): actual = pygeos.polygons(pygeos.linearrings(box_tpl(0, 0, 1, 1))) assert str(actual) == "POLYGON ((1 0, 1 1, 0 1, 0 0, 1 0))"
def setup(self): self.points = pygeos.points(np.random.random((100000, 2))) self.polygon = pygeos.polygons(np.random.random((3, 2)))
def setup(self): self.to_write = pygeos.polygons(np.random.random((10000, 100, 2))) self.to_read_wkt = pygeos.to_wkt(self.to_write) self.to_read_wkb = pygeos.to_wkb(self.to_write)
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_polygons_invalid_indices(indices): with pytest.raises((TypeError, ValueError)): pygeos.polygons([linear_ring], [linear_ring], indices=indices)
def verify(g, shp, thorough=False): #--------------------------------------------------------------------- logger.info(' Verify grid against coastline\n') #--------------------------------------------------------------------- lon_min = g.Dataset.SCHISM_hgrid_node_x.values.min() lon_max = g.Dataset.SCHISM_hgrid_node_x.values.max() lat_min = g.Dataset.SCHISM_hgrid_node_y.values.min() lat_max = g.Dataset.SCHISM_hgrid_node_y.values.max() c = shp.cx[lon_min:lon_max, lat_min:lat_max] # ## Test polygons d = g.Dataset x = d.SCHISM_hgrid_node_x.values y = d.SCHISM_hgrid_node_y.values tri = d.SCHISM_hgrid_face_nodes.values nodes = pd.DataFrame({'lon': x, 'lat': y}) elems = pd.DataFrame(tri, columns=['a', 'b', 'c']) bnodes = g.Dataset[['node', 'id', 'type']].to_dataframe() # ### Find the invalid nodes (that cross the coasts) cos = pygeos.from_shapely(c.geometry) cos_ = pygeos.set_operations.union_all(cos) gps = pygeos.points(list(nodes.values)) gtree = pygeos.STRtree(gps) invs = gtree.query(cos_, predicate='contains').tolist() #--------------------------------------------------------------------- logger.info('Number of nodes within the coastlines {}\n'.format(len(invs))) #--------------------------------------------------------------------- nps = len(invs) nels = 1 if thorough: # ### Find invalid elements (that cross land) # cells to polygons ap = nodes.loc[elems.a] bp = nodes.loc[elems.b] cp = nodes.loc[elems.c] elems['ap'] = ap.values.tolist() elems['bp'] = bp.values.tolist() elems['cp'] = cp.values.tolist() n = 2 al = elems.ap + elems.bp + elems.cp + elems.ap coords = [[l[i:i + n] for i in range(0, len(l), n)] for l in al] elems['coordinates'] = coords jig = pygeos.polygons(coords) jtree = pygeos.STRtree(jig) jig_ = pygeos.set_operations.union_all(jig) cross = pygeos.set_operations.intersection(jig_, cos_) # #### convert to dataframe fd = pd.DataFrame({'overlap': pygeos.to_wkt(cross)}, index=[0]) fd['overlap'] = fd['overlap'].apply(shapely.wkt.loads) gover = gp.GeoDataFrame(fd, geometry='overlap') # #### Reject small injuctions ipols = gover.explode().loc[0] ipols.columns = ['geometry'] mask = ipols.area.values == 0. ipols = ipols[~mask].reset_index(drop=True) ipols = gp.GeoDataFrame(ipols) #--------------------------------------------------------------------- logger.info( 'Number of elements intersecting the coastlines {}\n'.format( ipols.shape[0])) #--------------------------------------------------------------------- nels = ipols.shape[0] if nps == 0 and nels == 0: #--------------------------------------------------------------------- logger.info('Grid is verified against the coastline') #--------------------------------------------------------------------- return True elif nps == 0: #--------------------------------------------------------------------- logger.info('Grid is node verified against the coastline') #--------------------------------------------------------------------- return True else: #--------------------------------------------------------------------- logger.warning('Grid is not verified against the coastline') #--------------------------------------------------------------------- return False
def test_2_polygons_with_2_same_holes(): actual = pygeos.polygons( [box_tpl(0, 0, 10, 10), box_tpl(0, 0, 5, 5)], [box_tpl(1, 1, 2, 2), box_tpl(3, 3, 4, 4)], ) assert pygeos.area(actual).tolist() == [98.0, 23.0]
def test_2_polygons_with_different_holes(): actual = pygeos.polygons( [box_tpl(0, 0, 10, 10), box_tpl(0, 0, 5, 5)], [[box_tpl(1, 1, 3, 3)], [box_tpl(1, 1, 2, 2)]], ) assert pygeos.area(actual).tolist() == [96.0, 24.0]
def test_polygon_with_2_holes(): actual = pygeos.polygons( box_tpl(0, 0, 10, 10), [box_tpl(1, 1, 2, 2), box_tpl(3, 3, 4, 4)] ) assert pygeos.area(actual) == 98.0
def test_2_polygons_with_same_hole(): actual = pygeos.polygons( [box_tpl(0, 0, 10, 10), box_tpl(0, 0, 5, 5)], [box_tpl(1, 1, 2, 2)] ) assert pygeos.area(actual).tolist() == [99.0, 24.0]
def test_polygon_with_1_hole(): actual = pygeos.polygons(box_tpl(0, 0, 10, 10), [box_tpl(1, 1, 2, 2)]) assert pygeos.area(actual) == 99.0
def test_polygon_no_hole_list_raises(): with pytest.raises(ValueError): pygeos.polygons(box_tpl(0, 0, 10, 10), box_tpl(1, 1, 2, 2))
def test_polygons(): actual = pygeos.polygons(box_tpl(0, 0, 1, 1)) assert str(actual) == "POLYGON ((1 0, 1 1, 0 1, 0 0, 1 0))"
def test_polygons(holes, indices, expected): actual = pygeos.polygons([linear_ring, linear_ring], holes, indices=indices) assert_geometries_equal(actual, expected)
def test_polygons_missing_shell(): actual = pygeos.polygons([None, linear_ring], [hole_1, hole_2], indices=[0, 1]) assert_geometries_equal(actual, [None, poly_hole_2])