From 6b8b215e14dbae093ab5f8638a9d61bdeeadf2e3 Mon Sep 17 00:00:00 2001 From: RZR-UA Date: Sat, 22 Feb 2025 21:16:07 +0100 Subject: [PATCH] Introduce human deduplicateAndJoinTrackSegments v2 --- .../java/net/osmand/gpx/GpxOptimizer.java | 92 +++++++++++++++++-- 1 file changed, 84 insertions(+), 8 deletions(-) diff --git a/OsmAnd-java/src/main/java/net/osmand/gpx/GpxOptimizer.java b/OsmAnd-java/src/main/java/net/osmand/gpx/GpxOptimizer.java index b2876c419e6..0fef90a9fa3 100644 --- a/OsmAnd-java/src/main/java/net/osmand/gpx/GpxOptimizer.java +++ b/OsmAnd-java/src/main/java/net/osmand/gpx/GpxOptimizer.java @@ -1,5 +1,6 @@ package net.osmand.gpx; +import net.osmand.shared.data.KLatLon; import net.osmand.shared.gpx.primitives.Track; import net.osmand.shared.gpx.primitives.TrkSegment; import net.osmand.shared.gpx.primitives.WptPt; @@ -13,19 +14,19 @@ public class GpxOptimizer { private static final double EDGE_POINTS_MAX_ORTHOGONAL_DISTANCE = 10.0; private static final double PRECISION = KMapUtils.DEFAULT_LATLON_PRECISION; - public static Track deduplicateAndSpliceTrackSegments(Track track) { + public static Track deduplicateAndJoinTrackSegments(Track track) { Set duplicates = new HashSet<>(); findDisplacedEdgePointsToDeduplicate(track, duplicates); List cleanedSegments = new ArrayList<>(); deduplicatePointsToCleanedSegments(track, duplicates, cleanedSegments); - List splicedSegments = new ArrayList<>(); - spliceCleanedSegments(cleanedSegments, splicedSegments); + List joinedSegments = new ArrayList<>(); + joinCleanedSegments(cleanedSegments, joinedSegments); - Track splicedTrack = new Track(); - splicedTrack.setSegments(splicedSegments); - return splicedTrack; + Track joinedTrack = new Track(); + joinedTrack.setSegments(joinedSegments); + return joinedTrack; } private static String llKey(WptPt edge) { @@ -94,7 +95,82 @@ private static boolean isPossiblyDisplacedEdgePoint(int i, List points, S || (i == points.size() - 1 && points.size() > 1 && duplicates.contains(llKey(points.get(i - 1)))); } - private static void spliceCleanedSegments(List cleanedSegments, List splicedSegments) { - // TODO based on IndexRouteRelationCreator.spliceWaysIntoSegments + private static void joinCleanedSegments(List segmentsToJoin, List joinedSegments) { + boolean[] done = new boolean[segmentsToJoin.size()]; + while (true) { + List result = new ArrayList<>(); + for (int i = 0; i < segmentsToJoin.size(); i++) { + if (!done[i]) { + done[i] = true; + if (!segmentsToJoin.get(i).getPoints().isEmpty()) { + addSegmentToResult(result, false, segmentsToJoin.get(i), false); // "head" segment + while (true) { + boolean stop = true; + for (int j = 0; j < segmentsToJoin.size(); j++) { + if (!done[j] && considerSegmentToJoin(result, segmentsToJoin.get(j))) { + done[j] = true; + stop = false; + } + } + if (stop) { + break; // nothing joined + } + } + break; // segment is done + } + } + } + if (result.isEmpty()) { + break; // all done + } + TrkSegment joined = new TrkSegment(); + joined.getPoints().addAll(result); + joinedSegments.add(joined); + } + } + + private static void addSegmentToResult(List result, boolean insert, TrkSegment segment, boolean reverse) { + List points = new ArrayList<>(); + for (WptPt wpt : segment.getPoints()) { + points.add(new WptPt(wpt.getLatitude(), wpt.getLongitude())); + } + if (reverse) { + Collections.reverse(points); + } + result.addAll(insert ? 0 : result.size(), points); + } + + private static boolean considerSegmentToJoin(List result, TrkSegment candidate) { + if (result.isEmpty()) { + return false; + } + + if (candidate.getPoints().isEmpty()) { + return true; + } + + WptPt firstPoint = result.get(0); + WptPt lastPoint = result.get(result.size() - 1); + WptPt firstCandidate = candidate.getPoints().get(0); + WptPt lastCandidate = candidate.getPoints().get(candidate.getPoints().size() - 1); + + KLatLon firstPointLL = new KLatLon(firstPoint.getLatitude(), firstPoint.getLongitude()); + KLatLon lastPointLL = new KLatLon(lastPoint.getLatitude(), lastPoint.getLongitude()); + KLatLon firstCandidateLL = new KLatLon(firstCandidate.getLatitude(), firstCandidate.getLongitude()); + KLatLon lastCandidateLL = new KLatLon(lastCandidate.getLatitude(), lastCandidate.getLongitude()); + + if (KMapUtils.INSTANCE.areLatLonEqual(lastPointLL, firstCandidateLL, PRECISION)) { + addSegmentToResult(result, false, candidate, false); // nodes + Candidate + } else if (KMapUtils.INSTANCE.areLatLonEqual(lastPointLL, lastCandidateLL, PRECISION)) { + addSegmentToResult(result, false, candidate, true); // nodes + etadidnaC + } else if (KMapUtils.INSTANCE.areLatLonEqual(firstPointLL, firstCandidateLL, PRECISION)) { + addSegmentToResult(result, true, candidate, true); // etadidnaC + nodes + } else if (KMapUtils.INSTANCE.areLatLonEqual(firstPointLL, lastCandidateLL, PRECISION)) { + addSegmentToResult(result, true, candidate, false); // Candidate + nodes + } else { + return false; + } + + return true; } }