49 lines
1.9 KiB
Python
49 lines
1.9 KiB
Python
"""Approximate same-start, same-direction full-course matching, without raw samples."""
|
|
|
|
import math
|
|
from itertools import pairwise
|
|
|
|
from .analytics import numeric
|
|
|
|
|
|
def distance(a, b):
|
|
lat1, lat2 = math.radians(a[0]), math.radians(b[0])
|
|
dlat, dlon = lat2 - lat1, math.radians(b[1] - a[1])
|
|
h = math.sin(dlat / 2) ** 2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2) ** 2
|
|
return 12742000 * math.asin(math.sqrt(min(1, h)))
|
|
|
|
|
|
def resample(segments):
|
|
# A recording break must not invent a connecting route.
|
|
if not isinstance(segments, list) or len(segments) != 1:
|
|
return None
|
|
points = segments[0]
|
|
if len(points) < 2 or any(not isinstance(p, list) or len(p) != 2 or
|
|
not all(numeric(v) for v in p) or
|
|
not -90 <= p[0] <= 90 or not -180 <= p[1] <= 180 for p in points):
|
|
return None
|
|
cumulative = [0.0]
|
|
for a, b in pairwise(points):
|
|
cumulative.append(cumulative[-1] + distance(a, b))
|
|
total = cumulative[-1]
|
|
if total < 500:
|
|
return None
|
|
result, cursor = [], 0
|
|
for i in range(33):
|
|
target = total * i / 32
|
|
while cursor < len(points) - 2 and cumulative[cursor + 1] < target:
|
|
cursor += 1
|
|
span = cumulative[cursor + 1] - cumulative[cursor]
|
|
fraction = (target - cumulative[cursor]) / span if span else 0
|
|
result.append([a + (b - a) * fraction for a, b in zip(points[cursor], points[cursor + 1], strict=True)])
|
|
return total, result
|
|
|
|
|
|
def similar(reference, candidate):
|
|
a, b = resample(reference), resample(candidate)
|
|
if not a or not b or abs(b[0] / a[0] - 1) > .1:
|
|
return False
|
|
differences = sorted(distance(x, y) for x, y in zip(a[1], b[1], strict=True))
|
|
return (distance(a[1][0], b[1][0]) <= 100 and distance(a[1][-1], b[1][-1]) <= 100
|
|
and sum(differences) / len(differences) <= 100 and differences[29] <= 200)
|