-1

I am writing some code to obtain the road centerline data from OSM based on GPS data for a given path that was driven. I am using OSMNX to interface with the OSM data but get strange results when using nearest_edges to match the GPS data from my path to the OSM data.

Here is the input path (with data spacing < 1m apart): enter image description here

And here is the path after matching using nearest_edges: enter image description here

Here is the code I am using to generate these results:

#!/usr/bin/python3
    
import osmnx as ox
import csv
import utm
from shapely.geometry import Point
import os
import simplekml

# get data from file
path = "C:\\Users\\nj5wdf\\OneDrive - Aptiv\\Projects\\Prodrive\\Curvature_Python\\tests\\"
filename = "map_provider_test_data.csv"
filename = os.path.join(path, filename)
with open(filename, 'r') as file:
    # create csv reader object
    data = list(csv.reader(file))            
    lat = []
    lon = []
    for row in data:
        lat.append(float(row[0]))
        lon.append(float(row[1]))

buffer = 0.002
north_lat = max(lat) + buffer
south_lat = min(lat) - buffer
east_lon = max(lon) + buffer
west_lon = min(lon) - buffer

# generate graph of all road networks within the bounding box
graph = ox.graph_from_bbox(
    north_lat, south_lat, east_lon, west_lon,
    network_type="drive",
    simplify=True,
    truncate_by_edge=True,
    clean_periphery=True)

# convert input lat / lon to UTM
x = []
y = []
for _lat, _lon in zip(lat, lon):
    _y, _x, _, _ = utm.from_latlon(_lat, _lon)
    x.append(_x)
    y.append(_y)

# get nearest edges
nodes, streets = ox.graph_to_gdfs(graph)
graph_proj = ox.project_graph(graph)
nearest = ox.nearest_edges(graph_proj, lon, lat) 
    
# remove redundant edges from result
last_near = nearest[0]
reduced_nearest = []
for near in nearest:
    if (near not in reduced_nearest) and (near[1] != last_near[0]):
        reduced_nearest.append(near)
        last_near = near

# get centerline points from nearest edges 
centerline_points = []
for edge in reduced_nearest:
    street_gdf = streets.loc[edge]['geometry']
    lat_list = list(street_gdf.xy[1])
    lon_list = list(street_gdf.xy[0])
    [centerline_points.append(Point(_lat, _lon)) for _lat, _lon in zip(lat_list, lon_list)]

# instantiate simpleKml
kml = simplekml.Kml()

coords = zip(lon_list, lat_list)

# plot line
ls = kml.newlinestring(name="matched")
ls.coords = coords
ls.style.linestyle.width = 2
ls.style.linestyle.color = simplekml.Color.red
ls.altitudemode = simplekml.AltitudeMode.relativetoground

# save points to file
output_filename = "tests/map_provider_matched.kml"
kml.save(output_filename)

Here is the test data I am using: test_data

Any ideas for how i can get better performance? I've tried using a different nearest neighbor function, but that performed even worse that ox.nearest_edges.

ScottP
  • 1
  • 3
  • Can you provide a *complete* but *minimal* example code snippet to reproduce your problem? Currently your code is not complete (it has undefined variables in it which are essential to troubleshooting) nor minimal (it contains several other lines irrelevant to your question about the nearest edge search). – gboeing Nov 04 '21 at 20:31
  • I edited the code above to make it more complete. I think it is as minimal as possible. I also attached the test data I am using. Thanks for your help! – ScottP Nov 05 '21 at 13:33
  • Many of your variables remain undefined. Your example needs to be standalone, copy-and-paste runnable for others to reproduce and troubleshoot it. – gboeing Nov 06 '21 at 05:46
  • Thank you for your patience. I updated the code and I am able to run it. – ScottP Nov 08 '21 at 22:57
  • It looks like you're just swapping the x and y coordinates. Longitude is x and latitude is y, but you seem to have that reversed. – gboeing Nov 09 '21 at 04:31
  • I swapped the coordinates, and get a different result completely that does not follow the correct road anymore. Originally, I converted the lat/lon to cartesian x, y since I projected the graph to cartesian. But I get the same result whether I use cartesian x,y or lat,lon. [link] (https://www.dropbox.com/s/s1i9o3c2idcggal/path_matching.jpg?dl=0). I also updated the code above to include the kml file creation. – ScottP Nov 09 '21 at 21:52
  • I should have pointed out that the green path is the input path and the red path is the data output from nearest_edges. – ScottP Nov 09 '21 at 22:16

1 Answers1

0

There's a lot going on here. It's hard to troubleshoot because you didn't distill it down to a minimal example and your initial problem statement isn't very specific:

I am using OSMNX to interface with the OSM data but get strange results when using nearest_edges to match the GPS data from my path to the OSM data.

I don't know what "strange results" means, but I've distilled (what I believe you're trying to do) down to a minimal example here that appears to work just fine.

import geopandas as gpd
import osmnx as ox
import pandas as pd

# load the points
cols = ['lat', 'lng', 'a', 'b', 'c', 'd', 'e']
df = pd.read_csv('map_provider_test_data.csv', names=cols)

# download the graph
center = df['lat'].mean(), df['lng'].mean()
G = ox.graph_from_point(center, dist=1500, network_type='drive', truncate_by_edge=True)

# project graph and points to same CRS
Gp = ox.project_graph(G)
geom = gpd.points_from_xy(df['lng'], df['lat'])
gdf = gpd.GeoDataFrame(df, geometry=geom, crs='epsg:4326').to_crs(Gp.graph['crs'])

# calculate nearest edge to each point
ne = ox.nearest_edges(Gp, X=gdf['geometry'].x, Y=gdf['geometry'].y)

You can plot this if you like, to see that the points are snapped to the correct nearest edges:

unique_edges = set(ne)
ec = ['r' if e in unique_edges else '#666666' for e in G.edges]
fig, ax = ox.plot_graph(G, edge_color=ec, edge_linewidth=1, node_size=0, show=False)
ax = ax.scatter(df['lng'], df['lat'], c='y', s=5, zorder=-1)

You can also open it up in a GIS and pan and scroll around: the nearest edge search all looks like it works fine.

gboeing
  • 5,691
  • 2
  • 15
  • 41