Skip to content
Merged
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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
/captures
.externalNativeBuild
.cxx
app/gpxFiles/*
app/*.geojson
app/callout*.txt

# Gradle files
.gradle/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,8 @@ class GeoEngine {
mapMatchFilter.filter(
LngLatAlt(location.longitude, location.latitude),
gridState,
FeatureCollection()
FeatureCollection(),
false
)
}
val matchedWay = mapMatchFilter.matchedWay
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -369,6 +370,7 @@ fun addIntersectionCalloutFromDescription(
val incomingHeading = (heading.toDouble() + 180.0) % 360.0

val directions = getCombinedDirectionSegments(incomingHeading)
val intersectionResults = emptyList<PositionedString>().toMutableList()
for (way in description.intersection.members) {

val wayHeading = way.heading(description.intersection)
Expand Down Expand Up @@ -408,7 +410,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,
Expand All @@ -417,4 +419,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)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -96,53 +95,44 @@ 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
// order of the coordinates. This results in the same coordinate being duplicated at
// 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()
}
Expand Down Expand Up @@ -294,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
}
Expand Down Expand Up @@ -356,12 +346,12 @@ class RoadFollower(val parent: MapMatchFilter,
}

var newRoute = emptyList<Way>().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)
Expand All @@ -370,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
Expand Down Expand Up @@ -455,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) {
Expand Down Expand Up @@ -554,9 +546,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
Expand Down Expand Up @@ -679,15 +669,15 @@ 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.
addWaysToFollowers(intersection, follower, iterator, gridState.ruler)
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.
Expand Down Expand Up @@ -725,7 +715,7 @@ class MapMatchFilter {
}

var colorIndex = 0
fun filter(location: LngLatAlt, gridState: GridState, collection: FeatureCollection): Triple<LngLatAlt?, Feature?, String> {
fun filter(location: LngLatAlt, gridState: GridState, collection: FeatureCollection, dump: Boolean): Triple<LngLatAlt?, Feature?, String> {

extendFollowerList(location, gridState)

Expand Down Expand Up @@ -792,7 +782,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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,27 +170,34 @@ class Way : Feature() {
}
}

fun doesIntersect(other: Way) : Intersection? {
for(ours in intersections) {
fun doesIntersect(other: Way) : Pair<Intersection?, Int> {
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 {
Expand Down Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>().toMutableSet()
for (way in intersection.members) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ class IntersectionsTestMvt {
mapMatchFilter.filter(
location = currentLocation,
gridState = gridState,
collection = FeatureCollection()
collection = FeatureCollection(),
dump = false
)
val userGeometry = UserGeometry(
location = currentLocation,
Expand Down
Loading
Loading