def naive_compute_distance_similarity_matrix(sorted_detections, ground_truths): """Computes a similarity based on euclidean distance between all pairs of geometries in a naive fashion. Args: sorted_detections (ndarray, list) : A ndarray of detections stored as: * Bounding boxes for a given class where each row is a detection stored as: ``[BoundingBox, confidence]`` * Polygons for a given class where each row is a detection stored as: ``[Polygon, confidence]`` * Points for a given class where each row is a detection stored as: ``[Point, confidence]`` ground_truths (ndarray,list) : A ndarray of ground truth stored as: * Bounding boxes for a given class where each row is a ground truth stored as: ``[BoundingBox]`` * Polygons for a given class where each row is a ground truth stored as: ``[Polygon]`` * Points for a given class where each row is a ground truth stored as: ``[Point]`` Returns: ndarray : An similarity matrix (#detections, #ground truth) """ # We prepare the distance matrix (#detection, #gt) distance_matrix = np.zeros((sorted_detections.shape[0], len(ground_truths))) # Naive iterative distance matrix construction (Note: we iterate over the sorted detections) for k, ground_truth in enumerate(ground_truths): for m, detection in enumerate(sorted_detections): distance_matrix[m, k] = distance(centroid(detection[0]), centroid(ground_truth[0])) return 1 - distance_matrix
def convert_crs(gdf, current_crs="epsg:4326"): """[summary] Args: gdf ([type]): [description] Returns: [type]: [description] """ if current_crs == "epsg:4326": lat = pygeos.geometry.get_y(pygeos.centroid(gdf['geometry'].iloc[0])) lon = pygeos.geometry.get_x(pygeos.centroid(gdf['geometry'].iloc[0])) # formula below based on :https://gis.stackexchange.com/a/190209/80697 approximate_crs = "epsg:" + str( int(32700 - np.round((45 + lat) / 90, 0) * 100 + np.round((183 + lon) / 6, 0))) else: approximate_crs = "epsg:4326" #from pygeos/issues/95 geometries = gdf['geometry'] coords = pygeos.get_coordinates(geometries) transformer = pyproj.Transformer.from_crs(current_crs, approximate_crs, always_xy=True) new_coords = transformer.transform(coords[:, 0], coords[:, 1]) result = pygeos.set_coordinates(geometries.copy(), np.array(new_coords).T) return result, approximate_crs
def moment_of_inertia(collection): """ Computes the moment of inertia of the polygon. This treats each boundary point as a point-mass of 1. Thus, for constant unit mass at each boundary point, the MoI of this pointcloud is \sum_i d_{i,c}^2 where c is the centroid of the polygon Altman's OS_1 measure, cited in Boyce and Clark (1964), also used in Weaver and Hess (1963). """ ga = _cast(collection) coords = pygeos.get_coordinates(ga) geom_ixs = numpy.repeat(numpy.arange(len(ga)), pygeos.get_num_coordinates(ga)) centroids = pygeos.get_coordinates(pygeos.centroid(ga))[geom_ixs] squared_euclidean = numpy.sum((coords - centroids)**2, axis=1) dists = (pandas.DataFrame.from_dict( dict(d2=squared_euclidean, geom_ix=geom_ixs)).groupby("geom_ix").d2.sum()).values return pygeos.area(ga) / numpy.sqrt(2 * dists)
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 export_csv(gdf, path, latlon=False, geom=True, lat_name='lat', lon_name='lot', geom_name='geometry', column_names=None, selection=False, virtual=True, chunksize=1000000, **kwargs): """ Writes GeoDataFrame to a CSV spatial file. """ import pandas as pd sep = kwargs.pop('delimiter', ',') column_names = column_names or gdf.get_column_names(virtual=virtual, strings=True) dtypes = gdf[column_names].dtypes fields = column_names[:] if latlon: fields.append(lat_name) fields.append(lon_name) if geom: fields.append(geom_name) geom_arr = gdf.geometry._geometry if selection not in [None, False] or gdf.filtered: mask = gdf.evaluate_selection_mask(selection) geom_arr = geom_arr.filter(mask) for i1, i2, chunks in gdf.evaluate_iterator(column_names, chunk_size=chunksize, selection=selection): if latlon: coordinates = pg.get_coordinates( pg.centroid(pg.from_wkb(geom_arr[i1:i2]))).T chunks.append(coordinates[0]) chunks.append(coordinates[1]) if geom: chunks.append(pg.to_wkt(pg.from_wkb(geom_arr[i1:i2]))) chunk_dict = {col: values for col, values in zip(fields, chunks)} chunk_pdf = pd.DataFrame(chunk_dict) if i1 == 0: # Only the 1st chunk should have a header and the rest will be appended mode = 'w' header = True else: mode = 'a' header = False chunk_pdf.to_csv(path_or_buf=path, mode=mode, header=header, sep=sep, index=False, **kwargs)
def convert_point_to_constant_box(input_array, box_size): input_array = np.copy(input_array) for i in range(input_array.shape[0]): x, y = get_coordinates(centroid(input_array[i, 0]))[0] input_array[i, 0] = box(x - (box_size / 2), y - (box_size / 2), x + (box_size / 2), y + (box_size / 2)) return input_array
def as_points(x): """Convert an array of geometries to an array of centroid points. Args: x (numpy.ndarray): An array of geometries. Returns: numpy.ndarray: An array of points. """ return centroid(x)
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 heatmap(pois, basemap_provider='OpenStreetMap', basemap_name='Mapnik', width='100%', height='100%', radius=10): """Generates a heatmap of the input POIs. Parameters: pois (GeoDataFrame): A POIs GeoDataFrame. basemap_provider (string): The basemap provider. basemap_name: The basemap itself as named by the provider. List and preview of available providers and their basemaps can be found in https://leaflet-extras.github.io/leaflet-providers/preview/ width (integer or percentage): Width of the map in pixels or percentage (default: 100%). height (integer or percentage): Height of the map in pixels or percentage (default: 100%). radius (float): Radius of each point of the heatmap (default: 10). Returns: A Folium Map object displaying the heatmap generated from the POIs. """ # Set the crs to WGS84 if pois.geometry.crs == 'EPSG:4326': pass else: pois.geometry.to_crs('EPSG:4326') # Automatically center the map at the center of the gdf's bounding box bb = pois.geometry.total_bounds() map_center = pg.get_coordinates(pg.centroid(bb))[0].tolist() tiles, attribution, max_zoom = get_provider_info(basemap_provider, basemap_name) heat_map = folium.Map(location=map_center, tiles=tiles, attr=attribution, max_zoom=max_zoom, width=width, height=height) # Automatically set zoom level bounds = pg.total_bounds(bb) heat_map.fit_bounds(([bounds[1], bounds[0]], [bounds[3], bounds[2]])) # List comprehension to make list of lists heat_data = pois.geometry.get_coordinates(invert=True) # Plot it on the map HeatMap(heat_data, radius=radius).add_to(heat_map) return heat_map
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 geojson(geojson, basemap_provider='OpenStreetMap', basemap_name='Mapnik', width='100%', height='100%', styled=False): """Plots into a Folium map. Parameters: geojson (dict): A geojson object. basemap_provider (string): The basemap provider. basemap_name: The basemap itself as named by the provider. List and preview of available providers and their basemaps can be found in https://leaflet-extras.github.io/leaflet-providers/preview/ width (int|string): Width of the map in pixels or percentage (default: 100%). height (int|string): Height of the map in pixels or percentage (default: 100%). styled (bool): If True, follows the mapbox simple style, as proposed in https://github.com/mapbox/simplestyle-spec/tree/master/1.1.0. Returns: (object) A Folium Map object displaying the geoJSON. """ df = gpd.GeoDataFrame.from_features(geojson['features'], crs="epsg:4326") bb = ymin, xmin, ymax, xmax = df.geometry.total_bounds map_center = pg.get_coordinates(pg.centroid(pg.box(xmin, ymin, xmax, ymax)))[0] tiles, attribution, max_zoom = get_provider_info(basemap_provider, basemap_name) m = folium.Map(location=map_center, tiles=tiles, attr=attribution, max_zoom=max_zoom, width=width, height=height) m.fit_bounds([[xmin, ymin], [xmax, ymax]]) if styled: folium.GeoJson(df, name='geojson', style_function=lambda x: dict( color=x['properties']['stroke'], fillColor=x['properties']['fill'], fillOpacity=x['properties']['fill-opacity'], opacity=0.1, weight=x['properties']['stroke-width'])).add_to(m) else: folium.GeoJson(df, name='geojson').add_to(m) return m
def _pandas(cls, column, **kwargs): shape = kwargs.get("shape") shape_format = kwargs.get("shape_format") column_shape_format = kwargs.get("column_shape_format") # Check that shape is given and given in the correct format if shape is not None: try: if shape_format == "wkt": shape_ref = geos.from_wkt(shape) elif shape_format == "wkb": shape_ref = geos.from_wkb(shape) elif shape_format == "geojson": shape_ref = geos.from_geojson(shape) else: raise NotImplementedError( "Shape constructor method not implemented. Must be in WKT, WKB, or GeoJSON format." ) except: raise Exception("A valid reference shape was not given.") else: raise Exception("A shape must be provided for this method.") # Load the column into a pygeos Geometry vector from numpy array (Series not supported). if column_shape_format == "wkt": shape_test = geos.from_wkt(column.to_numpy(), on_invalid="ignore") elif column_shape_format == "wkb": shape_test = geos.from_wkb(column.to_numpy(), on_invalid="ignore") else: raise NotImplementedError( "Column values shape format not implemented.") # Allow for an array of reference shapes to be provided. Return a union of all the shapes in the array (Polygon or Multipolygon) shape_ref = geos.union_all(shape_ref) # Prepare the geometries geos.prepare(shape_ref) geos.prepare(shape_test) column_centroids = geos.centroid(shape_test) print(column_centroids) return pd.Series(geos.within(column_centroids, shape_ref))
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 _(shape: pygeos.Geometry): """ if we know we're working with a pygeos polygon, then use pygeos.centroid """ return pygeos.coordinates.get_coordinates(pygeos.centroid(shape)).squeeze()
# Is in EPSG:5070 but not recognized as such print("Reading CHAT data...") df = (read_dataframe(src_dir / "WAFWA_CHAT_Lower48.shp").set_crs(DATA_CRS).drop( columns=["ls_cond"]).rename(columns=field_map).rename( columns={"chat_rank": "chatrank"})) for col in chat_fields: df[col] = df[col].astype("uint8") df = df.drop(columns=["hexagon_id"]) ### Find the CHAT units that intersect with OK / TX input areas # Use centerpoints, since input area roughly follows edges of hexes points = pg.centroid(df.geometry.values.data) tree = pg.STRtree(points) for state in ["ok", "tx"]: print(f"Processing {state} CHAT...") input_area = pg.union_all( inputs_df.loc[inputs_df.inputs == f"{state}chat"].geometry.values.data) ix = tree.query(input_area, predicate="intersects") state_df = df.iloc[ix].reset_index(drop=True) # lcon not present for TX if state == "tx": state_df = state_df.drop(columns=["lcon"]) # Reclassify chatrank to match blueprint integration rules. # First shift other values up one ix = state_df.chatrank >= 2
def test_centroid(): actual = pygeos.centroid(polygon) assert pygeos.equals(actual, pygeos.points(1, 1))
def centroid(data): if compat.USE_PYGEOS: return pygeos.centroid(data) else: return _unary_geo("centroid", data)
def bbox_to_point(bbox): if isinstance(bbox[0], Geometry): return centroid(bbox[0]) elif isinstance(bbox, (list, tuple, np.ndarray)): return centroid(box(*bbox[:4]))