Skip to content

Conversation

@tsweckard
Copy link

Summary

  • If a path falls on a section of road that doesn't match the specified direction of the entire roadway, our direction filter can filter out the incorrect path since for example, both paths would be going E/W instead of N/S, it's really a toss up for which path get's returned.
  • Instead of prioritizing unnamed to unnamed edge traversal over unnamed to named edges in our unnamed fallback login (used for ramp path generation), we should keep them the same priority and choose the edge that is the straightest continuation. We ran into a problem where the path was going from a ramp to an unnamed service road instead of choosing the next named road that was straight off the ramp.

Solution

  • Abstract DirectionFilterHelper methods from BufferResource for better testing and code cleanup
  • If the difference in latitude or longitude (whichever is being compared) is less that 1/2 the thresholdDistance in degrees, default to the primary path as the paths can be considered too horizontal or vertical to be accurate. This is with the assumption that the difference between the farthest terminals of both paths is roughly 2 times the thresholdDistance. There are some cases where the roads turn which is why I reduced it to 1/2 the threshold distance.
  • If the previous edge was unnamed, find the straightest edge from next named and unnamed edges instead of just unnamed.

@tsweckard tsweckard changed the title GraphHopper Refinements Directional Filter and Unnamed Fallback Refinements Jan 27, 2026
Point furthestPointOfSecondPath = getTerminalPoint(lineStrings.get(lineStrings.size() - 1), buildUpstream);

boolean selectFirstPath = true;
boolean isNonCardinal = !CARDINAL_DIRECTIONS.contains(direction);
Copy link

@mdorford mdorford Jan 27, 2026

Choose a reason for hiding this comment

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

halfThresholdDistanceInDegrees is an unclear name since we have two different kinds of degrees at play here: lat/long degrees (which are the degrees being used) and bearing degrees (which are not the degrees being used).
Also, that value will be fine for latitude but isn't great for longitude inside the United States. At the southern tip of Texas, you get 100,000 meters per degree longitude but at northern tip of Maine it is only 75,000. This may or may not be that important to what you are doing but a better 'general' number of meters per degree might be 95,000 or 93,000 or something. But, 111,200 might be close enough for what you are doing here.

switch (direction) {
case NORTH:
case SOUTH:
// Default to the first path if Y difference < threshold (paths too horizontal for N/S determination)

Choose a reason for hiding this comment

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

I think that this comment means to say, 'difference < halfThresholdInDegrees'

break;
case EAST:
case WEST:
// Default to the first path if X difference < threshold (paths too vertical for E/W determination)

Choose a reason for hiding this comment

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

I think that this comment means to say, 'difference < halfThresholdInDegrees'

break;
}

if (isNonCardinal) {

Choose a reason for hiding this comment

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

Unless I am wrong, (and I might be wrong), the angle at which yDiff or xDiff will be < halfThresholdInDegrees is about 60 degrees off of the true cardinal direction. So a road that is at angle 59 (which is 59 degrees off of exact north since 'straight north' is zero) would be successfully determined to be 'North' and a road at 61 degrees would default to first path.
In that case, it is easy to adjust the non-cardinal directions to have this same amount of leniency.
(Double check that I haven't made any boneheaded mistakes) the following should work:
if (isNonCardinal) {
// For non-cardinal directions: Calculate bearing between terminal points for better direction accuracy.
// Splits the circle into two halves and checks if the angle is within the specified half.
int bearing = (int) Math.round(AngleCalc.ANGLE_CALC.calcAzimuth(
furthestPointOfFirstPath.getY(), furthestPointOfFirstPath.getX(),
furthestPointOfSecondPath.getY(),furthestPointOfSecondPath.getX()));

        switch (direction) {
            case NORTHEAST:
                selectFirstPath = bearing >= BEARING_DUE_NORTHWEST + 30 || bearing <= BEARING_DUE_SOUTHEAST - 30;
                break;
            case SOUTHWEST:
                selectFirstPath = bearing > BEARING_DUE_SOUTHEAST + 30 && bearing < BEARING_DUE_NORTHWEST - 30;
                break;
            case NORTHWEST:
                selectFirstPath = bearing >= BEARING_DUE_SOUTHWEST + 30 || bearing <= BEARING_DUE_NORTHEAST - 30;
                break;
            case SOUTHEAST:
                selectFirstPath = bearing > BEARING_DUE_NORTHEAST + 30 && bearing < BEARING_DUE_SOUTHWEST - 30;
                break;
            default:
                break;
        }
    }


boolean selectFirstPath = true;
boolean isNonCardinal = !CARDINAL_DIRECTIONS.contains(direction);
double halfThresholdInDegrees = thresholdDistance / 2 / DistanceCalcEarth.METERS_PER_DEGREE;
Copy link

@mdorford mdorford Jan 27, 2026

Choose a reason for hiding this comment

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

A better 'cheap' solution for the difference between meters in lat and long would be to use this value (111,194) for north-south, and either:

  1. use 87,500 for east-west or
  2. have a small lookup table with maybe five values in it so that you were using 75,000 for the xDiff if the lat was around 54, 80,000 for xDiff if the lat was around (something-or-other> all the way down to 100,000 if the lat was around 26 (Brownsville, Texas).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants