All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.graphhopper.routing.querygraph.QueryGraph Maven / Gradle / Ivy

Go to download

GraphHopper is a fast and memory efficient Java road routing engine working seamlessly with OpenStreetMap data.

There is a newer version: 10.0
Show newest version
/*
 *  Licensed to GraphHopper GmbH under one or more contributor
 *  license agreements. See the NOTICE file distributed with this work for
 *  additional information regarding copyright ownership.
 *
 *  GraphHopper GmbH licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except in
 *  compliance with the License. You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.graphhopper.routing.querygraph;

import com.carrotsearch.hppc.IntArrayList;
import com.carrotsearch.hppc.IntObjectMap;
import com.carrotsearch.hppc.cursors.IntCursor;
import com.carrotsearch.hppc.procedures.IntObjectProcedure;
import com.graphhopper.coll.GHIntObjectHashMap;
import com.graphhopper.routing.util.AllEdgesIterator;
import com.graphhopper.routing.util.EdgeFilter;
import com.graphhopper.routing.weighting.QueryGraphWeighting;
import com.graphhopper.routing.weighting.Weighting;
import com.graphhopper.storage.*;
import com.graphhopper.storage.index.Snap;
import com.graphhopper.util.EdgeExplorer;
import com.graphhopper.util.EdgeIterator;
import com.graphhopper.util.EdgeIteratorState;
import com.graphhopper.util.GHUtility;
import com.graphhopper.util.shapes.BBox;

import java.util.*;

/**
 * A class which is used to query the underlying graph with real GPS points. It does so by
 * introducing virtual nodes and edges. It is lightweight in order to be created every time a new
 * query comes in, which makes the behaviour thread safe.
 * 

* Calling any create method creates virtual edges between the tower nodes of the existing * graph and new virtual tower nodes. Every virtual node has two adjacent nodes and is connected * to each adjacent nodes via 2 virtual edges with opposite base node / adjacent node encoding. * However, the edge explorer returned by {@link #createEdgeExplorer()} only returns two * virtual edges per virtual node (the ones with correct base node). * * @author Peter Karich */ public class QueryGraph implements Graph { static final int BASE_SNAP = 0, SNAP_BASE = 1, SNAP_ADJ = 2, ADJ_SNAP = 3; private final BaseGraph baseGraph; private final int baseNodes; private final int baseEdges; private final TurnCostStorage turnCostStorage; private final NodeAccess nodeAccess; private final QueryOverlay queryOverlay; // Use LinkedHashSet for predictable iteration order. private final Set unfavoredEdges = new LinkedHashSet<>(5); private final IntObjectMap> virtualEdgesAtRealNodes; private final List> virtualEdgesAtVirtualNodes; public static QueryGraph create(BaseGraph graph, Snap snap) { return QueryGraph.create(graph, Collections.singletonList(snap)); } public static QueryGraph create(BaseGraph graph, Snap fromSnap, Snap toSnap) { return QueryGraph.create(graph.getBaseGraph(), Arrays.asList(fromSnap, toSnap)); } public static QueryGraph create(BaseGraph graph, List snaps) { return new QueryGraph(graph, snaps); } private QueryGraph(BaseGraph graph, List snaps) { baseGraph = graph; baseNodes = graph.getNodes(); baseEdges = graph.getEdges(); queryOverlay = QueryOverlayBuilder.build(graph, snaps); nodeAccess = new ExtendedNodeAccess(graph.getNodeAccess(), queryOverlay.getVirtualNodes(), baseNodes); turnCostStorage = baseGraph.getTurnCostStorage(); // build data structures holding the virtual edges at all real/virtual nodes that are modified compared to the // mainGraph. final EdgeExplorer mainExplorer = baseGraph.createEdgeExplorer(); virtualEdgesAtRealNodes = buildVirtualEdgesAtRealNodes(mainExplorer); virtualEdgesAtVirtualNodes = buildVirtualEdgesAtVirtualNodes(); } public QueryOverlay getQueryOverlay() { return queryOverlay; } @Override public BaseGraph getBaseGraph() { return baseGraph; } public boolean isVirtualEdge(int edgeId) { return edgeId >= baseEdges; } public boolean isVirtualNode(int nodeId) { return nodeId >= baseNodes; } public void unfavorVirtualEdges(IntArrayList edgeIds) { for (IntCursor c : edgeIds) { unfavorVirtualEdge(c.value); } } /** * Assigns the 'unfavored' flag to a virtual edge (for both directions) */ public void unfavorVirtualEdge(int virtualEdgeId) { if (!isVirtualEdge(virtualEdgeId)) return; VirtualEdgeIteratorState edge = getVirtualEdge(getInternalVirtualEdgeId(virtualEdgeId)); edge.setUnfavored(true); unfavoredEdges.add(edge); // we have to set the unfavored flag also for the virtual edge state that is used when we discover the same edge // from the adjacent node. note that the unfavored flag will be set for both 'directions' of the same edge state. VirtualEdgeIteratorState reverseEdge = getVirtualEdge(getPosOfReverseEdge(getInternalVirtualEdgeId(virtualEdgeId))); reverseEdge.setUnfavored(true); unfavoredEdges.add(reverseEdge); } /** * Returns all virtual edges that have been unfavored via * {@link #unfavorVirtualEdge(int)} or {@link #unfavorVirtualEdges(IntArrayList)} */ public Set getUnfavoredVirtualEdges() { // Need to create a new set to convert Set to // Set. return new LinkedHashSet<>(unfavoredEdges); } /** * Removes the 'unfavored' status of all virtual edges. */ public void clearUnfavoredStatus() { for (VirtualEdgeIteratorState edge : unfavoredEdges) { edge.setUnfavored(false); } unfavoredEdges.clear(); } @Override public int getNodes() { return queryOverlay.getVirtualNodes().size() + baseNodes; } @Override public int getEdges() { return queryOverlay.getNumVirtualEdges() / 2 + baseEdges; } @Override public NodeAccess getNodeAccess() { return nodeAccess; } @Override public BBox getBounds() { return baseGraph.getBounds(); } @Override public EdgeIteratorState getEdgeIteratorState(int origEdgeId, int adjNode) { if (!isVirtualEdge(origEdgeId)) return baseGraph.getEdgeIteratorState(origEdgeId, adjNode); int edgeId = getInternalVirtualEdgeId(origEdgeId); EdgeIteratorState eis = getVirtualEdge(edgeId); if (eis.getAdjNode() == adjNode || adjNode == Integer.MIN_VALUE) return eis; edgeId = getPosOfReverseEdge(edgeId); EdgeIteratorState eis2 = getVirtualEdge(edgeId); if (eis2.getAdjNode() == adjNode) return eis2; throw new IllegalStateException("Edge " + origEdgeId + " not found with adjNode:" + adjNode + ". found edges were:" + eis + ", " + eis2); } @Override public EdgeIteratorState getEdgeIteratorStateForKey(int edgeKey) { int edge = GHUtility.getEdgeFromEdgeKey(edgeKey); if (!isVirtualEdge(edge)) return baseGraph.getEdgeIteratorStateForKey(edgeKey); return getVirtualEdge(edgeKey - 2 * baseEdges); } private VirtualEdgeIteratorState getVirtualEdge(int edgeId) { return queryOverlay.getVirtualEdge(edgeId); } static int getPosOfReverseEdge(int edgeId) { // find reverse edge via convention. see virtualEdges comment above return edgeId % 2 == 0 ? edgeId + 1 : edgeId - 1; } private int getInternalVirtualEdgeId(int origEdgeId) { return 2 * (origEdgeId - baseEdges); } @Override public EdgeExplorer createEdgeExplorer(final EdgeFilter edgeFilter) { // re-use these objects between setBaseNode calls to prevent GC final EdgeExplorer mainExplorer = baseGraph.createEdgeExplorer(edgeFilter); final VirtualEdgeIterator virtualEdgeIterator = new VirtualEdgeIterator(edgeFilter, null); return new EdgeExplorer() { @Override public EdgeIterator setBaseNode(int baseNode) { if (isVirtualNode(baseNode)) { List virtualEdges = virtualEdgesAtVirtualNodes.get(baseNode - baseNodes); return virtualEdgeIterator.reset(virtualEdges); } else { List virtualEdges = virtualEdgesAtRealNodes.get(baseNode); if (virtualEdges == null) { return mainExplorer.setBaseNode(baseNode); } else { return virtualEdgeIterator.reset(virtualEdges); } } } }; } private IntObjectMap> buildVirtualEdgesAtRealNodes(final EdgeExplorer mainExplorer) { final IntObjectMap> virtualEdgesAtRealNodes = new GHIntObjectHashMap<>(queryOverlay.getEdgeChangesAtRealNodes().size()); queryOverlay.getEdgeChangesAtRealNodes().forEach(new IntObjectProcedure() { @Override public void apply(int node, QueryOverlay.EdgeChanges edgeChanges) { List virtualEdges = new ArrayList<>(edgeChanges.getAdditionalEdges()); EdgeIterator mainIter = mainExplorer.setBaseNode(node); while (mainIter.next()) { if (!edgeChanges.getRemovedEdges().contains(mainIter.getEdge())) { virtualEdges.add(mainIter.detach(false)); } } virtualEdgesAtRealNodes.put(node, virtualEdges); } }); return virtualEdgesAtRealNodes; } private List> buildVirtualEdgesAtVirtualNodes() { final List> virtualEdgesAtVirtualNodes = new ArrayList<>(); for (int i = 0; i < queryOverlay.getVirtualNodes().size(); i++) { List virtualEdges = Arrays.asList( queryOverlay.getVirtualEdge(i * 4 + SNAP_BASE), queryOverlay.getVirtualEdge(i * 4 + SNAP_ADJ) ); virtualEdgesAtVirtualNodes.add(virtualEdges); } return virtualEdgesAtVirtualNodes; } @Override public AllEdgesIterator getAllEdges() { throw new UnsupportedOperationException("Not supported yet."); } @Override public EdgeIteratorState edge(int a, int b) { throw exc(); } @Override public TurnCostStorage getTurnCostStorage() { return turnCostStorage; } @Override public Weighting wrapWeighting(Weighting weighting) { return new QueryGraphWeighting(weighting, baseGraph.getNodes(), baseGraph.getEdges(), queryOverlay.getClosestEdges()); } @Override public int getOtherNode(int edge, int node) { if (isVirtualEdge(edge)) { return getEdgeIteratorState(edge, node).getBaseNode(); } return baseGraph.getOtherNode(edge, node); } @Override public boolean isAdjacentToNode(int edge, int node) { if (isVirtualEdge(edge)) { EdgeIteratorState virtualEdge = getEdgeIteratorState(edge, node); return virtualEdge.getBaseNode() == node || virtualEdge.getAdjNode() == node; } return baseGraph.isAdjacentToNode(edge, node); } List getVirtualEdges() { return queryOverlay.getVirtualEdges(); } private UnsupportedOperationException exc() { return new UnsupportedOperationException("QueryGraph cannot be modified."); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy