Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Match multiple parts of the route individually in case of self-intersection #79

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion backend/backend/routing/matching/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def matches(self, lsas: QuerySet, route: LineString) -> Tuple[QuerySet, LineStri
return lsas.filter(pk__in=pks_to_include), route


def get_matches(route: LineString, matchers: List[RouteMatcher]) -> Iterable[LSA]:
def get_matches(route: LineString, matchers: List[RouteMatcher]) -> QuerySet[LSA]:
"""
Return all LSA's that match the route.
"""
Expand Down
35 changes: 27 additions & 8 deletions backend/backend/routing/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
import time
from collections import namedtuple
from typing import Iterable, List
import os

import pyproj
from django.conf import settings
from django.contrib.gis.geos import LineString, Point
from django.contrib.gis.measure import D
from django.core.exceptions import ValidationError
from django.http import JsonResponse, HttpResponseServerError
from django.http import HttpResponseServerError, JsonResponse
from django.http.response import HttpResponse
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
Expand Down Expand Up @@ -277,13 +276,33 @@ def post(self, request, *args, **kwargs):

if usedRouting != "osm" and usedRouting != "drn":
return JsonResponse({"error": "Unsupported value provided for the parameter 'routing'. Choose between 'osm' or 'drn'."})

if matcher == "ml":
unordered_lsas = get_matches(route_linestring, [ ProximityMatcher(search_radius_m=20), MLMatcher(usedRouting) ])
elif matcher == "legacy":
unordered_lsas = get_matches(route_linestring, [ TopologicHypermodelMatcher.from_config_file(f'config/topologic.hypermodel.{usedRouting}.updated.json') ])

if route_linestring.simple:
route_parts = [route_linestring]
else:
return JsonResponse({"error": "Unsupported value provided for the parameter 'matcher'. Choose between 'ml' or 'legacy'."})
# If the route crosses itself, we split it into parts and match each part separately.
# Each part is buffered a bit to make sure the parts keep crossing the intersection fully.
unary_union = route_linestring.unary_union # multilinestring
route_parts = []
buffer = 100 # distance in meter
buffer_wgs84 = buffer / 40000000.0 * 360.0 # wgs84
for part in unary_union:
buffered = route_linestring.intersection(part.buffer(buffer_wgs84))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this part is also not really working. When we intersect the complete route linestring with the buffered segments, intersections of the route linestring are also going to lay in the buffer polygon. Therefore, the intersection of the route_linestring with the buffer polygon will likely result in a MultiLinestring for those cases. Or in a single Linestring, but where we still have the intersection in it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solution would be to elongate / shorten along the line, which is a bit more implementation work but should be easy.

route_parts.append(buffered)

unordered_lsas = None # or queryset
for route_part in route_parts: # one part if the route does not cross itself, otherwise multiple buffered parts
if matcher == "ml":
matches = get_matches(route_part, [ ProximityMatcher(search_radius_m=20), MLMatcher(usedRouting) ])
elif matcher == "legacy":
matches = get_matches(route_part, [ TopologicHypermodelMatcher.from_config_file(f'config/topologic.hypermodel.{usedRouting}.updated.json') ])
else:
return JsonResponse({"error": "Unsupported value provided for the parameter 'matcher'. Choose between 'ml' or 'legacy'."})
if unordered_lsas is None:
unordered_lsas = matches
else:
# Remove duplicates from the matches. This may happen when the route crosses itself.
unordered_lsas = unordered_lsas.union(matches)

# Snap the LSA positions to the route as marked waypoints
lsa_snaps = snap_lsas(unordered_lsas, route_linestring)
Expand Down
Loading