Open In App

Find the Nearest Node to a Point Using OSMnx Distance Module

Last Updated : 21 Mar, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Nearest neighbor search algorithms are crucial for robust navigation. OSMnx makes use of different types of nearest neighbor search algorithms for the projected and unprotected graph. In this article, we will see how we can find the nearest node to a point using OSMnx distance module in Python.

Syntax of osmnx.distance.nearest_nodes() Function

osmnx.distance.nearest_nodes(G, X, Y, return_dist=False)

Parameters:

  • G (networkx.MultiDiGraph) – graph in which to find nearest nodes
  • X (float or list) – points’ x (longitude) coordinates, in same CRS/units as graph and containing no nulls
  • Y (float or list) – points’ y (latitude) coordinates, in same CRS/units as graph and containing no nulls
  • return_dist (bool) – optionally also return distance between points and nearest nodes

Returns: nn or (nn, dist) – nearest node IDs or optionally a tuple where dist contains distances between the points and their nearest nodes

Return Type: int/list or tuple

Note: install geopandas==0.14.3, osmnx==1.9.1, notebook==7.0.7, folium==0.15.1, matplotlib==3.8.2, mapclassify==2.6.1

Nearest Node to a Point for an Unprojected Graph Using OSMnx Module

If graph is unprojected, OSMnx distance module uses Ball Tree algorithm for nearest neighbor search. In Ball Tree algorithm, the total space of training data is divided into multiple circular blocks. Each circular block has a centroid point and has set of node points (location points).

For nearest node search, ball tree algorithm calculates the distance of a node with the centroid point rather than calculating the distance with all nodes in the data set. Then the training data set (to identify the nearest node) is taken from the circular block, which has the nearest centroid point.

Note: requires scikit-learn as an optional dependency

Let’s find the nearest node to a point for an unprojected graph using osmnx distance module. As a first step, we need multidigraph of a place. Below code fetch multidigraph of Punalur, Kerala.

Python3
import osmnx as ox

# fecth multi digraph from place
multi_digraph = ox.graph_from_place('Punalur, Kerala')

Review the Nodes

Let’s review the nodes by plotting it on a map. The code as follows:

Python3
# convert multidigraph nodes to geodataframe
gdf_nodes = ox.utils_graph.graph_to_gdfs(
    multi_digraph, nodes=True, edges=False, node_geometry=True,
    fill_edge_geometry=False)

# display it on map
gdf_nodes.explore()

Output

punalur_node_map

Punalur nodes

Find Nearest Node

Now we can use the osmnx distance module to find the nearest node. Let’s take Chadayamangalam as a point and we need to identify the nearest node from our multidigraph. The code as follows:

Python3
# chadayamangalam coordinates
chadaya_lat, chadaya_lon = 8.873103284974913, 76.86933422158793
nearest_node = ox.distance.nearest_nodes(multi_digraph,
                                         chadaya_lon, chadaya_lat,
                                         return_dist=True)

Output

(8245709348, 1367.182397021979)

Fetch Geometry of Node ID

It returns the nearest node id and the distance in meters. Let’s fetch the geometry of the node id from the above geodataframe.

Python3
# fetch the node geometry based on node id
nearest_node_id = nearest_node[0]
gdf_nodes.loc[nearest_node_id]

Output

y                                   8.881764
x 76.860501
highway NaN
street_count 3
geometry POINT (76.8605012 8.8817642)
Name: 8245709348, dtype: object

Let’s review by plotting it on a map.

Python3
import geopandas as gpd
from shapely import (
    Point, LineString)

# create a new geodataframe with the nearest node and new point
nearest_node_dict = {'col1': ['Chadayamangalam', 'Nearest Node'],
                     'geometry': [Point(chadaya_lon, chadaya_lat),
                                  LineString([
                                      Point(gdf_nodes.loc[nearest_nodeid].x,
                                            gdf_nodes.loc[nearest_nodeid].y),
                                      Point(chadaya_lon, chadaya_lat)])]}
# convert dictionary to geodataframe
nearest_node_gdf = gpd.GeoDataFrame(nearest_node_dict, crs="EPSG:4326")
# nearest node map reference
nearest_node_map = nearest_node_gdf.explore(color="red")
# combine nearest node with existing node map
gdf_nodes.explore(m=nearest_node_map)

Output

nearest_node_chadaya

Nearest Node

You can notice a line (in red color) from chadayamangalam to the nearest node in our punalur geodataframe.

Nearest Node to a Point for an Projected Graph Using OSMnx Module

If graph is projected, OSMnx distance module uses KD tree algorithm for nearest neighbor search. In K-D tree algorithm, the points can be organized in a K-Dimensional space. A non-leaf node in K-D tree divide the space into two parts. Points to the left of the space are represented by the left subtree of the node and points to the right of the space are represented by the right subtree.

The root node has an x-aligned plane (right aligned). For a new node, if the x value of root node is less than the x value of new node, then the new node will be left aligned (y aligned). If the node is x aligned then the x value is considered for comparison or if the node is y aligned then the y value is considered. If the points are equal, then the node is considered as right aligned.

