Skip to content

Commit

Permalink
Make CHGraphs/Profiles selectable by name
Browse files Browse the repository at this point in the history
* CHProfile now has a profileName field, which by default is the same as the file name we used before. However, its also possible to set a non-default custom name.

* Getting a CHGraph from GraphHopperStorage is now possible by providing only the profile name (no longer need a CHProfile object with a Weighting and everything)

* This way its now also possible to have multiple CHGraphs with the 'same' weighting (but different profile names)
  • Loading branch information
easbar committed Feb 8, 2020
1 parent 75a8a61 commit 0ee5582
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ protected AbstractWeighting(FlagEncoder encoder, TurnCostProvider turnCostProvid
*/
public abstract double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse);


@Override
public long calcEdgeMillis(EdgeIteratorState edgeState, boolean reverse) {
// special case for loop edges: since they do not have a meaningful direction we always need to read them in
Expand Down
1 change: 0 additions & 1 deletion core/src/main/java/com/graphhopper/storage/CHGraph.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ public interface CHGraph extends Graph {
@Override
AllCHEdgesIterator getAllEdges();


void disconnectEdge(int edge, int adjNode, int prevEdge);

/**
Expand Down
62 changes: 40 additions & 22 deletions core/src/main/java/com/graphhopper/storage/CHProfile.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import com.graphhopper.routing.weighting.AbstractWeighting;
import com.graphhopper.routing.weighting.Weighting;

import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand All @@ -15,22 +14,35 @@
* @author easbar
*/
public class CHProfile {
private final String profileName;
private final Weighting weighting;
private final boolean edgeBased;

public static CHProfile nodeBased(Weighting weighting) {
return new CHProfile(weighting, TraversalMode.NODE_BASED);
return nodeBased(defaultName(weighting, false), weighting);
}

public static CHProfile nodeBased(String profileName, Weighting weighting) {
return new CHProfile(profileName, weighting, false);
}

public static CHProfile edgeBased(Weighting weighting) {
return new CHProfile(weighting, TraversalMode.EDGE_BASED);
return edgeBased(defaultName(weighting, true), weighting);
}

public CHProfile(Weighting weighting, TraversalMode traversalMode) {
this(weighting, traversalMode.isEdgeBased());
public static CHProfile edgeBased(String profileName, Weighting weighting) {
return new CHProfile(profileName, weighting, true);
}

public CHProfile(Weighting weighting, boolean edgeBased) {
this(defaultName(weighting, edgeBased), weighting, edgeBased);
}

public CHProfile(String profileName, Weighting weighting, boolean edgeBased) {
if (!profileName.matches("^[a-z0-9_\\-]*$")) {
throw new IllegalArgumentException("CH profile names may only contain lower case letters, numbers, underscores and dashs, given: " + profileName);
}
this.profileName = profileName;
this.weighting = weighting;
this.edgeBased = edgeBased;
}
Expand All @@ -48,20 +60,7 @@ public TraversalMode getTraversalMode() {
}

public String toFileName() {
String result = AbstractWeighting.weightingToFileName(weighting);
// keeping legacy file names for now, like fastest_edge_utc40 (instead of fastest_40_edge), because we will
// most likely use profile names soon: #1708
Pattern pattern = Pattern.compile("-?\\d+");
Matcher matcher = pattern.matcher(result);
if (matcher.find()) {
String turnCostPostfix = matcher.group();
result = matcher.replaceAll("");
result += edgeBased ? "edge" : "node";
result += "_utc" + turnCostPostfix;
} else {
result += edgeBased ? "_edge" : "_node";
}
return result;
return profileName;
}

public String toString() {
Expand All @@ -78,17 +77,36 @@ public String toString() {
return result;
}

public String getName() {
return profileName;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CHProfile chProfile = (CHProfile) o;
return edgeBased == chProfile.edgeBased &&
Objects.equals(weighting, chProfile.weighting);
return getName().equals(chProfile.getName());
}

@Override
public int hashCode() {
return Objects.hash(weighting, edgeBased);
return getName().hashCode();
}

private static String defaultName(Weighting weighting, boolean edgeBased) {
String result = AbstractWeighting.weightingToFileName(weighting);
// this is how we traditionally named the files, something like 'fastest_edge_utc40'
Pattern pattern = Pattern.compile("-?\\d+");
Matcher matcher = pattern.matcher(result);
if (matcher.find()) {
String turnCostPostfix = matcher.group();
result = matcher.replaceAll("");
result += edgeBased ? "edge" : "node";
result += "_utc" + turnCostPostfix;
} else {
result += edgeBased ? "_edge" : "_node";
}
return result;
}
}
19 changes: 10 additions & 9 deletions core/src/main/java/com/graphhopper/storage/GraphHopperStorage.java
Original file line number Diff line number Diff line change
Expand Up @@ -117,24 +117,25 @@ public CHGraph getCHGraph() {
}
}

public CHGraph getCHGraph(CHProfile chProfile) {
return getCHGraph(chProfile.getName());
}

/**
* @return the {@link CHGraph} for the specified {@link CHProfile}
* @return the {@link CHGraph} for the specified profile name
*/
public CHGraph getCHGraph(CHProfile profile) {
public CHGraph getCHGraph(String profileName) {
if (chGraphs.isEmpty())
throw new IllegalStateException("There is no CHGraph");

if (profile == null)
throw new IllegalStateException("Cannot find CHGraph with null CHProfile");

List<CHProfile> existing = new ArrayList<>();
List<String> existing = new ArrayList<>();
for (CHGraphImpl cg : chGraphs) {
if (cg.getCHProfile().equals(profile))
if (cg.getCHProfile().getName().equals(profileName))
return cg;
existing.add(cg.getCHProfile());
existing.add(cg.getCHProfile().getName());
}

throw new IllegalStateException("Cannot find CHGraph for the specified profile: " + profile + ", existing:" + existing);
throw new IllegalStateException("Cannot find CHGraph for the specified profile: " + profileName + ", existing:" + existing);
}

public boolean isCHPossible() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1003,7 +1003,7 @@ private GraphHopperStorage createGHStorage() {
private GraphHopperStorage createGHStorage(boolean is3D, Weighting... weightings) {
CHProfile[] chProfiles = new CHProfile[weightings.length];
for (int i = 0; i < weightings.length; i++) {
chProfiles[i] = new CHProfile(weightings[i], traversalMode);
chProfiles[i] = new CHProfile(weightings[i], traversalMode.isEdgeBased());
}
return new GraphBuilder(encodingManager).set3D(is3D)
.setCHProfiles(chProfiles)
Expand Down Expand Up @@ -1156,7 +1156,7 @@ public String toString() {
private static abstract class CHCalculator implements PathCalculator {
@Override
public Path calcPath(GraphHopperStorage graph, Weighting weighting, TraversalMode traversalMode, int maxVisitedNodes, int from, int to) {
CHProfile chProfile = new CHProfile(weighting, traversalMode);
CHProfile chProfile = new CHProfile(weighting, traversalMode.isEdgeBased());
PrepareContractionHierarchies pch = PrepareContractionHierarchies.fromGraphHopperStorage(graph, chProfile);
CHGraph chGraph = graph.getCHGraph(chProfile);
if (chGraph.getEdges() == chGraph.getOriginalEdges()) {
Expand All @@ -1182,7 +1182,7 @@ public Path calcPath(GraphHopperStorage graph, Weighting weighting, TraversalMod

@Override
public Path calcPath(GraphHopperStorage graph, Weighting weighting, TraversalMode traversalMode, int maxVisitedNodes, QueryResult from, QueryResult to) {
CHProfile chProfile = new CHProfile(weighting, traversalMode);
CHProfile chProfile = new CHProfile(weighting, traversalMode.isEdgeBased());
PrepareContractionHierarchies pch = PrepareContractionHierarchies.fromGraphHopperStorage(graph, chProfile);
CHGraph chGraph = graph.getCHGraph(chProfile);
if (chGraph.getEdges() == chGraph.getOriginalEdges()) {
Expand Down
2 changes: 2 additions & 0 deletions core/src/test/java/com/graphhopper/storage/CHProfileTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ public void filename() {
EncodingManager.create(encoder);
TurnCostStorage tcs = new TurnCostStorage(null, null);
assertEquals("fastest_car_edge_utc30", CHProfile.edgeBased(new FastestWeighting(encoder, new DefaultTurnCostProvider(encoder, tcs, 30))).toFileName());
assertEquals("my_profile_name", CHProfile.edgeBased("my_profile_name", new FastestWeighting(encoder, new DefaultTurnCostProvider(encoder, tcs, 30))).toFileName());
assertEquals("shortest_car_edge_utc-1", CHProfile.edgeBased(new ShortestWeighting(encoder, new DefaultTurnCostProvider(encoder, tcs, INFINITE_U_TURN_COSTS))).toFileName());
assertEquals("shortest_car_edge", CHProfile.edgeBased(new ShortestWeighting(encoder, NO_TURN_COST_PROVIDER)).toFileName());
assertEquals("fastest_car_node", CHProfile.nodeBased(new FastestWeighting(encoder)).toFileName());
assertEquals("short_fastest_car_node", CHProfile.nodeBased(new ShortFastestWeighting(encoder, 0.1)).toFileName());
assertEquals("your_profile_name", CHProfile.nodeBased("your_profile_name", new ShortFastestWeighting(encoder, 0.1)).toFileName());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,43 @@ public void testLoadingWithLessWeightings_nodeAndEdge_works() {
mixedStorage.flush();
}

@Test
public void testCHProfilesWithDifferentNames() {
FastestWeighting weighting = new FastestWeighting(carEncoder);
// creating multiple profiles with the same name is an error
{
try {
new GraphBuilder(encodingManager)
.setCHProfiles(
CHProfile.nodeBased("a", weighting),
CHProfile.nodeBased("b", weighting),
CHProfile.nodeBased("a", weighting)
)
.create();
fail("creating mulitple profiles with the same name should be an error");
} catch (Exception e) {
assertTrue("unexpected error: " + e.getMessage(), e.getMessage().contains("a CHGraph already exists"));
}
}
// ... but using multiple profiles with different names is fine even when their properties/weighting are the same
{
GraphHopperStorage storage = new GraphBuilder(encodingManager)
.setCHProfiles(
CHProfile.nodeBased("a", weighting),
CHProfile.nodeBased("b", weighting),
CHProfile.nodeBased("c", weighting)
)
.create();
assertSame(storage.getCHGraph("a"), storage.getCHGraph("a"));
assertNotNull(storage.getCHGraph("a"));
assertNotNull(storage.getCHGraph("b"));
assertNotNull(storage.getCHGraph("c"));
assertNotSame(storage.getCHGraph("a"), storage.getCHGraph("b"));
assertNotSame(storage.getCHGraph("b"), storage.getCHGraph("c"));
assertNotSame(storage.getCHGraph("a"), storage.getCHGraph("c"));
}
}

private GraphHopperStorage createStorageWithWeightings(String... profileStrings) {
return new GraphBuilder(encodingManager)
.setCHProfileStrings(profileStrings)
Expand Down

0 comments on commit 0ee5582

Please sign in to comment.