def test_equals_exact_tolerance(): # specifying tolerance p1 = pygeos.points(50, 4) p2 = pygeos.points(50.1, 4.1) actual = pygeos.equals_exact([p1, p2, None], p1, tolerance=0.05) np.testing.assert_allclose(actual, [True, False, False]) assert actual.dtype == np.bool actual = pygeos.equals_exact([p1, p2, None], p1, tolerance=0.2) np.testing.assert_allclose(actual, [True, True, False]) assert actual.dtype == np.bool # default value for tolerance assert pygeos.equals_exact(p1, p1).item() is True assert pygeos.equals_exact(p1, p2).item() is False
def test_from_wkt(): expected = pygeos.points(1, 1) actual = pygeos.from_wkt("POINT (1 1)") assert pygeos.equals(actual, expected) # also accept bytes actual = pygeos.from_wkt(b"POINT (1 1)") assert pygeos.equals(actual, expected)
def boundary_distance(polygon, points): """ Find the distance between a polygon's boundary and an array of points. Uses either `shapely` or `pygeos` (5-10x faster) as a backend. Parameters ------------- polygon : shapely.geometry.Polygon Polygon to query points : (n, 2) float 2D points Returns ------------ distance : (n,) float Minimum distance from each point to polygon boundary """ try: import pygeos # the pygeos way is 5-10x faster pg_points = pygeos.points(*points.T) pg_boundary = pygeos.boundary(pygeos.Geometry(polygon.wkt)) distance = pygeos.distance(pg_boundary, pg_points) except BaseException: # in pure shapely we have to loop inverse = polygon.boundary distance = np.array([ inverse.distance(i) for i in MultiPoint(points)]) return distance
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 test_from_wkb_hex(): # HEX form expected = pygeos.points(1, 1) actual = pygeos.from_wkb("0101000000000000000000F03F000000000000F03F") assert pygeos.equals(actual, expected) actual = pygeos.from_wkb(b"0101000000000000000000F03F000000000000F03F") assert pygeos.equals(actual, expected)
def _sample_land_points(self, N_pts): pt_df = pd.DataFrame(columns=['lon', 'lat'], index=[]) ii_p = 0 while len(pt_df) < N_pts: pts = np.random.rand(2 * 2 * (N_pts - len(pt_df))).reshape( (N_pts - len(pt_df)) * 2, 2) #print ('pts shape',pts.shape) pts[:, 0] = pts[:, 0] * 360 - 180 # lon pts[:, 1] = pts[:, 1] * (80 + 70) - 70 # lat between -70 and 80 pts_pygeos = pygeos.points(pts) Q = self.tree.query_bulk(pts_pygeos, predicate='within').T[:, 0] #print ('Q-shape',Q.shape) pt_df = pt_df.append(pd.DataFrame(pts[Q], columns=['lon', 'lat'])) #print ('len pt_df', len(pt_df), 'iter_toc',time.time()-tic) ii_p += 1 pt_df = pt_df.iloc[0:N_pts] # reset the index pt_df.index = range(len(pt_df)) return pt_df
def poly_tree(): # create buffers so that midpoint between two buffers intersects # each buffer. NOTE: add EPS to help mitigate rounding errors at midpoint. geoms = pygeos.buffer(pygeos.points(np.arange(10), np.arange(10)), HALF_UNIT_DIAG + EPS, quadsegs=32) yield pygeos.STRtree(geoms)
def setup(self): # create irregular polygons by merging overlapping point buffers self.left = pygeos.union_all( pygeos.buffer(pygeos.points(np.random.random((500, 2)) * 500), 15) ) # shift this up and right self.right = pygeos.apply(self.left, lambda x: x + 50)
def intersection(self, coordinates): # convert bounds to geometry # the old API uses tuples of bound, but pygeos uses geometries try: iter(coordinates) except TypeError: # likely not an iterable # this is a check that rtree does, we mimic it # to ensure a useful failure message raise TypeError( "Invalid coordinates, must be iterable in format " "(minx, miny, maxx, maxy) (for bounds) or (x, y) (for points). " "Got `coordinates` = {}.".format(coordinates)) # need to convert tuple of bounds to a geometry object if len(coordinates) == 4: indexes = super().query(pygeos.box(*coordinates)) elif len(coordinates) == 2: indexes = super().query(pygeos.points(*coordinates)) else: raise TypeError( "Invalid coordinates, must be iterable in format " "(minx, miny, maxx, maxy) (for bounds) or (x, y) (for points). " "Got `coordinates` = {}.".format(coordinates)) return indexes
def test_flush_geometries(tree): arr = pygeos.points(np.arange(10), np.arange(10)) tree = pygeos.STRtree(arr) # Dereference geometries arr[:] = None # Still it does not lead to a segfault tree.query(point)
def test_to_wkb_hex(): point = pygeos.points(1, 1) actual = pygeos.to_wkb(point, hex=True, byte_order=1) le = "01" point_type = "01000000" coord = "000000000000F03F" # 1.0 as double (LE) assert actual == le + point_type + 2 * coord
def test_to_wkb_3D(): point_z = pygeos.points(1, 1, 1) actual = pygeos.to_wkb(point_z, byte_order=1) # fmt: off assert actual == b"\x01\x01\x00\x00\x80\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\xf0?" # noqa # fmt: on actual = pygeos.to_wkb(point_z, output_dimension=2, byte_order=1) assert actual == POINT11_WKB
def test_to_wkb_byte_order(): point = pygeos.points(1.0, 1.0) be = b"\x00" le = b"\x01" point_type = b"\x01\x00\x00\x00" # 1 as 32-bit uint (LE) coord = b"\x00\x00\x00\x00\x00\x00\xf0?" # 1.0 as double (LE) assert pygeos.to_wkb(point, byte_order=1) == le + point_type + 2 * coord assert pygeos.to_wkb(point, byte_order=0) == be + point_type[::-1] + 2 * coord[::-1]
def test_destroy_prepared(): arr = np.array([pygeos.points(1, 1), None, pygeos.box(0, 0, 1, 1)]) pygeos.prepare(arr) assert arr[0]._ptr_prepared != 0 assert arr[2]._ptr_prepared != 0 pygeos.destroy_prepared(arr) assert arr[0]._ptr_prepared == 0 assert arr[1] is None assert arr[2]._ptr_prepared == 0 pygeos.destroy_prepared(arr) # does not error
def test_to_wkt(): point = pygeos.points(1, 1) actual = pygeos.to_wkt(point) assert actual == "POINT (1 1)" actual = pygeos.to_wkt(point, trim=False) assert actual == "POINT (1.000000 1.000000)" actual = pygeos.to_wkt(point, rounding_precision=3, trim=False) assert actual == "POINT (1.000 1.000)"
def setup(self): # create irregular polygons by merging overlapping point buffers self.polygon = pygeos.union_all( pygeos.buffer(pygeos.points(np.random.random((1000, 2)) * 500), 10)) xmin = np.random.random(100) * 100 xmax = xmin + 100 ymin = np.random.random(100) * 100 ymax = ymin + 100 self.bounds = np.array([xmin, ymin, xmax, ymax]).T self.boxes = pygeos.box(xmin, ymin, xmax, ymax)
def test_nearest_multi(self, geometry, expected, return_all): geoms = pygeos.points(np.arange(10), np.arange(10)) df = geopandas.GeoDataFrame({"geometry": geoms}) ps = [Point(p) for p in geometry] res = df.sindex.nearest(ps, return_all=return_all) assert_array_equal(res, expected) ps = pygeos.points(geometry) res = df.sindex.nearest(ps, return_all=return_all) assert_array_equal(res, expected) s = geopandas.GeoSeries(ps) res = df.sindex.nearest(s, return_all=return_all) assert_array_equal(res, expected) x, y = zip(*geometry) ga = geopandas.points_from_xy(x, y) res = df.sindex.nearest(ga, return_all=return_all) assert_array_equal(res, expected)
def intersection(self, coordinates, objects=False): """Wrapper for pygeos.query that uses the RTree API. Parameters ---------- coordinates : sequence or array Sequence of the form (min_x, min_y, max_x, max_y) to query a rectangle or (x, y) to query a point. objects : boolean, default False If True, return the label based indexes. If False, integer indexes are returned. """ if objects: warn( "`objects` is deprecated and will be removed in a future version. " "Instead, use `iloc` to index your GeoSeries/GeoDataFrame using " "integer indexes returned by `intersection`.", FutureWarning, ) # convert bounds to geometry # the old API uses tuples of bound, but pygeos uses geometries try: iter(coordinates) except TypeError: # likely not an iterable # this is a check that rtree does, we mimic it # to ensure a useful failure message raise TypeError( "Invalid coordinates, must be iterable in format " "(minx, miny, maxx, maxy) (for bounds) or (x, y) (for points). " "Got `coordinates` = {}.".format(coordinates)) # need to convert tuple of bounds to a geometry object if len(coordinates) == 4: indexes = super().query(pygeos.box(*coordinates)) elif len(coordinates) == 2: indexes = super().query(pygeos.points(*coordinates)) else: raise TypeError( "Invalid coordinates, must be iterable in format " "(minx, miny, maxx, maxy) (for bounds) or (x, y) (for points). " "Got `coordinates` = {}.".format(coordinates)) if objects: objs = self.objects[indexes].values ids = self.ids[indexes] return [ self.with_objects(id=id, object=obj) for id, obj in zip(ids, objs) ] else: return indexes
def test_prepare(): arr = np.array([pygeos.points(1, 1), None, pygeos.box(0, 0, 1, 1)]) assert arr[0]._ptr_prepared == 0 assert arr[2]._ptr_prepared == 0 pygeos.prepare(arr) assert arr[0]._ptr_prepared != 0 assert arr[1] is None assert arr[2]._ptr_prepared != 0 # preparing again actually does nothing original = arr[0]._ptr_prepared pygeos.prepare(arr) assert arr[0]._ptr_prepared == original
def points_from_xy(x, y, z=None): x = np.asarray(x, dtype="float64") y = np.asarray(y, dtype="float64") if z is not None: z = np.asarray(z, dtype="float64") if compat.USE_PYGEOS: return pygeos.points(x, y, z) out = _points_from_xy(x, y, z) aout = np.empty(len(x), dtype=object) aout[:] = out return aout
def test_to_wkt_3D(): # 3D points point_z = pygeos.points(1, 1, 1) actual = pygeos.to_wkt(point_z) assert actual == "POINT Z (1 1 1)" actual = pygeos.to_wkt(point_z, output_dimension=3) assert actual == "POINT Z (1 1 1)" actual = pygeos.to_wkt(point_z, output_dimension=2) assert actual == "POINT (1 1)" actual = pygeos.to_wkt(point_z, old_3d=True) assert actual == "POINT (1 1 1)"
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 test_to_wkb_srid(): # hex representation of POINT (0 0) with SRID=4 ewkb = "01010000200400000000000000000000000000000000000000" wkb = "010100000000000000000000000000000000000000" actual = pygeos.from_wkb(ewkb) assert pygeos.to_wkt(actual, trim=True) == "POINT (0 0)" assert pygeos.to_wkb(actual, hex=True, byte_order=1) == wkb assert pygeos.to_wkb(actual, hex=True, include_srid=True, byte_order=1) == ewkb point = pygeos.points(1, 1) point_with_srid = pygeos.set_srid(point, np.int32(4326)) result = pygeos.to_wkb(point_with_srid, include_srid=True, byte_order=1) assert np.frombuffer(result[5:9], "<u4").item() == 4326
def points_from_xy(x, y, z=None): x = np.asarray(x, dtype="float64") y = np.asarray(y, dtype="float64") if z is not None: z = np.asarray(z, dtype="float64") if compat.USE_PYGEOS: return pygeos.points(x, y, z) else: out = _points_from_xy(x, y, z) aout = np.empty(len(x), dtype=object) with compat.ignore_shapely2_warnings(): aout[:] = out return aout
def test_nearest_max_distance(self, expected, max_distance, return_all, return_distance): geoms = pygeos.points(np.arange(10), np.arange(10)) df = geopandas.GeoDataFrame({"geometry": geoms}) ps = [Point(0.5, 0.5), Point(0, 10)] res = df.sindex.nearest( ps, return_all=return_all, max_distance=max_distance, return_distance=return_distance, ) if return_distance: assert_array_equal(res[0], expected[0]) assert_array_equal(res[1], expected[1]) else: assert_array_equal(res, expected[0])
def intersection(self, coordinates, objects=False): """Wrapper for pygeos.query that uses the RTree API. Parameters ---------- coordinates : sequence or array Sequence of the form (min_x, min_y, max_x, max_y) to query a rectangle or (x, y) to query a point. objects : True or False If True, return the label based indexes. If False, integer indexes are returned. """ # convert bounds to geometry # the old API uses tuples of bound, but pygeos uses geometries try: iter(coordinates) except TypeError: # likely not an iterable # this is a check that rtree does, we mimick it # to ensure a useful failure message raise TypeError( "Invalid coordinates, must be iterable in format " "(minx, miny, maxx, maxy) (for bounds) or (x, y) (for points)." ) # need to convert tuple of bounds to a geometry object if len(coordinates) == 4: indexes = super().query(box(*coordinates)) elif len(coordinates) == 2: indexes = super().query(points(*coordinates)) else: raise TypeError( "Invalid coordinates, must be iterable in format " "(minx, miny, maxx, maxy) (for bounds) or (x, y) (for points)." ) if objects: objs = self.objects[indexes].values ids = self.ids[indexes] return [ self.with_objects(id=id, object=obj) for id, obj in zip(ids, objs) ] else: return indexes
def pg_points_wgs84(): size = 1000 x, y = generate_lon_lat(size) # generate some other fields in the data frame i = random.randint(-32767, 32767, size=size) ui = random.randint(0, 65535, size=size).astype("uint64") df = DataFrame(data={ "x": x, "y": y, "i": i, "ui": ui, "labels": i.astype("str") }) df["geometry"] = pg.points(np.array([x, y]).T) return df
def _geometry_from_latlon(table, lat_field, lon_field, crs): """Transforms an arrow to table to spatial arrow table, using lat, lon information. Extracts the lat, lon information from an arrow table, creates the Point geometry and writes the geometry information to the arrow table. Parameters: table (object): The arrow table. lat_field (string): The latitude field name. lon_field (string): The longitude field name. crs (string): The lat, lon CRS. Returns: (object): The arrow spatial table. """ lat = table.column(lat_field) lon = table.column(lon_field) geometry = pg.to_wkb(pg.points(lon, lat)) field = pa.field('geometry', 'binary', metadata={'crs': crs}) table = table.append_column(field, [geometry]) table = table.drop([lat_field, lon_field]) return table
def convert_to_point(input_array, trim_invalid_geometry=False, autocorrect_invalid_geometry=False): r"""Convert an input array to a Point array. Args: input_array (ndarray, list): A ndarray of Point optionally followed by a confidence value and/or a label where each row is: ``[x, y, (confidence), (label)]`` trim_invalid_geometry (bool): Optional, default to ``False``. If set to ``True`` conversion will ignore invalid geometries and leave them out of ``output_array``. This means that the function will return an array where ``output_array.shape[0] <= input_array.shape[0]``. If set to ``False``, an invalid geometry will raise an :exc:`~playground_metrics.utils.geometry_utils.InvalidGeometryError`. autocorrect_invalid_geometry (Bool): Optional, default to ``False``. Doesn't do anything, introduced to unify convert functions interfaces. Returns: ndarray: A Point ndarray where each row contains a geometry followed by optionally confidence and a label e.g.: ``[Point, (confidence), (label)]`` Raises: ValueError: If ``input_array`` have invalid dimensions. """ input_array = np.array(input_array, dtype=np.dtype('O')) if input_array.size == 0: return 'undefined', input_array if len(input_array.shape) == 1 or len(input_array.shape) > 2: raise ValueError('Invalid array number of dimensions: ' 'Expected a 2D array, found {}D.'.format( len(input_array.shape))) coordinates_array = input_array[:, :2].astype(np.float64) object_array = np.ndarray((input_array.shape[0], input_array.shape[1] - 1), dtype=np.dtype('O')) object_array[:, 0] = points(coordinates_array) object_array[:, 1:] = input_array[:, 2:] if trim_invalid_geometry: object_array = object_array[is_valid(object_array[:, 0]), :] return object_array
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]