Skip to content

Commit

Permalink
Merge pull request #3 from cosbidev/v2.0.4
Browse files Browse the repository at this point in the history
Update v2.0.4
  • Loading branch information
mtortora-ai committed Sep 15, 2022
2 parents 34c0ba5 + 2564f4d commit 10f993f
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 22 deletions.
2 changes: 1 addition & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
author = 'Matteo Tortora'

# The full version, including alpha/beta/rc tags
release = '2.0.3'
release = '2.0.4'

# -- General configuration ---------------------------------------------------

Expand Down
7 changes: 3 additions & 4 deletions pytrack/analytics/visualization.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,11 +211,10 @@ def draw_candidates(self, candidates, radius):
popup = f"coord: {cand} \n edge_osmid: {label}"
if cand_type:
folium.Circle(location=cand, popup=popup, radius=2, color="yellow", fill=True,
fill_opacity=1).add_to(
fg_cands)
fill_opacity=1).add_to(fg_cands)
else:
folium.Circle(location=cand, popup=popup, radius=1, color="red", fill=True, fill_opacity=1).add_to(
fg_cands)
folium.Circle(location=cand, popup=popup, radius=1, color="orange", fill=True,
fill_opacity=1).add_to(fg_cands)

del self._children[next(k for k in self._children.keys() if k.startswith('layer_control'))]
self.add_child(folium.LayerControl())
Expand Down
27 changes: 22 additions & 5 deletions pytrack/graph/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,44 @@ def get_filters(network_type='drive'):
'elevator|escalator|footway|path|pedestrian|planned|platform|proposed|raceway|steps|track"]'
'["service"!~"emergency_access|parking|parking_aisle|private"]')

osm_filters['bicycle'] = ('["highway"]["area"!~"yes"]["access"!~"private"]'
'["highway"!~"abandoned|bridleway|footway|bus_guideway|construction|corridor|elevator|'
'escalator|planned|platform|proposed|raceway|steps|footway"]'
'["service"!~"private"]["bicycle"!~"no"])')

osm_filters['service'] = ('["highway"]["area"!~"yes"]["access"!~"private"]["highway"!~"abandoned|bridleway|'
'construction|corridor|platform|cycleway|elevator|escalator|footway|path|planned|'
'proposed|raceway|steps|track"]["service"!~"emergency_access|parking|'
'parking_aisle|private"]["psv"!~"no"]["footway"!~"yes"]')

return osm_filters[network_type]


def osm_download(bbox, network_type=None):
def osm_download(bbox, network_type='drive', custom_filter=None):
""" Get the OpenStreetMap response.
Parameters
----------
bbox: tuple
bounding box within N, S, E, W coordinates.
network_type: str or None, optional, default: None
network_type: str, optional, default: 'drive'
Type of street network to obtain.
custom_filter: str or None, optional, default: None
Custom filter to be used instead of the predefined ones to query the Overpass API.
An example of a custom filter is the following '[highway][!"footway"]'.
For more information visit https://overpass-turbo.eu and https://taginfo.openstreetmap.org.
Returns
-------
response: json
Response of the OpenStreetMao API service.
"""
# TODO: add network_type statement
north, south, west, east = bbox

osm_filters = get_filters(network_type='drive')
if custom_filter is not None:
osm_filter = custom_filter
else:
osm_filter = get_filters(network_type=network_type)

url_endpoint = 'https://maps.mail.ru/osm/tools/overpass/api/interpreter'

Expand All @@ -49,7 +66,7 @@ def osm_download(bbox, network_type=None):

overpass_settings = f'[out:{out_resp}][timeout:{timeout}]'

query_str = f'{overpass_settings};(way{osm_filters}({south}, {west}, {north}, {east});>;);out;'
query_str = f'{overpass_settings};(way{osm_filter}({south}, {west}, {north}, {east});>;);out;'

response_json = requests.post(url_endpoint, data={'data': query_str})

Expand Down
4 changes: 2 additions & 2 deletions pytrack/graph/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def _simplification(G, response_json):

junction = [False, False]
if is_oneway:
junction[1:1] = [True if G.degree[node] > 3 else False for node in nodes[1:-1]]
junction[1:1] = [True if G.degree[node] > 2 else False for node in nodes[1:-1]] # Changed >3 to >2 V2.0.4
else:
junction[1:1] = [True if G.degree[node] > 4 else False for node in nodes[1:-1]]

