Wout VC
Wout VC

Reputation: 377

Wrong distance found when finding distance to nearest edge in OSMNx

Code

Hello I'm trying to find the distance to the nearest edge, in meters, using the OSMNx (OpenStreetMap + NetworkX) package. This is my code:

def main():
lat = 51.217309
lon = 4.418449
get_d_to_nearest_edge(lat, lon, 'node_area.graphml')

Note that 'node_area.graphml' is a offline map containing this specific location. The get_d_to_nearest_edge() function is given below:

def get_d_to_nearest_edge(latitude, longitude, file_name):
print("-Now the nearest edge will be found-")
G = ox.load_graphml(filename=file_name, folder='BAPenvironment')
_, _, _, distance = get_nearest_edge(G, (latitude, longitude))
print('The distance to the nearest edge is ' + to_str(distance) + ' m')

In the code above, the function get_nearest_edge() is accessed. The code of this function is below:

def get_nearest_edge(G, point):
"""
Return the nearest edge to a pair of coordinates. Pass in a graph and a tuple
with the coordinates. We first get all the edges in the graph. Secondly we compute
the euclidean distance from the coordinates to the segments determined by each edge.
The last step is to sort the edge segments in ascending order based on the distance
from the coordinates to the edge. In the end, the first element in the list of edges
will be the closest edge that we will return as a tuple containing the shapely
geometry and the u, v nodes.
Parameters
----------
G : networkx multidigraph
point : tuple
    The (lat, lng) or (y, x) point for which we will find the nearest edge
    in the graph
Returns
-------
closest_edge_to_point : tuple (shapely.geometry, u, v)
    A geometry object representing the segment and the coordinates of the two
    nodes that determine the edge section, u and v, the OSM ids of the nodes.
"""
start_time = time.time()

gdf = ox.graph_to_gdfs(G, nodes=False, fill_edge_geometry=True)
graph_edges = gdf[["geometry", "u", "v"]].values.tolist()

edges_with_distances = [
    (
        graph_edge,
        ox.Point(tuple(reversed(point))).distance(graph_edge[0])
    )
    for graph_edge in graph_edges
]

edges_with_distances = sorted(edges_with_distances, key=lambda x: x[1])
closest_edge_to_point, distance = edges_with_distances[0]

geometry, u, v = closest_edge_to_point

ox.log('Found nearest edge ({}) to point {} in {:,.2f} seconds'.format((u, v), point, time.time() - start_time))
print(distance)
return geometry, u, v, distance

This is almost the same function as the original, build-in in OSMNx function, but has been modified to return the distance too. The last function to_str() is here:

 # convert everything into the same type (np.array) and then convert it to a string 
   def to_str(var):
       return str(list(np.reshape(np.asarray(var), (1, np.size(var)))[0]))[1:-1]

Output

However the output seems to be incorrect. When I verify the distance to the nearest edge in Google Maps I found that this is 18.57m.

> Now the nearest edge will be found Converting node and edge attribute
> data types Loaded graph with 36 nodes and 62 edges in 0.01 seconds
> from "BAPenvironment\node_area.graphml" Created GeoDataFrame
> "unnamed_edges" from graph in 0.00 seconds Found nearest edge
> ((247670339, 247527633)) to point (51.217309, 4.418449) in 0.00 seconds
> 0.00018056587349098678 
> The distance to the nearest edge is 0.00018056587349098678 m

I just added the 'm' because I think this is in meters. Anyone knows how to get it in meters? Or is it in meters, and is my code wrong?

Upvotes: 2

Views: 1209

Answers (2)

gboeing
gboeing

Reputation: 6412

OSMnx's nearest_edges does return the distance. See the docs, which say "Optionally return the distance in graph’s coordinates’ units between the point and the nearest edge." If you don't want to work in degrees as your units, just project your graph:

import osmnx as ox
from shapely import Point

# lat-long point
point = 34.081076, -118.351811
G = ox.graph_from_point(point, dist=1000, network_type='drive')

# project the graph (and point) to a meter projection
Gp = ox.project_graph(G)
point_geom_proj, crs = ox.projection.project_geometry(Point(reversed(point)), to_crs=Gp.graph['crs'])
x, y = point_geom_proj.x, point_geom_proj.y

# find nearest edge as (u, v, key) and distance to it
(u, v, k), dist = ox.nearest_edges(Gp, x, y, return_dist=True)
dist # 40.2 meters

Upvotes: 1

c_m_conlan
c_m_conlan

Reputation: 306

I was able to solve this by changing the Ox.Point function. I wrote a new function to calculate the haversine distance to each coordinate in the LineString object.

from haversine import haversine, Unit

def closest_point_on_haversine(point,edge):
    closest_point = None
    
    for c in edge[0].coords:
        if closest_point == None:
            closest_point = haversine(tuple(reversed(point)), c, unit='m')
        else:
            if haversine(tuple(reversed(point)), c, unit='m') < closest_point:
                closest_point = haversine(tuple(reversed(point)), c, unit='m')
                
    return closest_point

def get_nearest_edge_with_dist(G, point):

    start_time = time.time()

    gdf = graph_to_gdfs(G, nodes=False, fill_edge_geometry=True)
    graph_edges = gdf[["geometry", "u", "v"]].values.tolist()

    edges_with_distances = [
        (
            graph_edge,
            # Point(tuple(reversed(point))).distance(graph_edge[0])
            closest_point_on_haversine(point,graph_edge)
        )
        for graph_edge in graph_edges
    ]

    edges_with_distances = sorted(edges_with_distances, key=lambda x: x[1])
    closest_edge_to_point, distance = edges_with_distances[0]

    geometry, u, v = closest_edge_to_point

    return geometry, u, v, distance

Upvotes: 0

Related Questions