class LocationIndexTree implements LocationIndex {

    @Override
    public QueryResult findClosest(final double queryLat, final double queryLon, final EdgeFilter edgeFilter) {
        if (isClosed())
            throw new IllegalStateException("You need to create a new LocationIndex instance as it is already closed");

        GHIntHashSet allCollectedEntryIds = new GHIntHashSet();
        final QueryResult closestMatch = new QueryResult(queryLat, queryLon);
        for (int iteration = 0; iteration < maxRegionSearch; iteration++) {
            GHIntHashSet storedNetworkEntryIds = new GHIntHashSet();
            boolean earlyFinish = findNetworkEntries(queryLat, queryLon, storedNetworkEntryIds, iteration);
            storedNetworkEntryIds.removeAll(allCollectedEntryIds);
            allCollectedEntryIds.addAll(storedNetworkEntryIds);

            // clone storedIds to avoid interference with forEach
            final GHBitSet checkBitset = new GHTBitSet(new GHIntHashSet(storedNetworkEntryIds));
            // find nodes from the network entries which are close to 'point'
            final EdgeExplorer explorer = graph.createEdgeExplorer();
            storedNetworkEntryIds.forEach(new IntPredicate() {
                @Override
                public boolean apply(int networkEntryNodeId) {
                    new XFirstSearchCheck(queryLat, queryLon, checkBitset, edgeFilter) {
                        @Override
                        protected double getQueryDistance() {
                            return closestMatch.getQueryDistance();
                        }

                        @Override
                        protected boolean check(int node, double normedDist, int wayIndex, EdgeIteratorState edge, QueryResult.Position pos) {
                            if (normedDist < closestMatch.getQueryDistance()) {
                                closestMatch.setQueryDistance(normedDist);
                                closestMatch.setClosestNode(node);
                                closestMatch.setClosestEdge(edge.detach(false));
                                closestMatch.setWayIndex(wayIndex);
                                closestMatch.setSnappedPosition(pos);
                                return true;
                            }
                            return false;
                        }
                    }.start(explorer, networkEntryNodeId);
                    return true;
                }
            });

            // do early finish only if something was found (#318)
            if (earlyFinish && closestMatch.isValid())
                break;
        }

        // denormalize distance and calculate snapping point only if closed match was found
        if (closestMatch.isValid()) {
            closestMatch.setQueryDistance(distCalc.calcDenormalizedDist(closestMatch.getQueryDistance()));
            closestMatch.calcSnappedPoint(distCalc);
        }

        return closestMatch;
    }

    public  LocationIndexTree(Graph g, Directory dir);

}