### Install from source Source: https://github.com/wannesm/leuvenmapmatching/blob/master/docs/usage/installation.md Command to compile and install the library directly from the source code. ```bash python3 setup.py install ``` -------------------------------- ### Install LeuvenMapMatching via pip Source: https://github.com/wannesm/leuvenmapmatching/blob/master/docs/usage/installation.md Commands to install the latest released or development versions of the library using pip. ```default $ pip install leuvenmapmatching ``` ```default $ pip install git+https://github.com/wannesm/leuvenmapmatching ``` -------------------------------- ### Custom Probability Distributions for Map Matching Source: https://context7.com/wannesm/leuvenmapmatching/llms.txt Extends the `BaseMatcher` class to implement custom transition and emission probability distributions. This example shows uniform transition probabilities and a step-function emission probability. ```python import math import numpy as np from leuvenmapmatching.matcher.base import BaseMatcher, BaseMatching class CustomMatcher(BaseMatcher): """Custom matcher with uniform transition and step-function emission.""" def __init__(self, *args, emission_threshold=10, **kwargs): super().__init__(*args, **kwargs) self.emission_threshold = emission_threshold def logprob_trans(self, prev_m, edge_m, edge_o, is_prev_ne=False, is_next_ne=False): """Uniform transition probability over all neighbors.""" num_neighbors = len(self.map.nodes_nbrto(prev_m.edge_m.l2)) if num_neighbors > 0: logprob = -math.log(num_neighbors) else: logprob = 0 return logprob, {} def logprob_obs(self, dist, prev_m=None, new_edge_m=None, new_edge_o=None, is_ne=False): """Step function emission: uniform within threshold, zero outside.""" if is_ne: threshold = self.emission_threshold * 5 # More tolerance for NE else: threshold = self.emission_threshold if dist < threshold: result = -math.log(threshold) # Uniform probability else: result = -np.inf # Zero probability outside threshold return result, {'emission_dist': dist} ``` -------------------------------- ### Implement Uniform Transition Probability Source: https://github.com/wannesm/leuvenmapmatching/blob/master/docs/usage/customdistributions.md Overwrite the `logprob_trans()` method to define a uniform distribution over possible road segments. This example calculates the negative logarithm of the number of neighboring nodes. ```python def logprob_trans(self, prev_m, edge_m, edge_o, is_prev_ne, is_next_ne): return -math.log(len(self.matcher.map.nodes_nbrto(self.edge_m.last_point()))) ``` -------------------------------- ### Project Route GPS Coordinates with pyproj (UTM) Source: https://github.com/wannesm/leuvenmapmatching/blob/master/docs/usage/latitudelongitude.md This example shows how to project route GPS coordinates using `pyproj` with UTM projection. It defines both the source (latlon) and target (utm) projections. Be mindful of `pyproj`'s x-y (longitude-latitude) convention versus leuvenmapmatching's latitude-longitude. ```python import pyproj route = [(4.67878,50.864),(4.68054,50.86381),(4.68098,50.86332),(4.68129,50.86303),(4.6817,50.86284), (4.68277,50.86371),(4.68894,50.86895),(4.69344,50.86987),(4.69354,50.86992),(4.69427,50.87157), (4.69643,50.87315),(4.69768,50.87552),(4.6997,50.87828)] p1 = pyproj.Proj(proj='latlon', datum='WGS84') p2 = pyproj.Proj(proj='utm', datum='WGS84') xs, ys = [], [] for lon, lat in route: x, y = pyproj.transform(lon, lat) xs.append(x) ys.append(y) ``` -------------------------------- ### Implement Custom Emission Probability Source: https://github.com/wannesm/leuvenmapmatching/blob/master/docs/usage/customdistributions.md Overwrite the `logprob_obs()` method to define custom emission probabilities, especially for non-emitting nodes. This example uses a step function with different distance tolerances for emitting and non-emitting nodes. ```python def logprob_obs(self, dist, prev_m, new_edge_m, new_edge_o, is_ne): if is_ne: if dist < 50: return -math.log(50) else: if dist < 10: return -math.log(10) return -np.inf ``` -------------------------------- ### Create Map Graph from osmnx GeoDataFrames (Approach 1) Source: https://github.com/wannesm/leuvenmapmatching/blob/master/docs/usage/openstreetmap.md Convert the projected osmnx graph into GeoDataFrames for nodes and edges. This approach iterates through nodes and edges to populate the map container, adding nodes by ID and coordinates, and edges by their start and end node IDs. ```python 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]) ``` -------------------------------- ### Use Custom Matcher with InMemMap Source: https://context7.com/wannesm/leuvenmapmatching/llms.txt Demonstrates how to initialize an InMemMap with a custom graph and use it with a CustomMatcher. Ensure the map data and matcher parameters are suitable for your specific use case. ```python from leuvenmapmatching.map.inmem import InMemMap map_con = InMemMap("mymap", graph={ "A": ((1, 1), ["B"]), "B": ((1, 3), ["A", "C"]), "C": ((2, 2), ["A", "B"]), }, use_latlon=False) matcher = CustomMatcher( map_con, emission_threshold=1.5, max_dist=3, non_emitting_states=True ) path = [(0.9, 1.0), (1.1, 2.0), (1.9, 2.1)] states, _ = matcher.match(path) print("Matched:", matcher.path_pred_onlynodes) ``` -------------------------------- ### Perform Map Matching with GPX Data Source: https://github.com/wannesm/leuvenmapmatching/blob/master/docs/usage/openstreetmap.md Read a GPX file to obtain a track of latitude-longitude coordinates and then perform map matching using the DistanceMatcher. Adjust parameters like max_dist, obs_noise, and dist_noise for optimal results. ```python 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) ``` -------------------------------- ### Plot Map with Default Settings Source: https://github.com/wannesm/leuvenmapmatching/blob/master/docs/usage/visualisation.md Use this function to plot the map, matching, and graph with default settings. Specify 'filename' to save the plot. ```python from leuvenmapmatching import visualization as mmviz mmviz.plot_map(map_con, matcher=matcher, show_labels=True, show_matching=True, show_graph=True, filename="my_plot.png") ``` -------------------------------- ### Download OSM Map Data as XML Source: https://github.com/wannesm/leuvenmapmatching/blob/master/docs/usage/openstreetmap.md Use the overpass-api.de service to download map data for a specified bounding box as an XML file. This is the first step in preparing data for map matching. ```python 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) ``` -------------------------------- ### Load OpenStreetMap Data for Production Map Matching Source: https://context7.com/wannesm/leuvenmapmatching/llms.txt Integrates real road networks from OpenStreetMap using the overpass API and osmread library. Ensure 'osm.xml' is downloaded before parsing. ```python from pathlib import Path import requests from leuvenmapmatching.map.inmem import InMemMap from leuvenmapmatching.matcher.distance import DistanceMatcher import osmread # Download OSM data for a region (Leuven, Belgium) xml_file = Path(".") / "osm.xml" url = 'http://overpass-api.de/api/map?bbox=4.694933,50.870047,4.709256,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 indexed map from OSM data map_con = InMemMap("leuven", 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) map_con.add_edge(node_b, node_a) # Bidirectional if isinstance(entity, osmread.Node): map_con.add_node(entity.id, (entity.lat, entity.lon)) map_con.purge() # Remove nodes without location or edges # Match GPS track with real-world parameters (in meters) matcher = DistanceMatcher( map_con, max_dist=100, # 100 meters max distance max_dist_init=25, # 25 meters for initial match min_prob_norm=0.001, non_emitting_length_factor=0.75, obs_noise=50, # 50 meters GPS noise obs_noise_ne=75, # 75 meters for non-emitting dist_noise=50, # 50 meters distance noise non_emitting_states=True, max_lattice_width=5 ) # Example GPS track (lat, lon) track = [ (50.8754, 4.7071), (50.8760, 4.7085), (50.8768, 4.7102), (50.8775, 4.7118) ] states, last_idx = matcher.match(track) matched_nodes = matcher.path_pred_onlynodes ``` -------------------------------- ### Plot Map with OpenStreetMap Background Source: https://github.com/wannesm/leuvenmapmatching/blob/master/docs/usage/visualisation.md Visualize map matching results with an OpenStreetMap background using the 'smopy' package. Set 'use_osm=True' and 'zoom_path=True' to focus on the relevant path. ```python mm_viz.plot_map(map_con, matcher=matcher, use_osm=True, zoom_path=True, show_labels=False, show_matching=True, show_graph=False, filename="my_osm_plot.png") ``` -------------------------------- ### Incremental Map Matching with Streaming Data Source: https://context7.com/wannesm/leuvenmapmatching/llms.txt Demonstrates incremental map matching by processing GPS observations in batches, building upon the previous lattice structure. Use `expand=True` to continue matching from the previous state. ```python from leuvenmapmatching.matcher.distance import DistanceMatcher from leuvenmapmatching.map.inmem import InMemMap map_con = InMemMap("mymap", graph={ "A": ((1, 1), ["B", "C", "X"]), "B": ((1, 3), ["A", "C", "D", "K"]), "C": ((2, 2), ["A", "B", "D", "E", "X", "Y"]), "D": ((2, 4), ["B", "C", "D", "E", "K", "L"]), "E": ((3, 3), ["C", "D", "F", "Y"]), "F": ((3, 5), ["D", "E", "L"]), "X": ((2, 0), ["A", "C", "Y"]), "Y": ((3, 1), ["X", "C", "E"]), "K": ((1, 5), ["B", "D", "L"]), "L": ((2, 6), ["K", "D", "F"]) }, use_latlon=False) # Full path (simulating streaming data) full_path = [ (0.8, 0.7), (0.9, 0.7), (1.1, 1.0), (1.2, 1.5), (1.2, 1.6), (1.1, 2.0), (1.1, 2.3), (1.3, 2.9), (1.2, 3.1), (1.5, 3.2), (1.8, 3.5), (2.0, 3.7) ] matcher = DistanceMatcher(map_con, max_dist=2, obs_noise=1, min_prob_norm=0.5) # First batch of observations states, _ = matcher.match(full_path[:5]) print("After 5 points:", matcher.path_pred_onlynodes) # Continue with more observations (expand=True continues from lattice) states, _ = matcher.match(full_path[:8], expand=True) print("After 8 points:", matcher.path_pred_onlynodes) # Final batch states, _ = matcher.match(full_path, expand=True) print("Final result:", matcher.path_pred_onlynodes) # If matching stops early, expand search space if matcher.early_stop_idx is not None: print(f"Matching stopped early at index {matcher.early_stop_idx}") states, _ = matcher.increase_max_lattice_width(10) # Expand to 10 ``` -------------------------------- ### Project Route GPS Coordinates with pyproj (Mercator) Source: https://github.com/wannesm/leuvenmapmatching/blob/master/docs/usage/latitudelongitude.md Utilize the `pyproj` library to project route GPS coordinates using the Mercator projection. Note that `pyproj` uses x-y (longitude-latitude) convention by default, while leuvenmapmatching uses latitude-longitude. Adjustments may be needed for axis order. ```python import pyproj route = [(4.67878,50.864),(4.68054,50.86381),(4.68098,50.86332),(4.68129,50.86303),(4.6817,50.86284), (4.68277,50.86371),(4.68894,50.86895),(4.69344,50.86987),(4.69354,50.86992),(4.69427,50.87157), (4.69643,50.87315),(4.69768,50.87552),(4.6997,50.87828)] lon_0, lat_0 = route[0] proj = pyproj.Proj(f"+proj=merc +ellps=GRS80 +units=m +lon_0={lon_0} +lat_0={lat_0} +lat_ts={lat_0} +no_defs") xs, ys = [], [] for lon, lat in route: x, y = proj(lon, lat) xs.append(x) ys.append(y) ``` -------------------------------- ### Inspect Matching Lattice Source: https://github.com/wannesm/leuvenmapmatching/blob/master/docs/usage/debug.md All paths through the lattice are available in `matcher.lattice`, which is a dictionary mapping observation indices to `LatticeColumn` objects. You can retrieve all matching objects for an observation using `values_all()`. ```python >>> matcher.lattice {0: , 1: , 2: , ... >>> matcher.lattice[0].values_all() {Matching, Matching, Matching, ... ``` -------------------------------- ### Perform incremental map matching with DistanceMatcher Source: https://github.com/wannesm/leuvenmapmatching/blob/master/docs/usage/incremental.md Initializes an InMemMap and uses DistanceMatcher to match path observations. The expand=True parameter allows the matcher to continue building the lattice from previous states. ```python from leuvenmapmatching.matcher.distance import DistanceMatcher from leuvenmapmatching.map.inmemmap import InMemMap map_con = InMemMap("mymap", graph={ "A": ((1, 1), ["B", "C", "X"]), "B": ((1, 3), ["A", "C", "D", "K"]), "C": ((2, 2), ["A", "B", "D", "E", "X", "Y"]), "D": ((2, 4), ["B", "C", "D", "E", "K", "L"]), "E": ((3, 3), ["C", "D", "F", "Y"]), "F": ((3, 5), ["D", "E", "L"]), "X": ((2, 0), ["A", "C", "Y"]), "Y": ((3, 1), ["X", "C", "E"]), "K": ((1, 5), ["B", "D", "L"]), "L": ((2, 6), ["K", "D", "F"]) }, use_latlon=False) path = [(0.8, 0.7), (0.9, 0.7), (1.1, 1.0), (1.2, 1.5), (1.2, 1.6), (1.1, 2.0), (1.1, 2.3), (1.3, 2.9), (1.2, 3.1), (1.5, 3.2), (1.8, 3.5), (2.0, 3.7), (2.3, 3.5), (2.4, 3.2), (2.6, 3.1), (2.9, 3.1), (3.0, 3.2), (3.1, 3.8), (3.0, 4.0), (3.1, 4.3), (3.1, 4.6), (3.0, 4.9)] matcher = DistanceMatcher(map_con, max_dist=2, obs_noise=1, min_prob_norm=0.5) states, _ = matcher.match(path[:5]) states, _ = matcher.match(path, expand=True) nodes = matcher.path_pred_onlynodes print("States\n------") print(states) print("Nodes\n------") print(nodes) print("") matcher.print_lattice_stats() ``` -------------------------------- ### Perform Simple Map Matching Source: https://github.com/wannesm/leuvenmapmatching/blob/master/docs/usage/introduction.md Configures a DistanceMatcher with a custom InMemMap and path data to compute the most likely sequence of nodes. ```python from leuvenmapmatching.matcher.distance import DistanceMatcher from leuvenmapmatching.map.inmem import InMemMap map_con = InMemMap("mymap", graph={ "A": ((1, 1), ["B", "C", "X"]), "B": ((1, 3), ["A", "C", "D", "K"]), "C": ((2, 2), ["A", "B", "D", "E", "X", "Y"]), "D": ((2, 4), ["B", "C", "F", "E", "K", "L"]), "E": ((3, 3), ["C", "D", "F", "Y"]), "F": ((3, 5), ["D", "E", "L"]), "X": ((2, 0), ["A", "C", "Y"]), "Y": ((3, 1), ["X", "C", "E"]), "K": ((1, 5), ["B", "D", "L"]), "L": ((2, 6), ["K", "D", "F"]) }, use_latlon=False) path = [(0.8, 0.7), (0.9, 0.7), (1.1, 1.0), (1.2, 1.5), (1.2, 1.6), (1.1, 2.0), (1.1, 2.3), (1.3, 2.9), (1.2, 3.1), (1.5, 3.2), (1.8, 3.5), (2.0, 3.7), (2.3, 3.5), (2.4, 3.2), (2.6, 3.1), (2.9, 3.1), (3.0, 3.2), (3.1, 3.8), (3.0, 4.0), (3.1, 4.3), (3.1, 4.6), (3.0, 4.9)] matcher = DistanceMatcher(map_con, max_dist=2, obs_noise=1, min_prob_norm=0.5, max_lattice_width=5) states, _ = matcher.match(path) nodes = matcher.path_pred_onlynodes print("States\n------") print(states) print("Nodes\n------") print(nodes) print("") matcher.print_lattice_stats() ``` -------------------------------- ### Create In-Memory Map Graph with InMemMap Source: https://context7.com/wannesm/leuvenmapmatching/llms.txt Instantiate an InMemMap object to represent a road network. Supports graph definition, lat/lon or XY coordinates, and optional rtree indexing for performance. Can also load from pickle files. ```python from leuvenmapmatching.map.inmem import InMemMap # Create a simple map with nodes and edges # Each node: label -> ((y, x), [list of neighbor labels]) map_con = InMemMap("mymap", graph={ "A": ((1, 1), ["B", "C", "X"]), "B": ((1, 3), ["A", "C", "D", "K"]), "C": ((2, 2), ["A", "B", "D", "E", "X", "Y"]), "D": ((2, 4), ["B", "C", "F", "E", "K", "L"]), "E": ((3, 3), ["C", "D", "F", "Y"]), "F": ((3, 5), ["D", "E", "L"]), "X": ((2, 0), ["A", "C", "Y"]), "Y": ((3, 1), ["X", "C", "E"]), "K": ((1, 5), ["B", "D", "L"]), "L": ((2, 6), ["K", "D", "F"]) }, use_latlon=False) # Create map with rtree indexing for large datasets (latitude-longitude) map_con_indexed = InMemMap( "indexed_map", use_latlon=True, # Use lat/lon coordinates use_rtree=True, # Enable rtree spatial indexing index_edges=True # Index edges instead of nodes ) # Add nodes and edges programmatically map_con_indexed.add_node(1, (50.8754, 4.7071)) # (lat, lon) map_con_indexed.add_node(2, (50.8760, 4.7085)) map_con_indexed.add_edge(1, 2) map_con_indexed.add_edge(2, 1) # Bidirectional # Get bounding box of the map lat_min, lon_min, lat_max, lon_max = map_con.bb() # Serialize map to file for later use map_con.dir = "/path/to/save" map_con.dump() # Load from pickle loaded_map = InMemMap.from_pickle("/path/to/save/mymap.pkl") ``` -------------------------------- ### Process GPX Files for Map Matching Source: https://context7.com/wannesm/leuvenmapmatching/llms.txt Reads GPS tracks from GPX files using 'gpx_to_path' and converts matched paths back to GPX format with 'path_to_gpx'. Ensure 'mytrack.gpx' exists. ```python from leuvenmapmatching.util.gpx import gpx_to_path, path_to_gpx from leuvenmapmatching.matcher.distance import DistanceMatcher from leuvenmapmatching.map.inmem import InMemMap # Load GPX track file track = gpx_to_path("mytrack.gpx") # Returns: [(lat, lon, time), (lat, lon, time), ...] # Assuming map_con is already created matcher = DistanceMatcher( map_con, max_dist=100, obs_noise=50, non_emitting_states=True, max_lattice_width=5 ) # Match directly from GPX file states, last_idx = matcher.match_gpx("mytrack.gpx") # Or match using the loaded track (without timestamps) path_coords = [(lat, lon) for lat, lon, time in track] states, last_idx = matcher.match(path_coords) # Export matched path to GPX matched_path = [(lat, lon, None) for lat, lon in matcher.path] path_to_gpx(matched_path, filename="matched_track.gpx") ``` -------------------------------- ### Create Map Graph from osmnx GeoDataFrames (Approach 2) Source: https://github.com/wannesm/leuvenmapmatching/blob/master/docs/usage/openstreetmap.md An alternative method to create a map graph from osmnx GeoDataFrames. This approach projects the graph and then extracts nodes and edges, adding nodes using their projected latitude and longitude. It also shows how to extract edges directly from the networkx graph. ```python 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) ``` -------------------------------- ### Create Map Graph using osmread Source: https://github.com/wannesm/leuvenmapmatching/blob/master/docs/usage/openstreetmap.md Process a downloaded OSM XML file to create a map graph. This snippet focuses on 'ways' with a 'highway' tag and adds edges in both directions to account for one-way streets. Nodes are added with their latitude and longitude. ```python 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() ``` -------------------------------- ### Perform Map Matching with DistanceMatcher Source: https://context7.com/wannesm/leuvenmapmatching/llms.txt Utilize the DistanceMatcher class to align GPS observations to a road network graph using an HMM. Configure parameters like observation noise and path width for optimal matching. Results include predicted nodes and edges. ```python from leuvenmapmatching.matcher.distance import DistanceMatcher from leuvenmapmatching.map.inmem import InMemMap # Create map map_con = InMemMap("mymap", graph={ "A": ((1, 1), ["B", "C", "X"]), "B": ((1, 3), ["A", "C", "D", "K"]), "C": ((2, 2), ["A", "B", "D", "E", "X", "Y"]), "D": ((2, 4), ["B", "C", "F", "E", "K", "L"]), "E": ((3, 3), ["C", "D", "F", "Y"]), "F": ((3, 5), ["D", "E", "L"]), "X": ((2, 0), ["A", "C", "Y"]), "Y": ((3, 1), ["X", "C", "E"]), "K": ((1, 5), ["B", "D", "L"]), "L": ((2, 6), ["K", "D", "F"]) }, use_latlon=False) # GPS observations (noisy path) path = [ (0.8, 0.7), (0.9, 0.7), (1.1, 1.0), (1.2, 1.5), (1.2, 1.6), (1.1, 2.0), (1.1, 2.3), (1.3, 2.9), (1.2, 3.1), (1.5, 3.2), (1.8, 3.5), (2.0, 3.7), (2.3, 3.5), (2.4, 3.2), (2.6, 3.1), (2.9, 3.1), (3.0, 3.2), (3.1, 3.8), (3.0, 4.0), (3.1, 4.3), (3.1, 4.6), (3.0, 4.9) ] # Create matcher with parameters matcher = DistanceMatcher( map_con, max_dist=2, # Max distance from road (hard cutoff) max_dist_init=2, # Max distance for initial point obs_noise=1, # Std deviation of GPS noise min_prob_norm=0.5, # Min normalized probability threshold max_lattice_width=5, # Limit paths to explore (speed optimization) non_emitting_states=True # Allow states between observations ) # Perform matching states, last_idx = matcher.match(path) # Get results nodes = matcher.path_pred_onlynodes # ['A', 'C', 'B', 'D', 'E', 'F'] edges = matcher.path_pred # [('A','C'), ('C','B'), ('B','D'), ...] # Print statistics matcher.print_lattice_stats() # Output: # Stats lattice # ############# # nbr levels : 22 # nbr lattice : 89 # avg lattice[level] : 4.045 # avg obs distance : 0.234 # last logprob : -15.432 ``` -------------------------------- ### Download and Project OSM Graph using osmnx Source: https://github.com/wannesm/leuvenmapmatching/blob/master/docs/usage/openstreetmap.md Retrieve a road network graph for a specified place using osmnx and then project it to a suitable coordinate system for accurate distance calculations. This approach simplifies data acquisition. ```python import osmnx graph = ox.graph_from_place('Leuven, Belgium', network_type='drive', simplify=False) graph_proj = ox.project_graph(graph) ``` -------------------------------- ### Project Latitude-Longitude to X-Y Coordinates Source: https://github.com/wannesm/leuvenmapmatching/blob/master/docs/usage/latitudelongitude.md This snippet demonstrates projecting latitude-longitude coordinates to an X-Y frame using the `to_xy()` method of an `InMemMap` object. It also shows how to convert a list of GPS locations to X-Y coordinates for use with the projected map. ```python from leuvenmapmatching.map.inmem import InMemMap map_con_latlon = InMemMap("myosm", use_latlon=True) # Add edges/nodes map_con_xy = map_con_latlon.to_xy() route_latlon = [] # Add GPS locations route_xy = [map_con_xy.latlon2yx(latlon) for latlon in route_latlon] ``` -------------------------------- ### Increase Logging Verbosity Source: https://github.com/wannesm/leuvenmapmatching/blob/master/docs/usage/debug.md To inspect intermediate algorithm steps, increase the logger's verbosity level and add a stream handler to output logs to sys.stdout. This requires importing the necessary modules and the logger object from leuvenmapmatching. ```python import sys import logging import leuvenmapmatching logger = leuvenmapmatching.logger logger.setLevel(logging.DEBUG) logger.addHandler(logging.StreamHandler(sys.stdout)) ``` -------------------------------- ### Match with Non-Emitting States Source: https://github.com/wannesm/leuvenmapmatching/blob/master/docs/usage/introduction.md Enables non-emitting states to bridge gaps between observations, using obs_noise_ne to influence path preference. ```python from leuvenmapmatching.matcher.distance import DistanceMatcher from leuvenmapmatching.map.inmem import InMemMap from leuvenmapmatching import visualization as mmviz path = [(1, 0), (7.5, 0.65), (10.1, 1.9)] mapdb = InMemMap("mymap", graph={ "A": ((1, 0.00), ["B"]), "B": ((3, 0.00), ["A", "C"]), "C": ((4, 0.70), ["B", "D"]), "D": ((5, 1.00), ["C", "E"]), "E": ((6, 1.00), ["D", "F"]), "F": ((7, 0.70), ["E", "G"]), "G": ((8, 0.00), ["F", "H"]), "H": ((10, 0.0), ["G", "I"]), "I": ((10, 2.0), ["H"]) }, use_latlon=False) matcher = DistanceMatcher(mapdb, max_dist_init=0.2, obs_noise=1, obs_noise_ne=10, non_emitting_states=True, only_edges=True , max_lattice_width=5) states, _ = matcher.match(path) nodes = matcher.path_pred_onlynodes print("States\n------") print(states) print("Nodes\n------") print(nodes) print("") matcher.print_lattice_stats() mmviz.plot_map(mapdb, matcher=matcher, show_labels=True, show_matching=True filename="output.png")) ``` -------------------------------- ### Backtrack Matching Lattice Source: https://github.com/wannesm/leuvenmapmatching/blob/master/docs/usage/debug.md To backtrack through the lattice, find the matching object for the last observation with the highest log probability. Previous matching objects connected to the current one can be queried using `m.prev` (best previous match) or `m.prev_other` (all previous matches with a connection). ```python >>> m = max(matcher.lattice[len(path)-1].values_all(), key=lambda m: m.logprob) >>> m.logprob -0.6835815469734807 ``` ```python >>> m.prev # Best previous match with a connection (multiple if equal probability) {Matching} ``` ```python >>> m.prev_other # All previous matches in the lattice with a connection {Matching, Matching, Matching, Matching} ``` -------------------------------- ### Visualize Map Matching Results Source: https://context7.com/wannesm/leuvenmapmatching/llms.txt Utilizes built-in plotting functions to visualize map matching results, including the road network, path, and matching connections. Supports OpenStreetMap background for latitude-longitude maps. ```python from leuvenmapmatching import visualization as mmviz from leuvenmapmatching.matcher.distance import DistanceMatcher from leuvenmapmatching.map.inmem import InMemMap import matplotlib.pyplot as plt # Create map and run matching mapdb = InMemMap("mymap", graph={ "A": ((1, 0.00), ["B"]), "B": ((3, 0.00), ["A", "C"]), "C": ((4, 0.70), ["B", "D"]), "D": ((5, 1.00), ["C", "E"]), "E": ((6, 1.00), ["D", "F"]), "F": ((7, 0.70), ["E", "G"]), "G": ((8, 0.00), ["F", "H"]), "H": ((10, 0.0), ["G", "I"]), "I": ((10, 2.0), ["H"]) }, use_latlon=False) path = [(1, 0), (7.5, 0.65), (10.1, 1.9)] matcher = DistanceMatcher(mapdb, max_dist_init=0.2, obs_noise=1, obs_noise_ne=10, non_emitting_states=True, max_lattice_width=5) matcher.match(path) # Plot with graph, path, and matching lines fig, ax = mmviz.plot_map( mapdb, matcher=matcher, show_labels=True, # Show node labels show_graph=True, # Show road network show_matching=True, # Show matching connections filename="matching.png" ) # For latitude-longitude maps with OSM background # mmviz.plot_map( # map_con, # matcher=matcher, # use_osm=True, # OpenStreetMap background # z=18, # Zoom level # show_matching=True # ) # Export lattice to Graphviz DOT format for debugging with open("lattice.dot", "w") as f: matcher.lattice_dot(file=f, precision=2) # Get matching statistics matcher.print_lattice_stats() # Inspect early stopping issues matcher.inspect_early_stopping() ``` -------------------------------- ### Define Custom Lattice Object Source: https://github.com/wannesm/leuvenmapmatching/blob/master/docs/usage/customdistributions.md Inherit from `BaseMatching` to create a custom lattice object for storing additional information. Pass this custom object to the `Matcher` constructor. ```python from leuvenmapmatching.map.base import BaseMatching class MyMatching(BaseMatching): ... matcher = MyMatcher(mapdb, matching=MyMatching) ``` -------------------------------- ### Handle Sparse Observations with Non-Emitting States Source: https://context7.com/wannesm/leuvenmapmatching/llms.txt Enables the matcher to infer intermediate road segments for sparse GPS data by enabling non-emitting states. Adjust 'obs_noise_ne' for higher tolerance in non-emitting segments. ```python from leuvenmapmatching.matcher.distance import DistanceMatcher from leuvenmapmatching.map.inmem import InMemMap # Map with a curved road mapdb = InMemMap("mymap", graph={ "A": ((1, 0.00), ["B"]), "B": ((3, 0.00), ["A", "C"]), "C": ((4, 0.70), ["B", "D"]), "D": ((5, 1.00), ["C", "E"]), "E": ((6, 1.00), ["D", "F"]), "F": ((7, 0.70), ["E", "G"]), "G": ((8, 0.00), ["F", "H"]), "H": ((10, 0.0), ["G", "I"]), "I": ((10, 2.0), ["H"]) }, use_latlon=False) # Sparse observations (only 3 points for a long path) path = [(1, 0), (7.5, 0.65), (10.1, 1.9)] # Matcher with non-emitting states enabled matcher = DistanceMatcher( mapdb, max_dist_init=0.2, # Strict initial distance obs_noise=1, # Noise for emitting states obs_noise_ne=10, # Higher tolerance for non-emitting states non_emitting_states=True, # Enable non-emitting states only_edges=True, # Match to edges (not nodes) max_lattice_width=5, non_emitting_length_factor=0.75 # Penalty for longer NE sequences ) states, _ = matcher.match(path) nodes = matcher.path_pred_onlynodes print("Matched nodes:", nodes) # Output: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'] # All intermediate nodes are inferred despite only 3 observations ``` -------------------------------- ### Inspect Best Matching Object Source: https://github.com/wannesm/leuvenmapmatching/blob/master/docs/usage/debug.md The best match is stored in `matcher.lattice_best`. This attribute is a list of `Matching` objects. Each object summarizes its information as a tuple. For edge matches, it includes start/end labels, the best point on the edge, and the locations of the edge's endpoints. ```python >>> matcher.lattice_best [Matching, Matching, Matching, ... ``` ```python >>> match = matcher.lattice_best[0] >>> match.edge_m.l1, match.edge_m.l2 # Edge start/end labels ('A', 'B') >>> match.edge_m.pi # Best point on A-B edge (1.0, 1.0) >>> match.edge_m.p1, match.edge_m.p2 # Locations of A and B ((1, 1), (1, 3)) >>> match.edge_o.l1, match.edge_o.l2 # Observation ('O0', None) >>> match.edge_o.pi # Location of observation O0, because no second location (0.8, 0.7) >>> match.edge_o.p1 # Same as pi because no interpolation (0.8, 0.7) ``` -------------------------------- ### Read OSM File with Latitude-Longitude Source: https://github.com/wannesm/leuvenmapmatching/blob/master/docs/usage/latitudelongitude.md Use this snippet to read an OpenStreetMap file directly into an InMemMap object while enabling latitude-longitude support. Ensure the `use_latlon` flag is set to `True` in the `Map` constructor. ```python from leuvenmapmatching.map.inmem import InMemMap map_con = InMemMap("myosm", use_latlon=True) for entity in osmread.parse_file(osm_fn): 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) 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() ``` -------------------------------- ### Plot Map with Custom Matplotlib Axis Source: https://github.com/wannesm/leuvenmapmatching/blob/master/docs/usage/visualisation.md Plot the map matching results onto a pre-defined matplotlib axis object. This allows for integration into existing figures. ```python fig, ax = plt.subplots(1, 1) mmviz.plot_map(map_con, matcher=matcher, ax=ax, show_labels=True, show_matching=True, show_graph=True, filename="my_plot.png") ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.