Note: requires scipy as an optional dependency

Let’s take the same multidigraph (Punalur) from the above code. The point to note, here we need projected graph. But our existing multidigraph is an unprotected graph. Let’s check it.

Python3
import geopandas as gpd
import osmnx as ox

# fecth multi digraph from place
multi_digraph = ox.graph_from_place('Punalur, Kerala')

# fetch the crs from multidigraph
crs = multi_digraph.graph["crs"]

# check whether the crs is projected
gpd.GeoSeries(crs=crs).crs.is_projected

Output

False

Since our graph is unprojected, we need to convert it to a projected graph. As a first step, we need to convert multidigraph to geodataframe and then change it to projected graph using geopandas. Finally convert the projected geodataframe back to multidigraph. The code as follows:

Python3
import osmnx as ox
import geopandas as gpd

# convert multidigraph nodes to geodataframe
gdf_nodes = ox.utils_graph.graph_to_gdfs(
    multi_digraph, nodes=True, edges=False,
    node_geometry=True, fill_edge_geometry=False)

# convert multidigraph edges to geodataframe
gdf_edges = ox.utils_graph.graph_to_gdfs(
    multi_digraph, nodes=False, edges=True,
    node_geometry=False, fill_edge_geometry=True)

# convert geodataframe nodes to projected gdf noded
gdf_nodes['geometry'] = gdf_nodes.geometry.to_crs("EPSG:7781")
# let's change the x and y columns with projected geometry coordinates
gdf_nodes['x'] = gdf_nodes['geometry'].x
gdf_nodes['y'] = gdf_nodes['geometry'].y

# convert geodataframe edges to projected gdf edges
gdf_edges['geometry'] = gdf_edges.geometry.to_crs("EPSG:7781")

# set crs attribute
graph_attrs = {"crs": "EPSG:7781"}

# convert projected geodataframe to multidigraph
proj_multidigraph = ox.utils_graph.graph_from_gdfs(
    gdf_nodes, gdf_edges, graph_attrs=graph_attrs)

# check for projection
crs = proj_multidigraph.graph["crs"]
gpd.GeoSeries(crs=crs).crs.is_projected

Output

True

Note: EPSG:7781 is a projected coordinate system for Kerala

Now we have a projected MultiDigraph of Punalur. Let’s find the nearest node using OSMnx distance module. Since we have a projected multidigraph, It is important to pass the X and Y coordinate in the same CRS. We can take Kokad coordinates and check for the nearest node in our multidigraph from Kokad.

The code to get projected coordinates of Kokad as follows:

Python3
import geopandas as gpd
from shapely import Point

# set geodataframe
kokad_lat, kokad_lon = 8.984569551375083, 76.87386194013466
kokad_node_dict = {'col1': ['Kokad'],
                   'geometry': [Point(kokad_lon, kokad_lat)]}
kokad_node_gdf = gpd.GeoDataFrame(kokad_node_dict, crs="EPSG:4326")

# convert it to projected coordinates
kokad_node_gdf['geometry'] = kokad_node_gdf.geometry.to_crs("EPSG:7781")

# get projected coordinates
kokad_proj_lon = kokad_node_gdf['geometry'].x
kokad_proj_lat = kokad_node_gdf['geometry'].y

Now let’s make use of OSMnx distance module to find the nearest node from Kokad. The code as show below:

Python3
# find the nearest node from kokad
nearest_node = ox.distance.nearest_nodes(
    proj_multidigraph, kokad_proj_lon, kokad_proj_lat,
    return_dist=True)

Output

(8249763424, 1082.6126641017327)

The nearest node id from Kokad is 8249763424 and the distance is 1082.6126641017327 meters.

Python3
nearest_nodeid = nearest_node[0]
# fetch the data based om node id
gdf_nodes.loc[nearest_nodeid]

Output

y                                             831489.012826
x 1096441.200688
highway NaN
street_count 3
geometry POINT (1096441.200687849 831489.0128263424)
Name: 8249763424, dtype: object

Let’s plot it in map for better understanding. The code as follows:

Python3
# get the nearest node id
nearest_nodeid = nearest_node[0]
# create a dictionary with Kokad point and Nearest node coordinates
nearest_node_dict = {'col1': ['Kokad', 'Nearest Node'],
                     'geometry': [Point(kokad_proj_lon, kokad_proj_lat),
                                  LineString([
                                      Point(gdf_nodes.loc[nearest_nodeid].x,
                                            gdf_nodes.loc[nearest_nodeid].y),
                                      Point(kokad_proj_lon, kokad_proj_lat)])]}
# convert to geodataframe
nearest_node_gdf = gpd.GeoDataFrame(nearest_node_dict, crs="EPSG:7781")

# get nearest node map and display it with our dataset
nearest_node_map = nearest_node_gdf.explore(color="red")
gdf_nodes.explore(m=nearest_node_map)

Output

You can notice a line (in red color) from Kokad to the nearest node in our punalur geodataframe.

kokad_nearest_node

Nearest node projected graph



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads