Skip to content

Spatial networks

A SpatialNetwork holds places as nodes with coordinates, joined by edges with a length in meters or another weight. A road network from OSMRoadsFetcher is one kind. The network is two tables, and every edit returns a new network. The road networks guide shows a full workflow.

from gigaspatial import SpatialNetwork, OSMRoadsFetcher

to_pandana and backend="pandana" need pip install "giga-spatial[pandana]". Why the network is held as tables, and how a saved network is laid out, are in ADR 0004 and ADR 0005.

The methods below are in this order: building, saving, and converting; reading the tables; editing; inserting points; algorithms; and reducing.

Bases: EditingMixin, AlgorithmsMixin, ReductionMixin, InsertionMixin

A spatial network held as a nodes table and an edges table.

Nodes carry coordinates in EPSG:4326, and edges carry a length in meters or another weight. A road network fetched by OSMRoadsFetcher is one kind; points joined to their nearest neighbours are another.

The nodes table is indexed by node_id and has the columns x (longitude), y (latitude), and kind, plus any attributes. The edges table has u, v, key, length (meters), kind, an optional geometry column of LineStrings, and any attributes. An edge is identified by (u, v, key).

A network never changes after it is built: methods that edit it return a new network. The tables returned by nodes and edges are shallow copies; adding columns to them does not change the network, but do not modify their values in place.

Parameters:

Name Type Description Default
nodes DataFrame

Node table, indexed by node ID or with a node_id column, with x and y columns or Point geometries.

required
edges DataFrame

Edge table with u and v columns. A missing key is numbered per node pair, and a missing length is computed along the geometry, or between the endpoints when there is none.

required
directed bool

Whether edges run from u to v only. A two-way road in a directed network is two rows, one per direction.

True
crs Any

CRS of the input coordinates. They are converted to EPSG:4326. The CRS of a GeoDataFrame takes precedence for its geometries.

WGS84
weight str

Default edge column for algorithms.

'length'
metadata Optional[Dict[str, Any]]

Provenance, such as the fetch parameters.

None

Raises:

Type Description
ValueError

If node IDs repeat, an edge references an unknown node, an edge (u, v, key) repeats, or the weight column is missing.

nodes property

The nodes table, indexed by node ID.

edges property

The edges table.

nodes_gdf cached property

Nodes as a GeoDataFrame of Points in EPSG:4326.

edges_gdf cached property

Edges as a GeoDataFrame in EPSG:4326, with straight lines where geometry is missing.

node_index property

Node IDs by position: node_index[i] is the ID at position i, and node_index.get_indexer(ids) gives positions.

bounds property

(minx, miny, maxx, maxy) of the nodes and edge geometries, in EPSG:4326.

from_networkx(G, weight='length') classmethod

Build a network from a networkx graph whose nodes have x and y.

osmnx graphs work directly. Whether the network is directed comes from the graph, and the CRS from G.graph["crs"] (EPSG:4326 if absent). Graphs without edge keys get key 0. The other graph attributes become the network's metadata.

Parameters:

Name Type Description Default
G Graph

Any networkx graph.

required
weight str

Default edge column for algorithms.

'length'

Returns:

Type Description
SpatialNetwork

The network.

from_points(points, connect='knn', k=5, max_distance=None, id_column=None, weight='length', weight_func=None) classmethod

Build an undirected network whose nodes are the points.

Parameters:

Name Type Description Default
points Any

A GeoDataFrame of points, a DataFrame with latitude and longitude columns, or a list of (lat, lon) tuples or Points.

required
connect str

"knn" joins each point to its k nearest neighbours; "distance" joins every pair within max_distance meters.

'knn'
k int

Number of neighbours for "knn".

5
max_distance Optional[float]

Longest edge in meters. Required for "distance"; with "knn" it drops longer neighbour links.

None
id_column Optional[str]

Column holding node IDs. The frame's index otherwise.

None
weight str

Default weight column. Anything but "length" needs weight_func.

'length'
weight_func Optional[Callable[[DataFrame, DataFrame, ndarray], Any]]

