Spatial Access
spatial-access is a Python package designed for measuring spatial accessibility to services. It provides tools to construct road networks, represent facilities and demand points, and compute various accessibility metrics like the Two-Step Floating Catchment Area (2SFCA) method. The current version is 1.0.2, and releases are made as needed to address issues and introduce features.
Common errors
-
ModuleNotFoundError: No module named 'spatial-access'
cause Attempting to import the library using a hyphen instead of an underscore, or the package is not installed.fixEnsure the package is installed via `pip install spatial-access` and then import using `import spatial_access as sa`. -
KeyError: 'some_missing_column_name'
cause A GeoDataFrame provided to `Network` or `Access` is missing a required column (e.g., weight, capacity, demand) that was specified by name.fixCheck your input GeoDataFrames (`nodes`, `edges`, `facilities`, `demand`) and ensure they contain the columns specified in the `spatial_access.Network` and `spatial_access.Access` constructors. Column names are case-sensitive. -
ValueError: The input network must be connected. Consider filtering to the largest connected component.
cause The road network provided to `spatial_access.Network` is not fully connected (e.g., contains isolated islands of roads), which prevents shortest path calculations between all points.fixPre-process your road network to ensure connectivity. You can often extract the largest connected component using `networkx` functions or similar tools before passing the data to `spatial_access`. -
pygeos.GEOSException: IllegalArgumentException: Points of LineString must have distinct coordinates
cause Input geometry data (especially LineStrings in edges) contains invalid or malformed geometries, such as a LineString with identical start and end points, or other self-intersections.fixInspect and clean your input GeoDataFrames for invalid geometries. Use `gdf.geometry.is_valid` to identify issues and `gdf.geometry.buffer(0)` or other `shapely` / `geopandas` tools for minor repairs.
Warnings
- breaking Prior to version 1.0.2, the internal C++ Dijkstra implementation could crash when the input road network contained multiple disconnected components, leading to unexpected program termination.
- gotcha Dropping non-existent columns from a road network GeoDataFrame would previously raise an error, interrupting the workflow.
- gotcha Coordinate Reference System (CRS) mismatches between input GeoDataFrames (nodes, edges, facilities, demand) can lead to incorrect distance calculations or errors during spatial operations. All input GeoDataFrames must share the same CRS.
- gotcha The `spatial_access.Network` object expects specific column names for weights (e.g., 'length', 'travel_time') and the `Access` object expects 'capacity' and 'demand' columns. Using incorrect column names will result in `KeyError`.
Install
-
pip install spatial-access
Imports
- spatial_access
import spatial-access
import spatial_access as sa
- Network
network = sa.Network(nodes, edges, 'weight_col')
- Access
access_model = sa.Access(network, facilities, demand, 'facility_col', 'demand_col')
Quickstart
from shapely.geometry import Point, LineString
import geopandas as gpd
import pandas as pd
import spatial_access as sa
# 1. Create dummy network data (nodes and edges)
# Nodes represent intersections or endpoints
nodes_df = pd.DataFrame({
'id': [0, 1, 2, 3],
'geometry': [Point(0, 0), Point(1, 0), Point(0, 1), Point(1, 1)]
})
nodes = gpd.GeoDataFrame(nodes_df, crs="EPSG:4326")
# Edges represent road segments connecting nodes
edges_df = pd.DataFrame({
'id': [0, 1, 2, 3],
'from': [0, 0, 1, 2],
'to': [1, 2, 3, 3],
'length': [1.0, 1.0, 1.0, 1.0], # A 'weight' or 'cost' column
'geometry': [
LineString([(0, 0), (1, 0)]),
LineString([(0, 0), (0, 1)]),
LineString([(1, 0), (1, 1)]),
LineString([(0, 1), (1, 1)])
]
})
edges = gpd.GeoDataFrame(edges_df, crs="EPSG:4326")
# 2. Create dummy facility and demand data
# Facilities (e.g., hospitals, stores) with capacity
facilities_df = pd.DataFrame({
'id': [0, 1],
'capacity': [10, 15],
'geometry': [Point(0.1, 0.1), Point(0.9, 0.9)]
})
facilities = gpd.GeoDataFrame(facilities_df, crs="EPSG:4326")
# Demand points (e.g., population centroids) with demand
demand_df = pd.DataFrame({
'id': [0, 1],
'demand': [5, 8],
'geometry': [Point(0.2, 0.2), Point(0.8, 0.8)]
})
demand = gpd.GeoDataFrame(demand_df, crs="EPSG:4326")
# 3. Initialize Network and Access objects
# The 'length' column is used as the weight for shortest path calculations
network = sa.Network(nodes, edges, "length")
# Create the Access model, linking network, facilities, and demand
access_model = sa.Access(network, facilities, demand, "capacity", "demand")
# 4. Compute 2SFCA accessibility
# Using a distance threshold of 10 units (arbitrary for dummy data)
# and a Gaussian weight function. The result is (Si, Dj) where Si is
# accessibility for demand points and Dj for facilities.
print("Calculating 2SFCA accessibility...")
accessibility_scores = access_model.two_sfca(10, weight_function="gaussian")
# Print accessibility scores for demand points
print("\nAccessibility scores for demand points (Si):")
print(accessibility_scores[0].head())