Skip to content

Commit

Permalink
Merge branch 'refs/heads/main' into renovate/major-hamcrest.version
Browse files Browse the repository at this point in the history
# Conflicts:
#	pom.xml
  • Loading branch information
ndwlocatieservices committed Oct 11, 2024
2 parents a09ab47 + 1fc2052 commit ab2b246
Show file tree
Hide file tree
Showing 78 changed files with 29,384 additions and 883 deletions.
45 changes: 45 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Change Log
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

Here we write upgrade notes. It's a team effort to make them as straightforward as possible.
## [13.0.0] - 2024-10-07

### Added
- properties edgeid and edge key to isochrone match dto

### Changed

### Fixed
- VirtualEdgeIteratorStateReverseExtractor throws class cast exception when VirtualEdgeIteratorState has an IteratorStateImpl member
- QueryGraphWeightingAdapter was blocking non start virtual edges in reverse direction.

## [12.0.0] - 2024-09-24

### Added

### Changed

### Fixed
- Correctly consider driving direction in isochrone when start point is on reversed link. Upstream links are now flipped
on the library side instead of nls-routing-api, making this change breaking.

## [11.1.0] - 2024-09-13

### Added
- Add reverse link ID to GraphHopper network for FCD segments

### Changed

### Fixed

## [11.0.1] - 2024-09-06

### Added
- MINOR Added this changelog file

### Changed

