# PySGN: A Python package for constructing synthetic geospatial networks [![GitHub CI](https://github.com/wang-boyu/pysgn/actions/workflows/build.yml/badge.svg)](https://github.com/wang-boyu/pysgn/actions) [![Read the Docs](https://readthedocs.org/projects/pysgn/badge/?version=stable)](https://pysgn.readthedocs.io/en/stable) [![Codecov](https://codecov.io/gh/wang-boyu/pysgn/branch/main/graph/badge.svg)](https://codecov.io/gh/wang-boyu/pysgn) [![PyPI](https://img.shields.io/pypi/v/pysgn.svg)](https://pypi.org/project/pysgn) [![PyPI - License](https://img.shields.io/pypi/l/pysgn)](https://pypi.org/project/pysgn/) [![DOI](https://zenodo.org/badge/DOI/10.21105/joss.09346.svg)](https://doi.org/10.21105/joss.09346) ## Introduction PySGN (**Py**thon for **S**ynthetic **G**eospatial **N**etworks) is a Python package for constructing synthetic geospatial networks. It is built on top of the [NetworkX](https://networkx.github.io/) package, which provides a flexible and efficient data structure for representing complex networks and [GeoPandas](https://geopandas.org/), which extends the datatypes used by pandas to allow spatial operations on geometric types. PySGN is designed to be easy to use and flexible, allowing users to generate networks with a wide range of characteristics. ## Installation PySGN can be installed using `pip`: ```bash pip install pysgn ``` If you plan to run the code snippets below or the Getting Started notebook `docs/getting_started.ipynb` locally, install the optional `docs` extras to get the other dependencies such as geodatasets, Jupyter and Sphinx: ```bash pip install "pysgn[docs]" ``` Alternatively, PySGN is available on `conda-forge` and can be installed with: ```bash conda install -c conda-forge pysgn ``` To work from source, you may clone this repository: ```bash git clone https://github.com/wang-boyu/pysgn.git cd pysgn ``` then either: - using `pip` in editable mode ```bash pip install -e . # or with extras pip install -e ".[docs]" ``` - using `conda` with the provided `environment.yml`. ```bash conda env create -f environment.yml conda activate pysgn ``` This installs PySGN in editable mode (`pip install -e .`), so no additional installation step is required. ## Usage Example ### Geospatial Erdős-Rényi Network Here's a simple example of how to use the `geo_erdos_renyi_network` function to create a geospatial Erdős-Rényi network. It generates a network where each pair of nodes is connected with probability `p`, which depends on the spatial distance between the nodes. The parameter `a` controls the rate of decay of the connection probability with distance. All PySGN functions expect the input GeoDataFrame to contain a single geometry type (Points or Polygons). ```python import geodatasets import geopandas as gpd from pysgn import geo_erdos_renyi_network # Load the sample grocery-store points from geodatasets # and explode the GeoDataFrame into single points (one point per row). gdf = ( gpd.read_file(geodatasets.get_path("geoda.groceries")) .explode(index_parts=False) .reset_index(drop=True) .to_crs("EPSG:26971") ) # Create a geospatial Erdős-Rényi network graph = geo_erdos_renyi_network(gdf, a=3) # Output the number of nodes and edges print(f"Number of nodes: {graph.number_of_nodes()}") print(f"Number of edges: {graph.number_of_edges()}") ``` ### Geospatial Watts-Strogatz Network Similarly you can use the `geo_watts_strogatz_network` function to create a geospatial Watts-Strogatz network. It first creates a network where each node is connected to its `k` nearest neighbors. Then, it rewires each edge with probability `p`. If an edge is chosen to be rewired, it is replaced with a new edge to a random node, where the probability of connecting to this new node is inversely proportional to the spatial distance. ```python import geodatasets import geopandas as gpd from pysgn import geo_watts_strogatz_network gdf = ( gpd.read_file(geodatasets.get_path("geoda.groceries")) .explode(index_parts=False) .reset_index(drop=True) .to_crs("EPSG:26971") ) # Create a geospatial Watts-Strogatz network graph = geo_watts_strogatz_network( gdf, k=4, # Each node is connected to k nearest neighbors p=0.1, # Probability of rewiring each edge a=2, # Distance decay exponent ) # Output the number of nodes and edges print(f"Number of nodes: {graph.number_of_nodes()}") print(f"Number of edges: {graph.number_of_edges()}") ``` ### Geospatial Barabási-Albert Network You can also use the `geo_barabasi_albert_network` function to create a geospatial Barabási-Albert network. It creates a network using geospatial preferential attachment, where the probability of connecting to existing nodes depends on both their degrees and the spatial distances. ```python import geodatasets import geopandas as gpd from pysgn import geo_barabasi_albert_network from pysgn.ordering import density_order gdf = ( gpd.read_file(geodatasets.get_path("geoda.groceries")) .explode(index_parts=False) .reset_index(drop=True) .to_crs("EPSG:26971") ) # Create a geospatial Barabási-Albert network graph = geo_barabasi_albert_network( gdf, m=3, # Each new node connects to 3 existing nodes a=2, # Distance decay exponent max_degree=150, # Maximum degree constraint # Use density-based node ordering (nodes in dense areas join first) node_order=lambda gdf: density_order(gdf, method='knn'), ) # Output the number of nodes and edges print(f"Number of nodes: {graph.number_of_nodes()}") print(f"Number of edges: {graph.number_of_edges()}") ``` ### Export to GeoDataFrames Once you have a graph, you can convert it back to GeoPandas GeoDataFrames for GIS workflows or file export. ```python from pysgn import graph_to_gdf nodes_gdf, edges_gdf = graph_to_gdf(graph) ``` ## Documentation For more information on how to use PySGN, please refer to the [documentation](https://pysgn.readthedocs.io/en/stable). ## Contributing If you run into an issue, please file a [ticket](https://github.com/wang-boyu/pysgn/issues) for us to discuss. If possible, follow up with a pull request. If you would like to add a feature, please reach out via [ticket](https://github.com/wang-boyu/pysgn/issues) or start a [discussion](https://github.com/wang-boyu/pysgn/discussions). A feature is most likely to be added if you build it! Don't forget to check out the [Contributors guide](https://github.com/wang-boyu/pysgn/blob/main/CONTRIBUTING.md). ## License PySGN is released under the MIT License. ```{toctree} --- maxdepth: 2 hidden: true --- Introduction Getting Started Utility Functions API Documentation ``` ## Indices and tables - {ref}`genindex` - {ref}`modindex` - {ref}`search`