Computes the weight column as weight_func(nodes_u, nodes_v, length), where nodes_u and nodes_v are the node rows at each end and length is in meters.

None

Returns:

Type Description
SpatialNetwork

The network. Nodes have kind "point" and edges "point_link".

load(path, data_store=None, columns=None) classmethod

Load a network saved with :meth:save.

List attributes such as osmid come back as one numpy array per edge, and text attributes as categoricals.

Parameters:

Name Type Description Default
path Any

Folder the network was saved to.

required
data_store Optional[Any]

Data store to read from. The local file system otherwise.

None
columns Optional[Iterable[str]]

Edge attribute columns to read, in addition to u, v, key, length, kind, and the weight column. Include "geometry" to keep edge geometry. All columns otherwise.

None

Raises:

Type Description
FileNotFoundError

If there is no saved network at path.

ValueError

If the folder was saved by a newer format version, or columns names a column that was not saved.

save(path, data_store=None)

Save the network as a folder: nodes.parquet, edges.parquet, and network.json.

Edges are GeoParquet with WKB geometry, readable by geopandas, DuckDB, and QGIS; without edge geometry, edges.parquet is plain Parquet. Attributes that mix lists and single values, as osmnx produces, are normalised first (see normalize_attributes); the network itself is not changed. Existing files in the folder are overwritten.

Parameters:

Name Type Description Default
path Any

Folder to write.

required
data_store Optional[Any]

Data store to write to. The local file system otherwise.

None

Raises:

Type Description
ValueError

If node IDs are neither integers nor text, or a column cannot be stored.

TypeError

If the metadata is not JSON serializable.

to_networkx(directed=None)

A new networkx graph with every node and edge attribute.

Missing values are left out of the attribute dictionaries, so straight edges have no geometry, as in osmnx. G.graph holds the metadata and crs. The graph is built on every call and not cached.

Parameters:

Name Type Description Default
directed Optional[bool]

True gives a MultiDiGraph; from an undirected network, every edge is added in both directions, with its geometry reversed for the reverse direction. False gives a MultiGraph; from a directed network, a reverse edge with the same key replaces the forward one, as networkx's to_undirected does. Defaults to how the network is directed.

None

Returns:

Type Description
MultiGraph

nx.MultiDiGraph or nx.MultiGraph.

to_pandana(weight=None)

A pandana network indexed by node ID, with twoway set to whether the network is undirected.

Built once per weight and cached. Needs pip install 'giga-spatial[pandana]'. backend="pandana" does not need it: it works on any network.

Raises:

Type Description
ValueError

If node IDs mix integers and text, which pandana cannot index.

to_geodataframes(crs=None)

Nodes and edges as GeoDataFrames, projected to crs if given.

node_coordinates(ids=None, crs=None)

Node coordinates as an array of (y, x) rows.

In EPSG:4326 that is (lat, lon). With crs, the coordinates are projected and keep the (y, x) order.

Parameters:

Name Type Description Default
ids Any

Node IDs, in the order wanted. All nodes otherwise.

None
crs Any

CRS to project to.

None

Raises:

Type Description
KeyError

If an ID is not in the network.

degree(directed=None)

Number of edges at each node.

Parameters:

Name Type Description Default
directed Optional[bool]

With True, every edge row counts at both of its ends, as networkx counts a multigraph's degree. With False, reverse and parallel edges between the same two nodes count once, so the degree is the number of roads meeting at a node. Defaults to how the network is directed.

None

Returns:

Type Description
Series

Degrees indexed by node ID. A self-loop counts twice.

total_length(by=None)

Sum of edge lengths in meters, optionally grouped by an edge column.

In a directed network a two-way road is two rows, so it counts twice.

info()

Summary of the network.

nearest_nodes(points, max_distance=None, return_distance=False)

The node closest to each point, by great-circle distance.

Parameters:

Name Type Description Default
points Any

A GeoDataFrame of points, a DataFrame with latitude and longitude columns, or a list of (lat, lon) tuples or Points.

required
max_distance Optional[float]

Meters. Points with no node this close get a missing ID.

None
return_distance bool

Also return the distance in meters.

False

Returns:

Type Description
Any

A Series of node IDs indexed like the points, or with

Any

return_distance a DataFrame with node_id and distance.

filter_nodes(ids=None, mask=None, geometry=None, predicate='intersects')

Keep the selected nodes and the edges between them.

Pass exactly one of ids, mask, or geometry.

Parameters:

Name Type Description Default
ids Any

Node IDs to keep.

None
mask Any

Booleans in node order, or a boolean Series indexed by node ID.

None
geometry Any

Area in EPSG:4326, a shapely geometry or a GeoDataFrame or GeoSeries in any CRS.

None
predicate str

"intersects" or "within", for geometry.

'intersects'

remove_nodes(ids=None, points=None, geometry=None, max_distance=None, predicate='intersects')

Remove nodes and the edges attached to them.

Pass exactly one of ids, points, or geometry.

Parameters:

Name Type Description Default
ids Any

Node IDs to remove.

None
points Any

Remove the node nearest each point, within max_distance meters.

None
geometry Any

Remove the nodes in this area (see :meth:filter_nodes).

None
max_distance Optional[float]

Meters, for points.

None
predicate str

"intersects" or "within", for geometry.

'intersects'

set_node_attribute(name, values)

Set one node attribute.

Parameters:

Name Type Description Default
name str

Column to set or create. x and y cannot be set.

required
values Any

A single value for every node, values in node order, or a Series or dict keyed by node ID. With a Series or dict, other nodes keep their value, or get a missing value in a new column.

required

Raises:

Type Description
KeyError

If a key is not a node ID.

update_nodes(frame, by='id', max_distance=None)

Update several node attributes from a table.

Values that are present overwrite; missing values leave the node's value as it is. New columns are created, missing for other nodes.

Parameters:

Name Type Description Default
frame DataFrame

With by="id", a table indexed by node ID (or with a node_id column). With by="coordinates", a points input whose rows are matched to their nearest node; when two rows match the same node, the later row wins.

required
by str

"id" or "coordinates".

'id'
max_distance Optional[float]

Meters, for by="coordinates". Rows with no node this close are skipped, with a warning.

None

Raises:

Type Description
KeyError

If by="id" and a row is not a node ID.

add_nodes(frame, id_column=None, kind='point')

Add nodes from a points input with IDs.

Parameters:

Name Type Description Default
frame Any

A GeoDataFrame of points, a DataFrame with latitude and longitude columns, or a list of (lat, lon) tuples.

required
id_column Optional[str]

Column holding the node IDs. The frame's index otherwise.

None
kind str

Kind of the new nodes.

'point'

Raises:

Type Description
ValueError

If an ID is already a node of the network.

filter_edges(mask=None, geometry=None, predicate='intersects')

Keep the selected edges. All nodes are kept, so some may be left without edges.

Pass exactly one of mask or geometry.

Parameters:

Name Type Description Default
mask Any

Booleans in edge order.

None
geometry Any

Area in EPSG:4326, a shapely geometry or a GeoDataFrame or GeoSeries in any CRS. Straight edges are tested as lines between their nodes.

None
predicate str

The edge's relation to the area: "intersects", "within", "crosses", or "touches".

'intersects'

remove_edges(edges=None, mask=None)

Remove edges. Nodes are kept.

Pass exactly one of edges or mask.

Parameters:

Name Type Description Default
edges Any

(u, v, key) or (u, v) tuples, or a table with u, v, and optionally key columns. Without a key, every edge between the two nodes is removed. In an undirected network, (v, u) matches (u, v).

None
mask Any

Booleans in edge order.

None

Raises:

Type Description
KeyError

If an edge is not in the network.

set_edge_attribute(name, values)

Set one edge attribute.

Parameters:

Name Type Description Default
name str

Column to set or create. u, v, and key cannot be set.

required
values Any

A single value for every edge, values in edge order, or a Series indexed by (u, v, key) or a dict keyed by (u, v, key) tuples. With a Series or dict, other edges keep their value, or get a missing value in a new column.

required

Raises:

Type Description
KeyError

If a key is not an edge of the network.

update_edges(frame)

Update several edge attributes from a table with u, v, and key columns.

Values that are present overwrite; missing values leave the edge's value as it is. New columns are created, missing for other edges. In an undirected network, (v, u, key) matches (u, v, key).

Raises:

Type Description
KeyError

If a row is not an edge of the network.

add_weighted_edges(u, v, weights, weight='length', kind='added', **attributes)

Add edges with a weight, like networkx's add_weighted_edges_from but with arrays.

Keys continue from the edges already between each pair of nodes. When weight is not "length" and no length is given, the length is computed along the given geometry, or between the endpoints.

Parameters:

Name Type Description Default
u Sequence[Any]

Start node IDs.

required
v Sequence[Any]

End node IDs.

required
weights Sequence[float]

The weight of each edge, stored in the weight column.

required
weight str

Column for weights.

'length'
kind str

Kind of the new edges.

'added'
**attributes Any

More columns, each a single value or one value per edge.

{}

Raises:

Type Description
KeyError

If an endpoint is not a node of the network.

clip(geometry, predicate='intersects')

The edges matching predicate with the area, and their endpoints.

Parameters:

Name Type Description Default
geometry Any

Area in EPSG:4326, a shapely geometry or a GeoDataFrame or GeoSeries in any CRS.

required
predicate str

"intersects", "within", "crosses", or "touches".

'intersects'

drop_geometry()

The network without edge geometry, to save memory when only the topology matters.

to_undirected(weight=None)

An undirected network with one edge per pair of nodes.

Reverse and parallel edges are merged into the one with the smallest weight, which keeps its attributes and geometry; every key becomes 0.

Parameters:

Name Type Description Default
weight Optional[str]

Edge column to compare. The network's weight otherwise.

None

concat(networks) classmethod

Combine networks into one.

Nodes with the same ID are the same node, as where separately fetched areas meet; the first network's row wins. Edges with the same (u, v, key) are kept once. The weight is the first network's; the metadata is shared metadata, or the list of each network's metadata under "parts".

Raises:

Type Description
ValueError

If the networks are not all directed or all undirected.

insert_points(points, id_column=None, max_distance=None, snap_to_node=5.0, chain=False, return_skipped=False)

Insert points of interest as nodes, each joined to its nearest road.

Each point is joined to the nearest road edge at the foot of the perpendicular: the edge is split there, a node of kind split is added, and a connection edge joins the point (kind point) to it. When the foot is within snap_to_node meters of the edge's end, the point connects to that node instead. Points whose feet fall on the same spot share one split node. Each road edge is split once, at all of its points, and the pieces' lengths add up exactly to the original length. In a directed network, both directions of a two-way road are split at the same nodes, and connections run both ways.

Parameters:

Name Type Description Default
points Any

A GeoDataFrame of points, a DataFrame with latitude and longitude columns, or a list of (lat, lon) tuples. Their other columns become node attributes.

required
id_column Optional[str]

Column holding the point IDs. The frame's index otherwise.

None
max_distance Optional[float]

Meters. Points farther from any road are skipped, with a warning.

None
snap_to_node float

Meters. Connect to the edge's end node when the foot is this close to it.

5.0
chain bool

Handle points nearest to the road first, and join a point to an already inserted point (a point_link edge) when that point is closer than any road.

False
return_skipped bool

Also return the points that were skipped.

False

Returns:

Type Description
Any

The new network, or (network, skipped_points). Split nodes get

Any

negative integer IDs, continuing below the lowest existing one.

Any

When the network's weight is not length, new connection edges

Any

get that weight equal to their length, and road pieces get their

Any

share of the road's weight.

Raises:

Type Description
ValueError

If point IDs repeat or are already node IDs, or a point column is named x, y, or kind.

adjacency(weight=None, directed=None)

