Map from OpenStreetMap¶
You can download a graph for map-matching from the OpenStreetMap.org service. Multiple methods exists, we illustrate two.
Using requests, osmread and gpx¶
You can perform map matching on a OpenStreetMap database by combing leuvenmapmatching
with the packages requests
, osmread
and gpx
.
Download a map as XML¶
You can use the overpass-api.de service:
from pathlib import Path
import requests
xml_file = Path(".") / "osm.xml"
url = 'http://overpass-api.de/api/map?bbox=4.694933,50.870047,4.709256000000001,50.879628'
r = requests.get(url, stream=True)
with xml_file.open('wb') as ofile:
for chunk in r.iter_content(chunk_size=1024):
if chunk:
ofile.write(chunk)
Create graph using osmread¶
Once we have a file containing the region we are interested in, we can select the roads we want to use to create a graph from. In this case we focus on ‘ways’ with a ‘highway’ tag. Those represent a variety of roads. For a more detailed filtering look at the possible values of the highway tag.
from leuvenmapmatching.map.inmem import InMemMap
import osmread
map_con = InMemMap("myosm", use_latlon=True, use_rtree=True, index_edges=True)
for entity in osmread.parse_file(str(xml_file)):
if isinstance(entity, osmread.Way) and 'highway' in entity.tags:
for node_a, node_b in zip(entity.nodes, entity.nodes[1:]):
map_con.add_edge(node_a, node_b)
# Some roads are one-way. We'll add both directions.
map_con.add_edge(node_b, node_a)
if isinstance(entity, osmread.Node):
map_con.add_node(entity.id, (entity.lat, entity.lon))
map_con.purge()
Note that InMemMap
is a simple container for a map. It is recommended to use
your own optimized connecter to your map dataset.
If you want to allow transitions that are not following the exact road segments you can inherit from the Map
class and define a new class with your own transitions.
The transitions are defined using the nodes_nbrto
and edges_nbrt
methods.
Perform map matching on an OpenStreetMap database¶
You can create a list of latitude-longitude coordinates manually. Or read a gpx file.
from leuvenmapmatching.util.gpx import gpx_to_path
track = gpx_to_path("mytrack.gpx")
matcher = DistanceMatcher(map_con,
max_dist=100, max_dist_init=25, # meter
min_prob_norm=0.001,
non_emitting_length_factor=0.75,
obs_noise=50, obs_noise_ne=75, # meter
dist_noise=50, # meter
non_emitting_states=True,
max_lattice_width=5)
states, lastidx = matcher.match(track)
Using osmnx and geopandas¶
Another great library to interact with OpenStreetMap data is the osmnx package. The osmnx package can retrieve relevant data automatically, for example when given a name of a region. This package is build on top of the geopandas package.
import osmnx
graph = ox.graph_from_place('Leuven, Belgium', network_type='drive', simplify=False)
graph_proj = ox.project_graph(graph)
# Create GeoDataFrames (gdfs)
# Approach 1
nodes_proj, edges_proj = ox.graph_to_gdfs(graph_proj, nodes=True, edges=True)
for nid, row in nodes_proj[['x', 'y']].iterrows():
map_con.add_node(nid, (row['x'], row['y']))
for eid, _ in edges_proj.iterrows():
map_con.add_edge(eid[0], eid[1])
# Approach 2
nodes, edges = ox.graph_to_gdfs(graph_proj, nodes=True, edges=True)
nodes_proj = nodes.to_crs("EPSG:3395")
edges_proj = edges.to_crs("EPSG:3395")
for nid, row in nodes_proj.iterrows():
map_con.add_node(nid, (row['lat'], row['lon']))
# We can also extract edges also directly from networkx graph
for nid1, nid2, _ in graph.edges:
map_con.add_edge(nid1, nid2)