### Fixed
2 changes: 1 addition & 1 deletion azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ extends:
jdkVersion: 1.21
mavenReleaseGoal: deploy
sonarOptionalCoverageExclusions: '**/config/**'
sonarOptionalExclusions: '**/graphhopper/isochrone/ShortestPathTree.java,**/graphhopper/routing/PathRouter.java'
sonarOptionalExclusions: '**/graphhopper/isochrone/ShortestPathTree.java,**/graphhopper/routing/PathRouter.java,**/graphhopper/routing/querygraph/VirtualEdgeIteratorStateReverseExtractor.java'
pushToGitHub: true
2 changes: 1 addition & 1 deletion map-matcher-geometry/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>nu.ndw.nls</groupId>
<artifactId>routing-map-matcher-parent</artifactId>
<version>10.0.3-SNAPSHOT</version>
<version>13.0.2-SNAPSHOT</version>
</parent>
<artifactId>routing-map-matcher-geometry</artifactId>
<dependencies>
Expand Down
2 changes: 1 addition & 1 deletion map-matcher-library/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>nu.ndw.nls</groupId>
<artifactId>routing-map-matcher-parent</artifactId>
<version>10.0.3-SNAPSHOT</version>
<version>13.0.2-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>routing-map-matcher-library</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class PathRouter extends Router {

public PathRouter(BaseGraph graph, EncodingManager encodingManager,
LocationIndex locationIndex,
Map<String, Profile> profilesByName,
Map<String,Profile> profilesByName,
PathDetailsBuilderFactory pathDetailsBuilderFactory,
TranslationMap translationMap, RouterConfig routerConfig,
WeightingFactory weightingFactory,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@

package com.graphhopper.routing.querygraph;

import static nu.ndw.nls.routingmapmatcher.network.model.Link.WAY_ID_KEY;

import com.carrotsearch.hppc.IntArrayList;
import com.graphhopper.routing.util.EncodingManager;
import com.graphhopper.routing.weighting.QueryGraphWeighting;
import com.graphhopper.routing.weighting.Weighting;
import com.graphhopper.storage.EdgeIteratorStateReverseExtractor;
Expand All @@ -34,39 +37,59 @@ public class QueryGraphWeightingAdapter extends QueryGraphWeighting {
private final boolean isSearchDirectionReversed;
private final EdgeIteratorStateReverseExtractor edgeIteratorStateReverseExtractor;
private final int firstVirtualEdgeId;
private final int startSegmentLinkId;
private final EncodingManager encodingManager;

public QueryGraphWeightingAdapter(Weighting weighting, int firstVirtualNodeId, int firstVirtualEdgeId,
IntArrayList closestEdges, EdgeIteratorStateReverseExtractor edgeIteratorStateReverseExtractor,
boolean isSearchDirectionReversed) {
boolean isSearchDirectionReversed, int startSegmentLinkId, EncodingManager encodingManager) {
super(weighting, firstVirtualNodeId, firstVirtualEdgeId, closestEdges);
this.isSearchDirectionReversed = isSearchDirectionReversed;
this.edgeIteratorStateReverseExtractor = edgeIteratorStateReverseExtractor;
this.firstVirtualEdgeId = firstVirtualEdgeId;
this.startSegmentLinkId = startSegmentLinkId;
this.encodingManager = encodingManager;
}

@Override
public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) {
// With bidirectional start segments, the search goes two ways for both down and upstream isochrones.
// The branches that are starting in the wrong direction of travelling (as determined by the nearest
// match) are filtered out.
if (isVirtualEdge(edgeState.getEdge())
&& edgeIteratorStateReverseExtractor.hasReversed(edgeState) != isSearchDirectionReversed) {
if (isVirtualEdge(edgeState.getEdge()) && searchDirectionIsCorrect(edgeState) && isStartSegment(edgeState)) {
return Double.POSITIVE_INFINITY;
}
return super.calcEdgeWeight(edgeState, reverse);
}

private boolean isStartSegment(EdgeIteratorState edgeState) {
return startSegmentLinkId == getLinkId(edgeState);
}

private boolean searchDirectionIsCorrect(EdgeIteratorState edgeState) {
return edgeIteratorStateReverseExtractor.hasReversed(edgeState) != isSearchDirectionReversed;
}

private boolean isVirtualEdge(int edge) {
return edge >= this.firstVirtualEdgeId;
}

private int getLinkId(EdgeIteratorState edge) {
return edge.get(encodingManager.getIntEncodedValue(WAY_ID_KEY));
}

public static QueryGraphWeightingAdapter fromQueryGraph(Weighting baseWeighting, QueryGraph queryGraph,
EdgeIteratorStateReverseExtractor edgeIteratorStateReverseExtractor, boolean reverseFlow) {
EdgeIteratorStateReverseExtractor edgeIteratorStateReverseExtractor, boolean reverseFlow, int matchedLinkId,
EncodingManager encodingManager) {
return new QueryGraphWeightingAdapter(baseWeighting,
queryGraph.getBaseGraph().getNodes(),
queryGraph.getBaseGraph().getEdges(), queryGraph
.getQueryOverlay()
.getClosestEdges(),
edgeIteratorStateReverseExtractor, reverseFlow);
queryGraph.getBaseGraph().getEdges(),
queryGraph
.getQueryOverlay()
.getClosestEdges(),
edgeIteratorStateReverseExtractor,
reverseFlow,
matchedLinkId,
encodingManager);
}
}
Original file line number Diff line number Diff line change
@@ -1,35 +1,39 @@
package com.graphhopper.routing.querygraph;

import static com.graphhopper.storage.EdgeIteratorStateReverseExtractor.getEdgeIteratorStateImpl;

import com.graphhopper.util.EdgeIteratorState;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import lombok.SneakyThrows;

/**
* The geometry direction of the edge iterator wayGeometry does not necessarily reflect the direction of a street or the
* original encoded geometry direction. It is just the direction of the exploration of the graph. GraphHopper sometimes
* reverses the geometry direction in the EdgeIteratorState with respect to the original geometry direction. This causes
* problems with the bearing and fraction calculation as well as the determination whether the travelling direction is
* reversed with respect to the original geometry direction for bidirectional road segments. To fix this an internal
* attribute of the edge iterator state is used indicating it has done so or not. Since this attribute is package
* private the same package structure is used.
* The class is for handling specific VirtualEdgeIteratorState and VirtualEdgeIterator implementations
* The VirtualEdgeIterator is package private so this class has to be in com.graphhopper.routing.querygraph
* to access it.
* private the same package structure is used. The class is for handling specific VirtualEdgeIteratorState and
* VirtualEdgeIterator implementations The VirtualEdgeIterator is package private so this class has to be in
* com.graphhopper.routing.querygraph to access it.
*
* @see <a href="https://discuss.graphhopper.com/t/edge-direction-problem/6530">Edge direction problem</a>
* @see <a href="https://discuss.graphhopper.com/t/understanding-edge-directions/1414">Understanding edge directions</a>
*/
public final class VirtualEdgeIteratorStateReverseExtractor {
private VirtualEdgeIteratorStateReverseExtractor(){

private VirtualEdgeIteratorStateReverseExtractor() {
//static class
}

@SneakyThrows
public static boolean hasReversed(EdgeIteratorState closestEdge) {
if (closestEdge instanceof VirtualEdgeIteratorState virtualEdgeIteratorState) {
return extractReversedFromVirtualEdge(virtualEdgeIteratorState);
} else if (closestEdge instanceof VirtualEdgeIterator virtualEdgeIterator) {
return extractReversedFromVirtualEdge(extractEdgeIteratorStateFromVirtualEdgeIterator(virtualEdgeIterator));
}else {
} else {
throw new IllegalArgumentException(
"This method can only be called with an EdgeIterable,VirtualEdgeIteratorState "
+ "or VirtualEdgeIterator"
Expand All @@ -38,18 +42,30 @@ public static boolean hasReversed(EdgeIteratorState closestEdge) {
}
}

private static boolean extractReversedFromVirtualEdge(VirtualEdgeIteratorState closestEdge)
private static boolean extractReversedFromVirtualEdge(EdgeIteratorState closestEdge)
throws NoSuchFieldException, IllegalAccessException {
Field field = closestEdge.getClass().getDeclaredField("reverse"); //NoSuchFieldException
field.setAccessible(true);
return (boolean) field.get(closestEdge);
}

/**
* The virtual-edge iterator is a wrapper class around a list of edgeIterator states and can contain both
* IteratorStateImpl and VirtualEdgeIteratorState in its list.
*
* @param virtualEdgeIterator
* @return EdgeIteratorState
*/
@SneakyThrows
private static VirtualEdgeIteratorState extractEdgeIteratorStateFromVirtualEdgeIterator(
private static EdgeIteratorState extractEdgeIteratorStateFromVirtualEdgeIterator(
VirtualEdgeIterator virtualEdgeIterator) {
Method method = virtualEdgeIterator.getClass().getDeclaredMethod("getCurrentEdge");
method.setAccessible(true);
return (VirtualEdgeIteratorState) method.invoke(virtualEdgeIterator);
EdgeIteratorState edgeIteratorState = (EdgeIteratorState) method.invoke(virtualEdgeIterator);
if (edgeIteratorState instanceof VirtualEdgeIteratorState virtualEdgeIteratorState) {
return virtualEdgeIteratorState;
} else {
return getEdgeIteratorStateImpl(edgeIteratorState);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,17 @@ public boolean hasReversed(EdgeIteratorState closestEdge) {
return edgeIterable.reverse;
} else {
return VirtualEdgeIteratorStateReverseExtractor.hasReversed(closestEdge);
}
}

public static EdgeIteratorState getEdgeIteratorStateImpl(EdgeIteratorState closestEdge) {

if (closestEdge instanceof EdgeIteratorStateImpl edgeIterable) {
return edgeIterable;
} else {
throw new IllegalArgumentException(
"This method can only be called with an EdgeIteratorStateImpl instance of EdgeIteratorState %s".formatted(
closestEdge));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import static java.util.Comparator.comparing;
import static nu.ndw.nls.routingmapmatcher.network.model.Link.WAY_ID_KEY;
import static nu.ndw.nls.routingmapmatcher.util.PathUtil.determineEdgeDirection;

import com.graphhopper.config.Profile;
import com.graphhopper.routing.ev.DecimalEncodedValue;
Expand All @@ -11,7 +10,6 @@
import com.graphhopper.routing.util.EncodingManager;
import com.graphhopper.routing.util.TraversalMode;
import com.graphhopper.storage.BaseGraph;
import com.graphhopper.storage.EdgeIteratorStateReverseExtractor;
import com.graphhopper.storage.index.LocationIndexTree;
import com.graphhopper.storage.index.Snap;
import com.graphhopper.util.EdgeIteratorState;
Expand All @@ -22,7 +20,6 @@
import nu.ndw.nls.routingmapmatcher.isochrone.algorithm.IsochroneByTimeDistanceAndWeight;
import nu.ndw.nls.routingmapmatcher.isochrone.algorithm.ShortestPathTreeFactory;
import nu.ndw.nls.routingmapmatcher.isochrone.mappers.IsochroneMatchMapper;
import nu.ndw.nls.routingmapmatcher.model.EdgeIteratorTravelDirection;
import nu.ndw.nls.routingmapmatcher.model.IsochroneMatch;
import nu.ndw.nls.routingmapmatcher.model.IsochroneUnit;
import nu.ndw.nls.routingmapmatcher.model.base.BaseLocation;
Expand All @@ -37,7 +34,6 @@ public class IsochroneService {
private static final int MILLISECONDS = 1000;
private final EncodingManager encodingManager;
private final BaseGraph baseGraph;
private final EdgeIteratorStateReverseExtractor edgeIteratorStateReverseExtractor;
private final IsochroneMatchMapper isochroneMatchMapper;
private final ShortestPathTreeFactory shortestPathTreeFactory;
private final LocationIndexTree locationIndexTree;
Expand Down Expand Up @@ -86,24 +82,24 @@ private List<IsochroneMatch> getIsochroneMatches(Point startPoint, int matchedLi
// It also sets the closestNode of the matchedQueryResult to the virtual node ID. In this way, it creates a
// start point for isochrone calculation based on the snapped point coordinates.
QueryGraph queryGraph = QueryGraph.create(baseGraph, startSegment);
boolean searchDirectionIsReversed = reversed != reverseFlow;
IsochroneByTimeDistanceAndWeight isochrone = shortestPathTreeFactory
.createShortestPathTreeByTimeDistanceAndWeight(null, queryGraph, TraversalMode.EDGE_BASED,
isochroneValue, isochroneUnit, searchDirectionIsReversed);
isochroneValue, isochroneUnit, reverseFlow, reversed, matchedLinkId);
// Here the closestNode is the virtual node ID created by the queryGraph.lookup.
List<IsoLabel> isoLabels = new ArrayList<>();
isochrone.search(startSegment.getClosestNode(), isoLabels::add);
boolean searchDirectionReversed = reversed != reverseFlow;
EdgeIteratorState startEdge = startSegment.getClosestEdge();
return isoLabels.stream()
.filter(isoLabel -> ! isoLabel.isRoot())
.filter(isoLabel -> !isoLabel.isRoot())
.sorted(comparing(IsoLabel::getDistance))
.map(isoLabel -> {
// Specify the maximum distance on which to crop the geometries.
// In the case of seconds, convert to meters based on the average speed of the iso label.
double maxDistance = IsochroneUnit.METERS == isochroneUnit ? isochroneValue
: calculateMaxDistance(queryGraph, isochroneValue, isoLabel, searchDirectionReversed);
return isochroneMatchMapper.mapToIsochroneMatch(isoLabel, maxDistance, queryGraph, startEdge);
return isochroneMatchMapper.mapToIsochroneMatch(isoLabel, maxDistance, queryGraph, startEdge,
reverseFlow);
})
.toList();
}
Expand Down Expand Up @@ -135,7 +131,7 @@ private double calculateMaxDistance(QueryGraph queryGraph, double maximumTimeInS

private double getAverageSpeedFromEdge(EdgeIteratorState currentEdge, boolean useSpeedFromReversedDirection) {
DecimalEncodedValue vehicleSpeedDecimalEncodedValue = encodingManager.getDecimalEncodedValue(
VehicleSpeed.key(profile.getVehicle()));
VehicleSpeed.key(profile.getName()));
if (useSpeedFromReversedDirection) {
return currentEdge.getReverse(vehicleSpeedDecimalEncodedValue);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.graphhopper.routing.querygraph.QueryGraph;
import com.graphhopper.routing.querygraph.QueryGraphWeightingAdapter;
import com.graphhopper.routing.util.EncodingManager;
import com.graphhopper.routing.util.TraversalMode;
import com.graphhopper.routing.weighting.Weighting;
import com.graphhopper.storage.EdgeIteratorStateReverseExtractor;
Expand All @@ -15,21 +16,16 @@ public class ShortestPathTreeFactory {

private static final int MILLISECONDS = 1000;
private final Weighting defaultWeighting;
private final EncodingManager encodingManager;

public IsochroneByTimeDistanceAndWeight createShortestPathTreeByTimeDistanceAndWeight(Weighting weighting,
QueryGraph queryGraph,
TraversalMode traversalMode,
double isochroneValue,
IsochroneUnit isochroneUnit,
boolean isSearchDirectionReversed
) {

QueryGraph queryGraph, TraversalMode traversalMode, double isochroneValue, IsochroneUnit isochroneUnit,
boolean reverseFlow, boolean reversed, int matchedLinkId) {
Weighting baseWeighting = weighting == null ? this.defaultWeighting : weighting;
Weighting queryGraphWeighting = QueryGraphWeightingAdapter.fromQueryGraph(baseWeighting, queryGraph,
new EdgeIteratorStateReverseExtractor(),isSearchDirectionReversed);
new EdgeIteratorStateReverseExtractor(), reverseFlow != reversed, matchedLinkId, encodingManager);
IsochroneByTimeDistanceAndWeight isochrone = new IsochroneByTimeDistanceAndWeight(queryGraph,
queryGraphWeighting, isSearchDirectionReversed,
traversalMode);
queryGraphWeighting, reverseFlow, traversalMode);
if (isochroneUnit == IsochroneUnit.METERS) {
isochrone.setDistanceLimit(isochroneValue);
} else if (isochroneUnit == IsochroneUnit.SECONDS) {
Expand All @@ -39,5 +35,4 @@ public IsochroneByTimeDistanceAndWeight createShortestPathTreeByTimeDistanceAndW
}
return isochrone;
}

}
Loading

0 comments on commit ab2b246

Please sign in to comment.