Sparse adjacency matrix by node position (see node_index).

Parallel edges keep the smallest weight. An undirected network, or directed=False, gives a symmetric matrix.

Parameters:

Name Type Description Default
weight Optional[str]

Edge column. The network's weight otherwise.

None
directed Optional[bool]

False ignores one-way restrictions of a directed network.

None

Raises:

Type Description
ValueError

If the weight is missing or negative on some edge.

shortest_path_lengths(origins, destinations=None, pairwise=False, weight=None, backend='scipy', directed=None)

Shortest path lengths between nodes.

Parameters:

Name Type Description Default
origins Any

Origin node IDs.

required
destinations Any

Destination node IDs. All nodes otherwise.

None
pairwise bool

Pair origins[i] with destinations[i] instead of building the full matrix.

False
weight Optional[str]

Edge column to add up. The network's weight otherwise.

None
backend str

"scipy", or "pandana" for very many pairs once its contraction hierarchy is built. pandana stores weights with limited precision, so its lengths can differ by a fraction of a meter.

'scipy'
directed Optional[bool]

False ignores one-way restrictions.

None

Returns:

Type Description
Any

With pairwise, an array with one length per pair. Otherwise a

Any

DataFrame indexed by origin with one column per destination.

Any

Unreachable pairs are inf.

Raises:

Type Description
ValueError

If the matrix would exceed MAX_MATRIX_BYTES; split the origins into batches.

KeyError

If a node ID is not in the network.

shortest_paths(origins, destinations, pairwise=True, weight=None, geometry=False, directed=None)

Shortest paths between nodes, as node sequences or lines.

Parameters:

Name Type Description Default
origins Any

Origin node IDs.

required
destinations Any

Destination node IDs. With pairwise, origins[i] goes to destinations[i]; otherwise every origin goes to every destination, origin by origin.

required
pairwise bool

Pair origins and destinations by position.

True
weight Optional[str]

Edge column to minimise. The network's weight otherwise.

None
geometry bool

Return routes as lines with their lengths.

False
directed Optional[bool]

False ignores one-way restrictions.

None

Returns:

Type Description
Any

A list of node ID lists, empty where there is no path. With

Any

geometry, a GeoDataFrame with origin, destination,

Any

length, nodes, and a LineString geometry (missing when

Any

there is no path or the origin is the destination).

reachable(origins, max_distance, weight=None, directed=None)

Every node within a network distance of each origin.

Returns:

Type Description
DataFrame

A long table with origin, node_id, and distance, sorted

DataFrame

by origin and distance; each distinct origin appears once, reaching

DataFrame

itself at distance 0.

nearest_by_network(origins, targets, k=1, max_distance=None, weight=None, directed=None)

The nearest targets to each origin by network distance, such as each school's nearest fiber node.

Parameters:

Name Type Description Default
origins Any

Origin node IDs.

required
targets Any

Target node IDs.

required
k int

Number of targets per origin. k=1 is a single multi-source search from all targets.

1
max_distance Optional[float]

Ignore targets farther than this.

None

Returns:

Type Description
DataFrame

A table with origin, target, and distance: one row per

DataFrame

origin for k=1 (target missing and distance inf when

DataFrame

none is reachable), and up to k rows per origin otherwise,

DataFrame

nearest first.

count_within(origins, targets, max_distance, weight=None, directed=None)

How many targets each origin reaches within a network distance, such as clinics within 5 km by road.

Targets are counted once each; an origin that is also a target counts itself.

Returns:

Type Description
Series

Counts indexed by origin ID, in the order given.

connected_components(strong=False)

Component label of each node; label 0 is the largest component, then by size.

Parameters:

Name Type Description Default
strong bool

In a directed network, components in which every node can reach every other along one-way roads. Weak components ignore direction. The same for undirected networks.

False

largest_component(strong=False)

The network's largest connected component.

connect_components(max_distance=None)

Join disconnected parts with component_link edges between their nearest nodes.

The links form a minimum spanning tree over the components by great-circle distance, found with Borůvka's method. In a directed network each link is added in both directions.

