From 19329456e948ad20dda93f4689289d36d86ad96a Mon Sep 17 00:00:00 2001 From: Dave Craig Date: Wed, 21 May 2025 13:57:41 +0100 Subject: [PATCH 1/5] Sort intersection callouts by heading from left to right This means that all intersection callouts start from the furthest Way to the left. That's less surprising than the previously random order. --- .../soundscape/geoengine/callouts/IntersectionUtils.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/callouts/IntersectionUtils.kt b/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/callouts/IntersectionUtils.kt index 4e0cad850..29010ed75 100644 --- a/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/callouts/IntersectionUtils.kt +++ b/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/callouts/IntersectionUtils.kt @@ -369,6 +369,7 @@ fun addIntersectionCalloutFromDescription( val incomingHeading = (heading.toDouble() + 180.0) % 360.0 val directions = getCombinedDirectionSegments(incomingHeading) + val intersectionResults = emptyList().toMutableList() for (way in description.intersection.members) { val wayHeading = way.heading(description.intersection) @@ -408,7 +409,7 @@ fun addIntersectionCalloutFromDescription( var destinationText = way.getName(way.intersections[WayEnd.START.id] == description.intersection, gridState, localizedContext) val intersectionCallout = localizedContext?.getString(roadDirectionId, destinationText) ?: "\t$destinationText $unlocalizedDirection" - results.add( + intersectionResults.add( PositionedString( text = intersectionCallout, type = AudioType.COMPASS, @@ -417,4 +418,9 @@ fun addIntersectionCalloutFromDescription( ) } } + // Order intersection callout by heading from left to right + intersectionResults.sortWith(Comparator { p1, p2 -> + p1.heading!!.compareTo(p2.heading!!) + }) + results.addAll(intersectionResults) } From 4142be318024d24f677bdd3350320c25e65f60e9 Mon Sep 17 00:00:00 2001 From: Dave Craig Date: Thu, 22 May 2025 13:04:25 +0100 Subject: [PATCH 2/5] MapMatchFilter and intersection callout fixes based on user testing Issue #506 had missing intersection callouts which was due to a miscalculation of the angle between two headings. It should have been calculating the angle between the lines assuming them to be bidirectional rather than just the headings. The MapMatchFilter has been tweaked to better deal with short spells where a route is deviated from. --- .gitignore | 2 +- .../soundscape/geoengine/GeoEngine.kt | 3 ++- .../geoengine/callouts/IntersectionUtils.kt | 5 ++-- .../geoengine/filters/MapMatchFilter.kt | 11 +++----- .../soundscape/geoengine/utils/GeoUtils.kt | 23 ++++++++++++++-- .../soundscape/IntersectionsTestMvt.kt | 3 ++- .../soundscape/MvtTileTest.kt | 26 +++++++++---------- .../soundscape/{ => gpxFiles}/travel-2.gpx | 0 .../soundscape/{ => gpxFiles}/travel-3.gpx | 0 .../{ => gpxFiles}/travel-small.gpx | 0 .../soundscape/{ => gpxFiles}/travel.gpx | 0 11 files changed, 46 insertions(+), 27 deletions(-) rename app/src/test/res/org/scottishtecharmy/soundscape/{ => gpxFiles}/travel-2.gpx (100%) rename app/src/test/res/org/scottishtecharmy/soundscape/{ => gpxFiles}/travel-3.gpx (100%) rename app/src/test/res/org/scottishtecharmy/soundscape/{ => gpxFiles}/travel-small.gpx (100%) rename app/src/test/res/org/scottishtecharmy/soundscape/{ => gpxFiles}/travel.gpx (100%) diff --git a/.gitignore b/.gitignore index 28d4534fc..f5f8a3da9 100644 --- a/.gitignore +++ b/.gitignore @@ -12,8 +12,8 @@ /captures .externalNativeBuild .cxx +app/gpxFiles/* app/*.geojson -app/callout*.txt # Gradle files .gradle/ diff --git a/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/GeoEngine.kt b/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/GeoEngine.kt index 3eb4aaff2..58deb9fa6 100644 --- a/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/GeoEngine.kt +++ b/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/GeoEngine.kt @@ -360,7 +360,8 @@ class GeoEngine { mapMatchFilter.filter( LngLatAlt(location.longitude, location.latitude), gridState, - FeatureCollection() + FeatureCollection(), + false ) } val matchedWay = mapMatchFilter.matchedWay diff --git a/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/callouts/IntersectionUtils.kt b/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/callouts/IntersectionUtils.kt index 29010ed75..b6967f886 100644 --- a/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/callouts/IntersectionUtils.kt +++ b/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/callouts/IntersectionUtils.kt @@ -14,7 +14,7 @@ import org.scottishtecharmy.soundscape.geoengine.mvttranslation.Intersection import org.scottishtecharmy.soundscape.geoengine.mvttranslation.Way import org.scottishtecharmy.soundscape.geoengine.mvttranslation.WayEnd import org.scottishtecharmy.soundscape.geoengine.utils.Direction -import org.scottishtecharmy.soundscape.geoengine.utils.calculateHeadingOffset +import org.scottishtecharmy.soundscape.geoengine.utils.calculateSmallestAngleBetweenLines import org.scottishtecharmy.soundscape.geoengine.utils.checkWhetherIntersectionIsOfInterest import org.scottishtecharmy.soundscape.geoengine.utils.confectNamesForRoad import org.scottishtecharmy.soundscape.geoengine.utils.findShortestDistance @@ -100,7 +100,8 @@ fun getRoadsDescriptionFromFov(gridState: GridState, userGeometry.ruler.distanceToLineString(userGeometry.mapMatchedLocation.point, road.geometry as LineString) val snappedHeading = userGeometry.snappedHeading() if (snappedHeading != null) { - if (calculateHeadingOffset(roadDistance.heading, snappedHeading) > 45.0) { + var innerAngle = calculateSmallestAngleBetweenLines(roadDistance.heading, snappedHeading) + if (innerAngle > 45.0) { // This way is not at the angle of travel, so skip it continue } diff --git a/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/filters/MapMatchFilter.kt b/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/filters/MapMatchFilter.kt index 493242d6d..9f2c61178 100644 --- a/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/filters/MapMatchFilter.kt +++ b/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/filters/MapMatchFilter.kt @@ -12,7 +12,7 @@ import org.scottishtecharmy.soundscape.geoengine.utils.rulers.CheapRuler import org.scottishtecharmy.soundscape.geoengine.utils.PointAndDistanceAndHeading import org.scottishtecharmy.soundscape.geoengine.utils.addSidewalk import org.scottishtecharmy.soundscape.geoengine.utils.bearingFromTwoPoints -import org.scottishtecharmy.soundscape.geoengine.utils.calculateHeadingOffset +import org.scottishtecharmy.soundscape.geoengine.utils.calculateSmallestAngleBetweenLines import org.scottishtecharmy.soundscape.geoengine.utils.circleToPolygon import org.scottishtecharmy.soundscape.geoengine.utils.clone import org.scottishtecharmy.soundscape.geoengine.utils.findShortestDistance @@ -26,7 +26,6 @@ import org.scottishtecharmy.soundscape.geojsonparser.geojson.LineString import org.scottishtecharmy.soundscape.geojsonparser.geojson.LngLatAlt import org.scottishtecharmy.soundscape.geojsonparser.geojson.Point import org.scottishtecharmy.soundscape.geojsonparser.moshi.GeoJsonObjectMoshiAdapter -import kotlin.math.abs import kotlin.math.asin import kotlin.math.cos import kotlin.math.min @@ -554,9 +553,7 @@ class RoadFollower(val parent: MapMatchFilter, val roadHeading = nearestPoint?.heading if (!gpsHeading.isNaN() && roadHeading != null) { - var headingDifference = calculateHeadingOffset(gpsHeading, roadHeading) - if (headingDifference > 90.0) - headingDifference = 180.0 - headingDifference + var headingDifference = calculateSmallestAngleBetweenLines(gpsHeading, roadHeading) val cosHeadingDifference = cos(toRadians(headingDifference)) if (cosHeadingDifference < 0.703) { // Unreliable GPS heading - the GPS is moving in a very different direction @@ -725,7 +722,7 @@ class MapMatchFilter { } var colorIndex = 0 - fun filter(location: LngLatAlt, gridState: GridState, collection: FeatureCollection): Triple { + fun filter(location: LngLatAlt, gridState: GridState, collection: FeatureCollection, dump: Boolean): Triple { extendFollowerList(location, gridState) @@ -792,7 +789,7 @@ class MapMatchFilter { } } if(useDijkstra) { - val testDistance = (follower.averagePointGap * 3) + 10.0 + val testDistance = (follower.averagePointGap * 8) + 15.0 val shortestDistance = findShortestDistance( matchedLocation!!.point, matched, diff --git a/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/utils/GeoUtils.kt b/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/utils/GeoUtils.kt index 07928812c..cc4af7d98 100644 --- a/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/utils/GeoUtils.kt +++ b/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/utils/GeoUtils.kt @@ -1209,11 +1209,30 @@ fun pointIsWithinBoundingBox(point: LngLatAlt?, box: BoundingBox) : Boolean { * heading of a road. * @param heading1 * @param heading2 - * @return The inner angle between the two headings in degrees. + * @return The inner angle between the two headings in degrees. This can be up to 180.0 degrees + * because that's the maximum difference between headings. */ fun calculateHeadingOffset(heading1: Double, heading2: Double): Double { - var diff = abs(heading1 - heading2) % 360.0 + val normalizedHeading1 = (heading1 % 360.0 + 360.0) % 360.0 + val normalizedHeading2 = (heading2 % 360.0 + 360.0) % 360.0 + var diff = abs(normalizedHeading1 - normalizedHeading2) if (diff > 180.0) diff = 360.0 - diff return diff } + +/** + * calculateSmallestAngleBetweenLines calculates the smallest angle between two lines. + * + * heading of a road. + * @param heading1 + * @param heading2 + * @return The inner angle between the two lines in degrees. This can be up to 90.0 degrees as it's + * the smallest angle between the two lines. + */ +fun calculateSmallestAngleBetweenLines(heading1: Double, heading2: Double): Double { + var innerAngle = calculateHeadingOffset(heading1, heading2) + if (innerAngle > 90.0) + innerAngle = 180.0 - innerAngle + return innerAngle +} diff --git a/app/src/test/java/org/scottishtecharmy/soundscape/IntersectionsTestMvt.kt b/app/src/test/java/org/scottishtecharmy/soundscape/IntersectionsTestMvt.kt index a070febbe..e3be85681 100644 --- a/app/src/test/java/org/scottishtecharmy/soundscape/IntersectionsTestMvt.kt +++ b/app/src/test/java/org/scottishtecharmy/soundscape/IntersectionsTestMvt.kt @@ -21,7 +21,8 @@ class IntersectionsTestMvt { mapMatchFilter.filter( location = currentLocation, gridState = gridState, - collection = FeatureCollection() + collection = FeatureCollection(), + dump = false ) val userGeometry = UserGeometry( location = currentLocation, diff --git a/app/src/test/java/org/scottishtecharmy/soundscape/MvtTileTest.kt b/app/src/test/java/org/scottishtecharmy/soundscape/MvtTileTest.kt index e12e890a1..27faa443d 100644 --- a/app/src/test/java/org/scottishtecharmy/soundscape/MvtTileTest.kt +++ b/app/src/test/java/org/scottishtecharmy/soundscape/MvtTileTest.kt @@ -37,6 +37,10 @@ import org.scottishtecharmy.soundscape.geoengine.utils.createPolygonFromTriangle import org.scottishtecharmy.soundscape.geoengine.utils.getFovTriangle import org.scottishtecharmy.soundscape.geojsonparser.geojson.FeatureCollection import org.scottishtecharmy.soundscape.geojsonparser.geojson.LngLatAlt +import kotlin.io.path.Path +import kotlin.io.path.listDirectoryEntries +import kotlin.io.path.name +import kotlin.io.path.nameWithoutExtension /** * FileGridState overrides ProtomapsGridState updateTile to get tiles from test resources instead of @@ -99,11 +103,10 @@ private fun vectorTileToGeoJsonFromFile( } private fun parseGpxFromFile(filename: String): FeatureCollection { - val path = "src/test/res/org/scottishtecharmy/soundscape/" val fc = FeatureCollection() var currentFeature = Feature() - File(path + filename).useLines { lines -> + File(filename).useLines { lines -> lines.forEach { line -> // Get the location @@ -579,13 +582,11 @@ class MvtTileTest { ) // Update the nearest road filter with our new location - if(startIndex + index == 526) { - mapMatchFilter.dump() - } val mapMatchedResult = mapMatchFilter.filter( LngLatAlt(location.longitude, location.latitude), gridState, - collection + collection, + false ) if(mapMatchedResult.first != null) { @@ -643,13 +644,12 @@ class MvtTileTest { @OptIn(ExperimentalCoroutinesApi::class) @Test fun testCallouts() { - testMovingGrid("travel.gpx", "callout-travel.txt", "map-matching.geojson") - testMovingGrid("travel-2.gpx", "callout-travel-2.txt", "map-matching-2.geojson") - testMovingGrid("travel-3.gpx", "callout-travel-3.txt", "map-matching-3.geojson") -// testMovingGrid("travel-4.gpx", "callout-travel-4.txt", "map-matching-4.geojson") -// testMovingGrid("travel-5.gpx", "callout-travel-5.txt", "map-matching-5.geojson") -// testMovingGrid("travel-6.gpx", "callout-travel-6.txt", "map-matching-6.geojson") -// testMovingGrid("travel-7.gpx", "callout-travel-7.txt", "map-matching-7.geojson") + + val directoryPath = Path("src/test/res/org/scottishtecharmy/soundscape/gpxFiles/") + val directoryEntries = directoryPath.listDirectoryEntries("*.gpx") + for(file in directoryEntries) { + testMovingGrid(file.toString(), "gpxFiles/${file.nameWithoutExtension}.txt", "gpxFiles/${file.nameWithoutExtension}.geojson") + } } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/app/src/test/res/org/scottishtecharmy/soundscape/travel-2.gpx b/app/src/test/res/org/scottishtecharmy/soundscape/gpxFiles/travel-2.gpx similarity index 100% rename from app/src/test/res/org/scottishtecharmy/soundscape/travel-2.gpx rename to app/src/test/res/org/scottishtecharmy/soundscape/gpxFiles/travel-2.gpx diff --git a/app/src/test/res/org/scottishtecharmy/soundscape/travel-3.gpx b/app/src/test/res/org/scottishtecharmy/soundscape/gpxFiles/travel-3.gpx similarity index 100% rename from app/src/test/res/org/scottishtecharmy/soundscape/travel-3.gpx rename to app/src/test/res/org/scottishtecharmy/soundscape/gpxFiles/travel-3.gpx diff --git a/app/src/test/res/org/scottishtecharmy/soundscape/travel-small.gpx b/app/src/test/res/org/scottishtecharmy/soundscape/gpxFiles/travel-small.gpx similarity index 100% rename from app/src/test/res/org/scottishtecharmy/soundscape/travel-small.gpx rename to app/src/test/res/org/scottishtecharmy/soundscape/gpxFiles/travel-small.gpx diff --git a/app/src/test/res/org/scottishtecharmy/soundscape/travel.gpx b/app/src/test/res/org/scottishtecharmy/soundscape/gpxFiles/travel.gpx similarity index 100% rename from app/src/test/res/org/scottishtecharmy/soundscape/travel.gpx rename to app/src/test/res/org/scottishtecharmy/soundscape/gpxFiles/travel.gpx From 0d9467ebb5ab58f540374963d73e7e1bf083d557 Mon Sep 17 00:00:00 2001 From: Dave Craig Date: Thu, 22 May 2025 16:21:36 +0100 Subject: [PATCH 3/5] Report -1 priority for intersections with 2 or fewer Ways Without this, we were picking some of these intersections to announce, and then dropping them when it came to announce them. This prevented other more useful intersections being announced. --- .../scottishtecharmy/soundscape/geoengine/utils/TileUtils.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/utils/TileUtils.kt b/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/utils/TileUtils.kt index b6980d26b..ae7da480d 100644 --- a/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/utils/TileUtils.kt +++ b/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/utils/TileUtils.kt @@ -563,6 +563,10 @@ fun checkWhetherIntersectionIsOfInterest( if(testNearestRoad == null) return 0 + // We don't announce intersections with only 2 or fewer Ways + if(intersection.members.size <= 2) + return -1 + var needsFurtherChecking = 0 val setOfNames = emptySet().toMutableSet() for (way in intersection.members) { From f63a9f65f0e9b1d5b8e2c881dde7493aabea6657 Mon Sep 17 00:00:00 2001 From: Dave Craig Date: Thu, 22 May 2025 16:24:17 +0100 Subject: [PATCH 4/5] Temporary nodes for Dijkstra use had the Ways directions incorrect More care was required to ensure that the START and END intersections were indeed at the START and END of the geometry. --- .../geoengine/mvttranslation/WayGenerator.kt | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/mvttranslation/WayGenerator.kt b/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/mvttranslation/WayGenerator.kt index 77d73918b..bbe6a9d44 100644 --- a/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/mvttranslation/WayGenerator.kt +++ b/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/mvttranslation/WayGenerator.kt @@ -170,27 +170,34 @@ class Way : Feature() { } } - fun doesIntersect(other: Way) : Intersection? { - for(ours in intersections) { + fun doesIntersect(other: Way) : Pair { + for((ourIndex ,ours) in intersections.withIndex()) { if(ours == null) continue for(theirs in other.intersections) { if(theirs == null) continue // Check for direct intersection first if(ours == theirs) - return ours + return Pair(ours, ourIndex) + } + } + + for((ourIndex ,ours) in intersections.withIndex()) { + if(ours == null) continue + for(theirs in other.intersections) { + if(theirs == null) continue // Check for tile-edge joiner if((ours.intersectionType == IntersectionType.TILE_EDGE) && - (theirs.intersectionType == IntersectionType.TILE_EDGE)) { + (theirs.intersectionType == IntersectionType.TILE_EDGE)) { for(member in ours.members) { if(theirs.members.contains(member)) { - return theirs + return Pair(theirs, ourIndex) } } } } } - return null + return Pair(null, 0) } fun isSidewalkOrCrossing() : Boolean { @@ -381,8 +388,8 @@ class Way : Feature() { newWay1.length = length1 val newWay2 = Way() - newWay2.intersections[0] = intersections[1] - newWay2.intersections[1] = newIntersection + newWay2.intersections[0] = newIntersection + newWay2.intersections[1] = intersections[1] newWay2.geometry = line2 newWay2.length = length2 From bc3e306ab96f52ced55f4345ae49f591c284ddd9 Mon Sep 17 00:00:00 2001 From: Dave Craig Date: Thu, 22 May 2025 16:25:14 +0100 Subject: [PATCH 5/5] Make 'forwards' logic more robust against tile boundaries Figuring out which direction a line is travelling on across a tile boundary was being done incompletely. The new changes fix that. --- .../geoengine/filters/MapMatchFilter.kt | 75 +++++++++---------- .../soundscape/MvtTileTest.kt | 8 ++ 2 files changed, 42 insertions(+), 41 deletions(-) diff --git a/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/filters/MapMatchFilter.kt b/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/filters/MapMatchFilter.kt index 9f2c61178..fc781eb21 100644 --- a/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/filters/MapMatchFilter.kt +++ b/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/filters/MapMatchFilter.kt @@ -95,19 +95,29 @@ class IndexedLineString { } line = LineString() - var nextIntersection = route[0].doesIntersect(route[1]) - if(nextIntersection == null) { - // We can get here if the tile grid is recalculated, and tiles rejoined and the route is - // left with out of date and disconnected ways. - line = null - return - } - var firstIntersection = route[0].getOtherIntersection(nextIntersection) - var forwards = nextIntersection != route[0].intersections[WayEnd.START.id] - for ((index, way) in route.withIndex()) { - if (index != 0) { - forwards = firstIntersection != way.intersections[WayEnd.END.id] + + var forwards = true + if(index < route.size - 1) { + // Most of the Ways in the route + val (nextIntersection, ourIndex) = route[index].doesIntersect(route[index + 1]) + if(nextIntersection == null) { + // We can get here if the tile grid is recalculated, and tiles rejoined and the route is + // left with out of date and disconnected ways. + line = null + return + } + forwards = (ourIndex == WayEnd.END.id) + } else { + // The last Way in the route + val (firstIntersection, ourIndex) = route[index].doesIntersect(route[index - 1]) + if(firstIntersection == null) { + // We can get here if the tile grid is recalculated, and tiles rejoined and the route is + // left with out of date and disconnected ways. + line = null + return + } + forwards = (ourIndex == WayEnd.START.id) } // And add its coordinates to the LineString along with whether or not we reversed the @@ -115,33 +125,14 @@ class IndexedLineString { // each intersection, but is simple. if (forwards) { line!!.coordinates.addAll((way.geometry as LineString).coordinates) - direction?.set(index, true) } else { line!!.coordinates.addAll((way.geometry as LineString).coordinates.reversed()) - direction?.set(index, false) } + direction?.set(index, forwards) // Note the index at which this Way ends indices?.set(index, line!!.coordinates.size) - - if(firstIntersection != null) { - firstIntersection = way.getOtherIntersection(firstIntersection) - if(firstIntersection?.intersectionType == IntersectionType.TILE_EDGE) { - for(member in firstIntersection!!.members) { - if(member.wayType == WayType.JOINER) { - if((member.intersections[WayEnd.START.id] == null) && - (member.intersections[WayEnd.END.id] == null)) - { - assert(false) - } else { - firstIntersection = member.getOtherIntersection(firstIntersection!!) - break - } - } - } - } - } } hashCode = line?.coordinates.hashCode() } @@ -293,7 +284,7 @@ class RoadFollower(val parent: MapMatchFilter, var minIndex = Int.MAX_VALUE var maxIndex = -1 for((index, way) in route.withIndex()) { - if(way.doesIntersect(newWay) != null) { + if(way.doesIntersect(newWay).first != null) { if(index < minIndex) minIndex = index if(index > maxIndex) maxIndex = index } @@ -355,12 +346,12 @@ class RoadFollower(val parent: MapMatchFilter, } var newRoute = emptyList().toMutableList() - if(route.first().doesIntersect(newWay) != null) { + if(route.first().doesIntersect(newWay).first != null) { if(!route.first().intersections.contains(null)) { newRoute.add(newWay) newRoute.addAll(route) } - } else if(route.last().doesIntersect(newWay) != null) { + } else if(route.last().doesIntersect(newWay).first != null) { if(!route.last().intersections.contains(null)) { newRoute.addAll(route) newRoute.add(newWay) @@ -369,15 +360,17 @@ class RoadFollower(val parent: MapMatchFilter, assert(false) } if(newRoute.isNotEmpty()) { - if (newRoute[0].doesIntersect(newRoute[1]) == null) { + if (newRoute[0].doesIntersect(newRoute[1]).first == null) { assert(false) } route = newRoute validateRoute() ils.updateFromRoute(route) - nearestPoint?.let { point -> - nearestPoint = ruler.distanceToLineString(point.point, ils.line as LineString) + if(ils.line != null) { + nearestPoint?.let { point -> + nearestPoint = ruler.distanceToLineString(point.point, ils.line as LineString) + } } directionOnLine = 0.0 directionHysteresis = 5 @@ -454,7 +447,7 @@ class RoadFollower(val parent: MapMatchFilter, !nearestPoint.positionAlongLine.isNaN()) { val delta = nearestPoint.positionAlongLine - lastNearestPoint.positionAlongLine - if((directionOnLine != 0.0) && (sign(delta) != sign(directionOnLine))) { + if((directionOnLine != 0.0) && ((sign(delta) != sign(directionOnLine)) || (delta == 0.0))) { // Change of direction? --directionHysteresis if(directionHysteresis == 0) { @@ -676,7 +669,7 @@ class MapMatchFilter { while(iterator.hasNext()) { val follower = iterator.next() val lastWayInRoute = follower.route.last() - val intersection = lastWayInRoute.doesIntersect(way) + var (intersection, ourIndex) = lastWayInRoute.doesIntersect(way) if (intersection != null) { // This road intersects with the last way, so it can either replace it, or // be added on to it. @@ -684,7 +677,7 @@ class MapMatchFilter { added = true } else { val firstWayInRoute = follower.route.first() - val firstIntersection = firstWayInRoute.doesIntersect(way) + val (firstIntersection, ourIndex2) = firstWayInRoute.doesIntersect(way) if (firstIntersection != null) { // This road intersects with the first way, so it can either replace it, // or be added on to it. diff --git a/app/src/test/java/org/scottishtecharmy/soundscape/MvtTileTest.kt b/app/src/test/java/org/scottishtecharmy/soundscape/MvtTileTest.kt index 27faa443d..1b81fb597 100644 --- a/app/src/test/java/org/scottishtecharmy/soundscape/MvtTileTest.kt +++ b/app/src/test/java/org/scottishtecharmy/soundscape/MvtTileTest.kt @@ -646,6 +646,14 @@ class MvtTileTest { fun testCallouts() { val directoryPath = Path("src/test/res/org/scottishtecharmy/soundscape/gpxFiles/") + + + val resultsStoragePath = "gpxFiles/" + val resultsStorageDir = File(resultsStoragePath) + if (!resultsStorageDir.exists()) { + resultsStorageDir.mkdirs() + } + val directoryEntries = directoryPath.listDirectoryEntries("*.gpx") for(file in directoryEntries) { testMovingGrid(file.toString(), "gpxFiles/${file.nameWithoutExtension}.txt", "gpxFiles/${file.nameWithoutExtension}.geojson")