forked from graphhopper/graphhopper
-
Notifications
You must be signed in to change notification settings - Fork 0
Directional Filter and Unnamed Fallback Refinements #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
68390e2
refactor/feat: disregard direction filter when paths are in perpendic…
tsweckard edcd8f6
feat: select straightest edge for named and unnamed edges in unnamedF…
tsweckard b30fb90
chore: add documentation
tsweckard 9240113
comment: apply Michael's intercardinal changes to DirectionFilterHelp…
tsweckard 08e306a
feat: add detailed data flow documentation for DirectionFilterHelper
payneBrandon fbe2395
comment: avoid switch, update param description
tsweckard 86fa87d
Merge remote-tracking branch 'origin/revert-master-commits-2' into re…
tsweckard File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
161 changes: 161 additions & 0 deletions
161
web-bundle/src/main/java/com/graphhopper/util/DirectionFilterHelper.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,161 @@ | ||
| package com.graphhopper.util; | ||
|
|
||
| import org.locationtech.jts.geom.LineString; | ||
| import org.locationtech.jts.geom.Point; | ||
|
|
||
| import java.util.Collections; | ||
| import java.util.EnumSet; | ||
| import java.util.List; | ||
|
|
||
| /** | ||
| * Helper class for filtering paths based on cardinal and intercardinal directions. | ||
| */ | ||
| public class DirectionFilterHelper { | ||
|
|
||
| private static final int BEARING_DUE_NORTHEAST = 45; | ||
| private static final int BEARING_DUE_SOUTHEAST = 135; | ||
| private static final int BEARING_DUE_SOUTHWEST = 225; | ||
| private static final int BEARING_DUE_NORTHWEST = 315; | ||
|
|
||
| public enum Direction { | ||
| UNKNOWN, | ||
| NORTH, | ||
| SOUTH, | ||
| EAST, | ||
| WEST, | ||
| NORTHWEST, | ||
| NORTHEAST, | ||
| SOUTHWEST, | ||
| SOUTHEAST, | ||
| BOTH | ||
| } | ||
|
|
||
| private static final EnumSet<Direction> CARDINAL_DIRECTIONS = EnumSet.of( | ||
| Direction.NORTH, | ||
| Direction.SOUTH, | ||
| Direction.EAST, | ||
| Direction.WEST | ||
| ); | ||
|
|
||
| /** | ||
| * Filters a list of 2 LineStrings based on the specified cardinal or intercardinal direction. | ||
| * Compares the furthest points of the first and last LineStrings to determine which aligns | ||
| * best with the desired direction, returning only the selected LineString. | ||
| * @param lineStrings list of 2 LineStrings to filter | ||
| * @param buildUpstream whether the paths were built up or downstream | ||
| * @param direction the direction to filter by | ||
| * @return A list of LineStrings filtered by the specified direction. | ||
| */ | ||
| public List<LineString> filterPathsByDirection(List<LineString> lineStrings, Boolean buildUpstream, Direction direction) { | ||
| if (lineStrings == null || lineStrings.isEmpty() || lineStrings.size() == 1) { | ||
| return lineStrings != null ? lineStrings : Collections.emptyList(); | ||
| } | ||
|
|
||
| if (direction == Direction.BOTH || direction == Direction.UNKNOWN) { | ||
| return lineStrings; | ||
| } | ||
|
|
||
| Point furthestPointOfFirstPath = getTerminalPoint(lineStrings.get(0), buildUpstream); | ||
| Point furthestPointOfSecondPath = getTerminalPoint(lineStrings.get(lineStrings.size() - 1), buildUpstream); | ||
|
|
||
| boolean selectFirstPath = true; | ||
| boolean isNonCardinal = !CARDINAL_DIRECTIONS.contains(direction); | ||
|
|
||
| if (isNonCardinal) { | ||
tsweckard marked this conversation as resolved.
Show resolved
Hide resolved
tsweckard marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // For non-cardinal directions: Calculate bearing between terminal points for better direction accuracy. | ||
| // Since selectFirstPath defaults to true, we only need to adjust if the bearing is 'clearly the other direction.' | ||
| // As in the cardinal directions, if the bearing is within 30 degrees of perpendicular to the desired direction, | ||
| // then leave selectFirstPath = true. | ||
| int bearing = (int) Math.round(AngleCalc.ANGLE_CALC.calcAzimuth( | ||
| furthestPointOfFirstPath.getY(), furthestPointOfFirstPath.getX(), | ||
| furthestPointOfSecondPath.getY(),furthestPointOfSecondPath.getX())); | ||
|
|
||
| // compute target bearing for the requested intercardinal direction | ||
| int targetBearing = switch (direction) { | ||
| case NORTHEAST -> buildUpstream ? BEARING_DUE_NORTHEAST : BEARING_DUE_SOUTHWEST; | ||
| case SOUTHEAST -> buildUpstream ? BEARING_DUE_SOUTHEAST : BEARING_DUE_NORTHWEST; | ||
| case SOUTHWEST -> buildUpstream ? BEARING_DUE_SOUTHWEST : BEARING_DUE_NORTHEAST; | ||
| case NORTHWEST -> buildUpstream ? BEARING_DUE_NORTHWEST : BEARING_DUE_SOUTHEAST; | ||
| default -> 0; | ||
| }; | ||
|
|
||
| // convert difference to range 0-180 (for instance, if two bearings are a difference of 190 then use 170 since going the other way is shorter) | ||
| int diff = Math.abs(bearing - targetBearing); | ||
| if (diff > 180) diff = 360 - diff; | ||
|
|
||
| // more than 120 degrees off -> selectFirstPath = false | ||
| selectFirstPath = diff <= 120; | ||
| } | ||
| else { | ||
| // Calculate the total separation between the two terminal points | ||
| double totalSeparation = Math.sqrt( | ||
| Math.pow(furthestPointOfFirstPath.getX() - furthestPointOfSecondPath.getX(), 2) + | ||
| Math.pow(furthestPointOfFirstPath.getY() - furthestPointOfSecondPath.getY(), 2) | ||
| ); | ||
| double halfSeparation = totalSeparation / 2; | ||
|
|
||
| switch (direction) { | ||
| case NORTH: | ||
| case SOUTH: | ||
| // Default to the first path if Y difference < half of total separation (paths too horizontal for N/S determination) | ||
| double yDiff = Math.abs(furthestPointOfFirstPath.getY() - furthestPointOfSecondPath.getY()); | ||
| if (yDiff < halfSeparation) break; | ||
|
|
||
| selectFirstPath = compareCoordinates( | ||
| furthestPointOfFirstPath.getY(), | ||
| furthestPointOfSecondPath.getY(), | ||
| direction == Direction.NORTH, | ||
| buildUpstream | ||
| ); | ||
| break; | ||
| case EAST: | ||
| case WEST: | ||
| // Default to the first path if X difference < half of total separation (paths too vertical for E/W determination) | ||
| double xDiff = Math.abs(furthestPointOfFirstPath.getX() - furthestPointOfSecondPath.getX()); | ||
| if (xDiff < halfSeparation) break; | ||
|
|
||
| selectFirstPath = compareCoordinates( | ||
| furthestPointOfFirstPath.getX(), | ||
| furthestPointOfSecondPath.getX(), | ||
| direction == Direction.EAST, | ||
| buildUpstream | ||
| ); | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| return selectFirstPath | ||
| ? Collections.singletonList(lineStrings.get(0)) | ||
| : Collections.singletonList(lineStrings.get(lineStrings.size() - 1)); | ||
| } | ||
|
|
||
| /** | ||
| * Retrieves the terminal point of a LineString based on the build direction. | ||
| * | ||
| * @param lineString the LineString to extract the terminal point from | ||
| * @param buildUpstream if true, returns the start point; if false, returns the end point | ||
| * @return the terminal Point of the LineString | ||
| */ | ||
| private Point getTerminalPoint(LineString lineString, boolean buildUpstream) { | ||
| return buildUpstream ? lineString.getStartPoint() : lineString.getEndPoint(); | ||
| } | ||
|
|
||
| /** | ||
| * Compares coordinate values to determine path selection based on the direction. | ||
| * | ||
| * @param firstCoordinate coordinate value from the first path (X for E/W, Y for N/S) | ||
| * @param secondCoordinate coordinate value from the second path | ||
| * @param isPositiveDirection true for NORTH/EAST, false for SOUTH/WEST | ||
| * @param buildUpstream whether paths were built upstream | ||
| * @return true to select the first path, false to select the second path | ||
| */ | ||
| private boolean compareCoordinates(double firstCoordinate, double secondCoordinate, boolean isPositiveDirection, boolean buildUpstream) { | ||
| if (isPositiveDirection) { | ||
| return buildUpstream ? firstCoordinate < secondCoordinate : firstCoordinate > secondCoordinate; | ||
| } else { | ||
| return buildUpstream ? firstCoordinate > secondCoordinate : firstCoordinate < secondCoordinate; | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.