Parameters:

Name Type Description Default
max_distance Optional[float]

Meters. Parts farther apart than this stay apart.

None

Returns:

Type Description
SpatialNetwork

The network with the links added, or the same network when it is

SpatialNetwork

already connected.

minimum_spanning_tree(weight=None)

An undirected network of the edges that join every component at the smallest total weight.

Directions are ignored, all nodes are kept (a forest when the network is disconnected), and keys become 0.

simplify(keep=None, sum_columns=('length',))

Remove nodes that only continue a road, joining the edges on either side.

A node is removed when it has exactly two neighbours and its edges continue through it: one edge in and one out of a one-way road, or both directions of a two-way road (in an undirected network, exactly two edges). This is the rule osmnx uses, except that osmnx also removes nodes between duplicated parallel ways and drops one of the ways, and deletes rings with no junction at all; here those nodes stay, and a ring keeps one node. Nodes of kind point or split and nodes in keep are never removed.

Joined edges get:

  • the sum of sum_columns and of the network's weight column;
  • one geometry along the joined edges;
  • text joined with ; where values differ, without repeats;
  • osmid and other list columns concatenated, without repeats;
  • oneway true only if every part is one-way, reversed true if any part is, other booleans true only if all parts are;
  • the length-weighted mean of other numeric columns;
  • the kind of their longest part.

Each direction of a two-way road is joined separately, so it stays two-way when both directions can be joined. Joined edges always get a geometry, as in osmnx, so a network without edge geometry gains a geometry column that is missing for the edges left as they were.

Parameters:

Name Type Description Default
keep Any

Node IDs to keep.

None
sum_columns Iterable[str]

Edge columns to add up.

('length',)

Returns:

Type Description
SpatialNetwork

The simplified network, or the same network when no node can be removed.

connecting_subnetwork(terminals, roots=None, method='shortest_path', weight=None)

The part of the network that joins a set of points, contracted to the points and branching nodes.

The network of :meth:ConnectionPlanner.plan. Directions are ignored. The result is undirected; it keeps the terminals, the roots, and the nodes where paths branch, with joined edges as :meth:simplify makes them, so lengths are added up and geometries joined. Use :class:ConnectionPlanner for each terminal's status, its distance to a root, and the plan's cost.

Parameters:

Name Type Description Default
terminals Any

Node IDs to join, such as schools.

required
roots Any

Node IDs already connected to each other, such as fibre nodes. Reaching any root is enough.

None
method str

"shortest_path" (the default): terminals connected one at a time, nearest first, to everything connected so far (Takahashi and Matsuyama). "steiner": Mehlhorn's approximation, faster and on roads a few percent longer. Both are Steiner tree approximations: with roots, a forest whose every tree reaches a root, so a terminal may connect through other terminals. "nearest_root": each terminal's shortest path to its nearest root. "all_pairs": the shortest paths between every terminal and every terminal and root; their number grows with the square of the terminals.

'shortest_path'
weight Optional[str]

Edge column to minimise. The network's weight otherwise.

None

Returns:

Type Description
SpatialNetwork

An undirected network. Terminals that cannot be connected are kept

SpatialNetwork

without edges.

Connection planning

ConnectionPlanner plans how to connect terminals, such as schools, to roots, such as fibre nodes, along a network at the smallest shared cost, and returns a ConnectionPlan. Why it is a rooted Steiner tree and a separate object is in ADR 0008.

from gigaspatial import ConnectionPlanner

plan = ConnectionPlanner(network, school_ids, roots=fibre_ids, weight="cost").plan()

Plans how to connect terminals to roots along a spatial network at the smallest shared cost.

Terminals are the points to connect, such as schools. Roots are nodes already connected to each other through an existing network, such as fibre nodes, a power grid's substations, or water mains; reaching any root is enough. A terminal may reach a root directly or through other terminals, and an edge used by several terminals is paid once. Directions are ignored, since a cable or a pipe does not follow one-way streets.

