Lat/lon failing to snap to the nearest point on graph

Kia ora!

When calculating a route, I have the issue of my source and target (unique coordinate pairs) snapping to the same point, which is not the nearest point to one or both of the source or target.

For example:

Source:	-41.16895294189453	175.00115966796875
Target:	-41.165096282958984	174.99754333496094
	Snapped point (for each source and target):
		-41.17054882281755	174.99392423765192
		-41.17054882281755	174.99392423765192

i.e. while the source and target coordinates are unique, the snapped point found for each is the same, and thus a) a matrix request returns 0, and b) a route request fails to find a path.

If the nearest point to both the source and target is a shared point on the graph (e.g. the end of the nearest road), I would expect this behaviour. However, in the given example, visualised below, the source (red) should snap to the point GraphHopper has calculated (green), while the target (blue) should snap to its nearest edge (to its North), i.e. not the green point.

Screenshot 2023-02-23 at 8.29.37 AM

Could this be a rounding error on the source/target coordinates? In this example, if the library rounds each coordinate to 2dp when identifying a snapped point, the source and target coordinates would be viewed as equivalent, for example.

Else, have I made a flawed assumption at some stage? I’ve included my code (from RoutingProfile.computeRoute(...)) at the end of this post.

Two side questions:

  • A few of the snapped points land upon a walking trail, not upon a road, despite using APIEnums.Profile.DRIVING_CAR. Is there an additional parameter I need to set to ensure we always snap to a road?
  • Is there a way for me to replicate the above visualisation in Java, to confirm exactly which edge the .pbf file believes is closest?
// the existing code
if (directedSegment) {
    resp = mGraphHopper.constructFreeHandRoute(req);
} else {
    mGraphHopper.setSimplifyResponse(geometrySimplify);
    resp = mGraphHopper.route(req);

    // start debugging code
    try {
        double[] radii = radiuses.clone();
        List<GHPoint> points = req.getPoints();
        GHResponse ghRsp = new GHResponse();
        Object routingTemplate = new ViaRoutingTemplate(req, ghRsp, mGraphHopper.getLocationIndex(), mGraphHopper.getEncodingManager());
        String vehicle = req.getVehicle();
        FlagEncoder encoder = mGraphHopper.getEncodingManager().getEncoder(vehicle);
        EdgeFilter edgeFilter = mGraphHopper.getEdgeFilterFactory().createEdgeFilter(req.getAdditionalHints(), encoder, mGraphHopper.getGraphHopperStorage());
        ((RoutingTemplate)routingTemplate).setEdgeFilter(edgeFilter);

        List<QueryResult> qResults = ((RoutingTemplate) routingTemplate).lookup(points, encoder);
        if (points.size() == qResults.size()) {
            for (int placeIndex = 0; placeIndex < points.size(); placeIndex++) {
                QueryResult qr = qResults.get(placeIndex);
                GHPoint3D gh3d = qr.getSnappedPoint();
                if (radii != null && qr.isValid() && qr.getQueryDistance() > radii[placeIndex] && radii[placeIndex] != -1.0) {
                    ghRsp.addError(new PointNotFoundException("Cannot find point " + placeIndex + ": " + points.get(placeIndex) + " within a radius of " + radii[placeIndex] + " meters.", placeIndex));
                } else {
                    // print the snapped point to see if the coords for the snapped sources and targets are unique (which they may not be in all cases).
                    // note: this will always occur with a sufficiently large search radius.
                        System.out.println("\t\t" + gh3d.lat + "\t" + gh3d.lon + "\t3" + "\t\t" + gh3d.ele);
                }
            }
        }
    } catch (Exception e) {
        // do nothing
    }
    // end debug code
    // continue existing code
    if (DebugUtility.isDebug() && !directedSegment) {
        LOGGER.info("visited_nodes.average - " + resp.getHints().get("visited_nodes.average", ""));
    }
    if (DebugUtility.isDebug() && directedSegment) {
        LOGGER.info("skipped segment - " + resp.getHints().get("skipped_segment", ""));
    }
    endUseGH();
}

Cheers,
Mak.

Hi @makalakalak,

the problem in this case is the access=private tag on the top road which excludes it from route generation:

there are some of topics on the private access already.

Best regards

Hi Amandus,

Thanks for the response. This makes logical sense. I have constructed a FlagEncoder, with the constructor below (in all honesty, I’ve edited the CarFlagEncoder, saving a duplicate). My goal is to use this FlagEncoder as my default, allowing vehicles to traverse on any path a vehicle could reasonably traverse, irrespective of regional law (property rights, military restrictions, passing all barriers, etc).

super(speedBits, speedFactor, maxTurnCosts);

intendedValues.add("unclassified");
intendedValues.add("private");
intendedValues.add("agricultural");
intendedValues.add("forestry");
intendedValues.add("restricted");
intendedValues.add("delivery");
intendedValues.add("no");
intendedValues.add("service");

setBlockByDefault(false);
potentialBarriers.add("bollard");
potentialBarriers.add("stile");
potentialBarriers.add("turnstile");
potentialBarriers.add("cycle_barrier");
potentialBarriers.add("motorcycle_barrier");
potentialBarriers.add("block");

initSpeedLimitHandler(this.toString());

// clear the default values of the VehicleFlagEncoder
restrictedValues.clear();
absoluteBarriers.clear();

init();

Yet, with the above setup, I still have the same error exhibited in my previous post: snapping fails to identify private ways. Below is a second example, where the nearest way to the red source is unclassified (I estimate, by OpenStreetMap) and the nearest way to the blue target is residential, yet they both snap to the green point at the end of another unclassified way.

Source: -40.706478118896484, 175.16148376464844  
Target: -40.70241165161133, 175.13275146484375
Snapped point: -40.71446003126636, 175.15114564786433