Expand Down Expand Up @@ -259,7 +259,7 @@ def _oneway_path_values(path):
ret: dict
Indicates whether an OSM path is oneway.
"""
return {path[key] for key in path.keys() if key.startswith("oneway") and path[key] == "no"}
return {path[key] for key in path.keys() if key.startswith("oneway")} # Removed 'and path[key] == "no"' v2.0.4


def _is_oneway(path, bidirectional):
Expand Down
5 changes: 5 additions & 0 deletions pytrack/matching/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# This lets you use package.module.Class as package.Class in your code.
from .candidate import Candidate

# This lets Sphinx know you want to document package.module.Class as package.Class.
__all__ = ['Candidate']
6 changes: 6 additions & 0 deletions pytrack/matching/candidate.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ def get_candidates(G, points, interp_dist=1, closest=True, radius=10):
"candidate_type": np.full(len(nodes.index[idx]), False),
"dists": list(dist)} for i, (point, idx, dist) in enumerate(zip(points, idxs, dists))}

no_cands = [node_id for node_id, cand in results.items() if not cand["candidates"]]

if no_cands:
for cand in no_cands:
del results[cand]
print(f"A total of {len(no_cands)} points has no candidates: {*no_cands,}")
return G, results


Expand Down
12 changes: 8 additions & 4 deletions pytrack/matching/mpmatching.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from collections import deque
import math

from . import mpmatching_utils

Expand Down Expand Up @@ -40,12 +41,12 @@ def viterbi_search(G, trellis, start="start", target="target", beta=mpmatching_u
# Initialize joint probability for each node
joint_prob = {}
for u_name in trellis.nodes():
joint_prob[u_name] = 0
joint_prob[u_name] = -float('inf')
predecessor = {}
queue = deque()

queue.append(start)
joint_prob[start] = mpmatching_utils.emission_prob(trellis.nodes[start]["candidate"], sigma)
joint_prob[start] = math.log10(mpmatching_utils.emission_prob(trellis.nodes[start]["candidate"], sigma))
predecessor[start] = None

while queue:
Expand All @@ -58,8 +59,11 @@ def viterbi_search(G, trellis, start="start", target="target", beta=mpmatching_u
for v_name in trellis.successors(u_name):
v = trellis.nodes[v_name]["candidate"]

new_prob = joint_prob[u_name] * mpmatching_utils.transition_prob(G, u, v, beta) * \
mpmatching_utils.emission_prob(v, sigma)
try:
new_prob = joint_prob[u_name] + math.log10(mpmatching_utils.transition_prob(G, u, v, beta)) + \
math.log10(mpmatching_utils.emission_prob(v, sigma))
except Exception as e:
print(e)

if joint_prob[v_name] < new_prob:
joint_prob[v_name] = new_prob
Expand Down
30 changes: 25 additions & 5 deletions pytrack/matching/mpmatching_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,27 @@
from pytrack.matching import candidate

SIGMA_Z = 4.07
BETA = 20
BETA = 3


def _emission_prob(dist, sigma=SIGMA_Z):
""" Compute emission probability of a node
Parameters
----------
dist: float
Distance between a real GPS point and a candidate node.
sigma: float, optional, default: SIGMA_Z
It is an estimate of the magnitude of the GPS error. See https://www.ismll.uni-hildesheim.de/lehre/semSpatial-10s/script/6.pdf
for a more detailed description of its calculation.
Returns
-------
ret: float
Emission probability of a node.
"""
c = 1 / (sigma * math.sqrt(2 * math.pi))
return c * math.exp(-(dist / sigma) ** 2)


# A gaussian distribution
Expand All @@ -17,9 +37,9 @@ def emission_prob(u, sigma=SIGMA_Z):
Parameters
----------
u: dict
u: pytrack.matching.Candidate
Node of the graph.
sigma: float
sigma: float, optional, default: SIGMA_Z
It is an estimate of the magnitude of the GPS error. See https://www.ismll.uni-hildesheim.de/lehre/semSpatial-10s/script/6.pdf
for a more detailed description of its calculation.
Expand Down Expand Up @@ -56,13 +76,13 @@ def transition_prob(G, u, v, beta=BETA):
ret: float
Transition probability between node u and v.
"""
c = 1 / BETA
c = 1 / beta

if u.great_dist and v.great_dist:
delta = abs(
nx.shortest_path_length(G, u.node_id, v.node_id, weight="length", method='dijkstra')
- distance.haversine_dist(*u.coord, *v.coord))
return c * math.exp(-delta / BETA) * 1e5
return c * math.exp(-delta / beta)
else:
return 1

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def reqs(*f):

setuptools.setup(
name='PyTrack-lib',
version='2.0.3',
version='2.0.4',
packages=setuptools.find_packages(),
# namespace_packages=['pytrack'],
url='https://github.com/cosbidev/PyTrack',
Expand Down

0 comments on commit 10f993f

Please sign in to comment.