With roots, this is the Steiner tree problem once every root is joined to one virtual root at weight 0: the plan is a forest whose every tree reaches a root. Without roots, it is the Steiner tree joining the terminals. Both are NP-hard; the planner returns approximations.

Insert the terminals and roots into the network first, with :meth:SpatialNetwork.insert_points, and pass their node IDs.

Parameters:

Name Type Description Default
network SpatialNetwork

The network to plan along.

required
terminals Any

Node IDs to connect.

required
roots Any

Node IDs already connected to each other.

()
weight Optional[str]

Edge column to minimise, such as a cost. The network's weight otherwise. Zero is allowed.

None
bridge_cost Optional[float]

Weight per metre of a straight-line link between parts of the network that no road joins, such as a microwave link or a planned road, in the units of weight. No bridges when None. Each part is offered links to its nearest other parts, at most five, and a plan uses a link only where it is cheaper than the roads.

None
bridge_max_distance float

Longest link, in metres.

5000.0

Raises:

Type Description
KeyError

If a terminal or root is not a node of the network.

ValueError

If the weight is missing or negative on some edge, or the bridge options are negative.

plan(method='shortest_path')

Plan the connections.

Parameters:

Name Type Description Default
method str

"shortest_path" (the default): Takahashi and Matsuyama's 2-approximation of the Steiner tree, and a model of a phased rollout: starting from the roots (or the first terminal), repeatedly connect the terminal nearest to anything connected so far, along its shortest path. Gives each terminal's order and marginal cost; ties go to the terminal listed first. On Kigali's roads it came within 0.7 to 6% of the optimum. "steiner": Mehlhorn's 2-approximation, with roots the rooted tree, without them the tree joining the terminals; 5 to 20 times faster, and 3 to 9% above the optimum on Kigali. :meth:improve brings either within about 1% of it. "nearest_root": each terminal's shortest path to its nearest root, and the union of those paths; needs roots, and never lets a terminal connect through another one. "all_pairs": the shortest paths between every terminal and every terminal or root; their number grows with the square of the terminals.

'shortest_path'

Returns:

Type Description
ConnectionPlan

The plan. With roots, terminals that cannot reach any root, even

ConnectionPlan

through bridges, are unreachable. Without roots, the plan joins the

ConnectionPlan

terminals of the network's part that holds the most of them, and

ConnectionPlan

the others are unreachable.

Raises:

Type Description
ValueError

If the method is unknown, or is "nearest_root" without roots.

The result of a connection planner.

Attributes:

Name Type Description
network SpatialNetwork

The connecting subnetwork, undirected, contracted to the terminals, the roots, the ends of bridges, and the nodes where paths branch, with lengths added up and geometries joined as :meth:SpatialNetwork.simplify does. Unreachable terminals and unused roots are kept without edges.

edges GeoDataFrame

The chosen edges, uncontracted, one row per pair of nodes with the attributes of its cheapest edge and a geometry (a straight line where the network has none). Bridges have kind "bridge", their great-circle length, and their weight in the weight column unless the weight is length.

terminals DataFrame

One row per terminal, indexed by node ID: status ("connected", "root" for a terminal that is itself a root, or "unreachable"); root, the root its tree reaches nearest along the plan; distance, the weight along the plan to that root; and bridged, whether that path uses a bridge. Without roots, root is missing, distance is NaN, and bridged says whether the terminal is joined to the others only through a bridge. Plans by "shortest_path" add order, when the terminal was connected (0 for a terminal that is a root, missing when unreachable), and marginal_cost, the weight of the edges its connection added.

cost float

The total weight of edges, with each bridge at bridge_cost per metre.

metadata Dict[str, Any]

The method, the weight, bridge_cost, the numbers of terminals and roots, terminals by status, bridges offered and used, and the seconds each step took.

bound Optional[float]

A lower bound on the cost of any plan connecting the same terminals, set by :meth:ConnectionPlanner.lower_bound.

gap Optional[float]

cost / bound - 1: the plan costs at most 1 + gap times the optimum.