Skip to content

Commit 78d19a8

Browse files
boldtrnkarussell
authored andcommitted
new web API to change the graph permanently e.g. via GeoJSON and pre-request blocking areas, graphhopper#789
1 parent 8e9d571 commit 78d19a8

File tree

55 files changed

+1265
-355
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1265
-355
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,5 @@ local.properties
4141
.DS_Store
4242
/core/nbproject/
4343
/reader-overlay-data/nbproject/
44-
!malta-latest.osm.pbf
44+
!malta-latest.osm.pbf
45+
/reader-json/nbproject/

config-example.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ prepare.min_one_way_network_size=200
5757
# the given distance in meter. Default is set to 1000km.
5858
routing.non_ch.max_waypoint_distance = 1000000
5959

60+
# You can permanently block certain areas from routing
61+
# e.g. you can block certain areas with block_area=lat1,lon1,lat2,lon2
6062

6163
##### Web #####
6264

core/src/main/java/com/graphhopper/GHRequest.java

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public GHRequest(GHPoint startPlace, GHPoint endPlace) {
100100
/**
101101
* Set routing request
102102
* <p>
103-
* @param points List of stopover points in order: start, 1st stop, 2nd stop, ..., end
103+
* @param points List of stopover points in order: start, 1st stop, 2nd stop, ..., end
104104
* @param favoredHeadings List of favored headings for starting (start point) and arrival (via
105105
* and end points) Headings are north based azimuth (clockwise) in (0, 360) or NaN for equal
106106
*/
@@ -236,19 +236,6 @@ public GHRequest setVehicle(String vehicle) {
236236
return this;
237237
}
238238

239-
@Override
240-
public String toString() {
241-
String res = "";
242-
for (GHPoint point : points) {
243-
if (res.isEmpty()) {
244-
res = point.toString();
245-
} else {
246-
res += "; " + point.toString();
247-
}
248-
}
249-
return res + "(" + algo + ")";
250-
}
251-
252239
public HintsMap getHints() {
253240
return hints;
254241
}
@@ -262,11 +249,20 @@ public List<String> getPointHints() {
262249
return pointHints;
263250
}
264251

265-
public String getPointHint(int index) {
266-
return pointHints.get(index);
252+
public boolean hasPointHints() {
253+
return pointHints.size() == points.size();
267254
}
268255

269-
public boolean hasPointHints(){
270-
return pointHints.size() == points.size();
256+
@Override
257+
public String toString() {
258+
String res = "";
259+
for (GHPoint point : points) {
260+
if (res.isEmpty()) {
261+
res = point.toString();
262+
} else {
263+
res += "; " + point.toString();
264+
}
265+
}
266+
return res + "(" + algo + ")";
271267
}
272268
}

core/src/main/java/com/graphhopper/GraphHopper.java

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@
1717
*/
1818
package com.graphhopper;
1919

20+
import com.graphhopper.json.geo.JsonFeature;
2021
import com.graphhopper.reader.DataReader;
2122
import com.graphhopper.reader.dem.BridgeElevationInterpolator;
2223
import com.graphhopper.reader.dem.CGIARProvider;
2324
import com.graphhopper.reader.dem.ElevationProvider;
2425
import com.graphhopper.reader.dem.SRTMProvider;
2526
import com.graphhopper.reader.dem.TunnelElevationInterpolator;
27+
import com.graphhopper.storage.change.ChangeGraphHelper;
28+
import com.graphhopper.storage.change.ChangeGraphResponse;
2629
import com.graphhopper.routing.*;
2730
import com.graphhopper.routing.ch.CHAlgoFactoryDecorator;
2831
import com.graphhopper.routing.ch.PrepareContractionHierarchies;
@@ -54,6 +57,10 @@
5457

5558
import static com.graphhopper.util.Parameters.Algorithms.*;
5659

60+
import java.util.concurrent.locks.Lock;
61+
import java.util.concurrent.locks.ReadWriteLock;
62+
import java.util.concurrent.locks.ReentrantReadWriteLock;
63+
5764
/**
5865
* Easy to use access point to configure import and (offline) routing.
5966
*
@@ -87,6 +94,7 @@ public class GraphHopper implements GraphHopperAPI {
8794
private boolean simplifyResponse = true;
8895
private TraversalMode traversalMode = TraversalMode.NODE_BASED;
8996
private int maxVisitedNodes = Integer.MAX_VALUE;
97+
private String blockedRectangularAreas = "";
9098

9199
private int nonChMaxWaypointDistance = Integer.MAX_VALUE;
92100
// for index
@@ -103,6 +111,7 @@ public class GraphHopper implements GraphHopperAPI {
103111
private boolean calcPoints = true;
104112
private ElevationProvider eleProvider = ElevationProvider.NOOP;
105113
private FlagEncoderFactory flagEncoderFactory = FlagEncoderFactory.DEFAULT;
114+
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
106115

107116
public GraphHopper() {
108117
chFactoryDecorator.setEnabled(true);
@@ -642,6 +651,7 @@ public GraphHopper init(CmdArgs args) {
642651
maxVisitedNodes = args.getInt(Routing.INIT_MAX_VISITED_NODES, Integer.MAX_VALUE);
643652
maxRoundTripRetries = args.getInt(RoundTrip.INIT_MAX_RETRIES, maxRoundTripRetries);
644653
nonChMaxWaypointDistance = args.getInt(Parameters.NON_CH.MAX_NON_CH_POINT_DISTANCE, Integer.MAX_VALUE);
654+
blockedRectangularAreas = args.get(Routing.BLOCK_AREA, "");
645655

646656
return this;
647657
}
@@ -672,7 +682,7 @@ public GraphHopper importOrLoad() {
672682
*/
673683
private GraphHopper process(String graphHopperLocation) {
674684
setGraphHopperLocation(graphHopperLocation);
675-
Lock lock = null;
685+
GHLock lock = null;
676686
try {
677687
if (ghStorage.getDirectory().getDefaultType().isStoring()) {
678688
lockFactory.setLockDir(new File(graphHopperLocation));
@@ -788,7 +798,7 @@ public boolean load(String graphHopperFolder) {
788798
if (!new File(graphHopperFolder).exists())
789799
return false;
790800

791-
Lock lock = null;
801+
GHLock lock = null;
792802
try {
793803
// create locks only if writes are allowed, if they are not allowed a lock cannot be created
794804
// (e.g. on a read only filesystem locks would fail)
@@ -836,7 +846,7 @@ private void initCHAlgoFactoryDecorator() {
836846
if (!chFactoryDecorator.hasWeightings())
837847
for (FlagEncoder encoder : encodingManager.fetchEdgeEncoders()) {
838848
for (String chWeightingStr : chFactoryDecorator.getWeightingsAsStrings()) {
839-
Weighting weighting = createWeighting(new HintsMap(chWeightingStr), encoder);
849+
Weighting weighting = createWeighting(new HintsMap(chWeightingStr), encoder, null);
840850
chFactoryDecorator.addWeighting(weighting);
841851
}
842852
}
@@ -911,15 +921,30 @@ private boolean isPrepared() {
911921
* @param hintsMap all parameters influencing the weighting. E.g. parameters coming via
912922
* GHRequest.getHints or directly via "&amp;api.xy=" from the URL of the web UI
913923
* @param encoder the required vehicle
924+
* @param graph The Graph enables the Weighting for NodeAccess and more
914925
* @return the weighting to be used for route calculation
915926
* @see HintsMap
916927
*/
917-
public Weighting createWeighting(HintsMap hintsMap, FlagEncoder encoder) {
928+
public Weighting createWeighting(HintsMap hintsMap, FlagEncoder encoder, Graph graph) {
918929
String weighting = hintsMap.getWeighting().toLowerCase();
919930

920931
if (encoder.supports(GenericWeighting.class)) {
921932
DataFlagEncoder dataEncoder = (DataFlagEncoder) encoder;
922-
return new GenericWeighting(dataEncoder, dataEncoder.readStringMap(hintsMap));
933+
ConfigMap cMap = dataEncoder.readStringMap(hintsMap);
934+
935+
// add default blocked rectangular areas from config properties
936+
if (!this.blockedRectangularAreas.isEmpty()) {
937+
String val = this.blockedRectangularAreas;
938+
String blockedAreasFromRequest = hintsMap.get(Parameters.Routing.BLOCK_AREA, "");
939+
if (!blockedAreasFromRequest.isEmpty())
940+
val += ";" + blockedAreasFromRequest;
941+
hintsMap.put(Parameters.Routing.BLOCK_AREA, val);
942+
}
943+
944+
cMap = new GraphEdgeIdFinder(graph, locationIndex).parseStringHints(cMap, hintsMap, new DefaultEdgeFilter(encoder));
945+
GenericWeighting genericWeighting = new GenericWeighting(dataEncoder, cMap);
946+
genericWeighting.setGraph(graph);
947+
return genericWeighting;
923948
} else if ("shortest".equalsIgnoreCase(weighting)) {
924949
return new ShortestWeighting(encoder);
925950
} else if ("fastest".equalsIgnoreCase(weighting) || weighting.isEmpty()) {
@@ -969,6 +994,8 @@ public List<Path> calcPaths(GHRequest request, GHResponse ghRsp) {
969994
request.setVehicle(vehicle);
970995
}
971996

997+
Lock readLock = readWriteLock.readLock();
998+
readLock.lock();
972999
try {
9731000
if (!encodingManager.supports(vehicle))
9741001
throw new IllegalArgumentException("Vehicle " + vehicle + " unsupported. "
@@ -1010,7 +1037,7 @@ else if (ALT_ROUTE.equalsIgnoreCase(algoStr))
10101037

10111038
RoutingAlgorithmFactory tmpAlgoFactory = getAlgorithmFactory(hints);
10121039
Weighting weighting = null;
1013-
Graph routingGraph = ghStorage;
1040+
QueryGraph queryGraph;
10141041

10151042
boolean forceFlexibleMode = hints.getBool(CH.DISABLE, false);
10161043
if (!chFactoryDecorator.isDisablingAllowed() && forceFlexibleMode)
@@ -1025,20 +1052,20 @@ else if (!(tmpAlgoFactory instanceof PrepareContractionHierarchies))
10251052

10261053
tMode = getCHFactoryDecorator().getNodeBase();
10271054
weighting = ((PrepareContractionHierarchies) tmpAlgoFactory).getWeighting();
1028-
routingGraph = ghStorage.getGraph(CHGraph.class, weighting);
1029-
1055+
queryGraph = new QueryGraph(ghStorage.getGraph(CHGraph.class, weighting));
1056+
queryGraph.lookup(qResults);
10301057
} else {
10311058
checkNonChMaxWaypointDistance(points);
1032-
weighting = createWeighting(hints, encoder);
1059+
queryGraph = new QueryGraph(ghStorage);
1060+
queryGraph.lookup(qResults);
1061+
weighting = createWeighting(hints, encoder, queryGraph);
10331062
ghRsp.addDebugInfo("tmode:" + tMode.toString());
10341063
}
10351064

10361065
int maxVisitedNodesForRequest = hints.getInt(Routing.MAX_VISITED_NODES, maxVisitedNodes);
10371066
if (maxVisitedNodesForRequest > maxVisitedNodes)
10381067
throw new IllegalArgumentException("The max_visited_nodes parameter has to be below or equal to:" + maxVisitedNodes);
10391068

1040-
QueryGraph queryGraph = new QueryGraph(routingGraph);
1041-
queryGraph.lookup(qResults);
10421069
weighting = createTurnWeighting(queryGraph, weighting, tMode);
10431070

10441071
AlgorithmOptions algoOpts = AlgorithmOptions.start().
@@ -1068,6 +1095,29 @@ else if (!(tmpAlgoFactory instanceof PrepareContractionHierarchies))
10681095
} catch (IllegalArgumentException ex) {
10691096
ghRsp.addError(ex);
10701097
return Collections.emptyList();
1098+
} finally {
1099+
readLock.unlock();
1100+
}
1101+
}
1102+
1103+
/**
1104+
* This method applies the changes to the graph specified as feature collection. It does so by locking the routing
1105+
* to avoid concurrent changes which could result in incorrect routing (like when done while a Dijkstra search) or
1106+
* also while just reading one edge row (inconsistent edge properties).
1107+
*/
1108+
public ChangeGraphResponse changeGraph(Collection<JsonFeature> collection) {
1109+
// TODO allow calling this method if called before CH preparation
1110+
if (getCHFactoryDecorator().isEnabled())
1111+
throw new IllegalArgumentException("To use the changeGraph API you need to turn off CH");
1112+
1113+
Lock writeLock = readWriteLock.writeLock();
1114+
writeLock.lock();
1115+
try {
1116+
ChangeGraphHelper overlay = new ChangeGraphHelper(ghStorage, locationIndex);
1117+
long updateCount = overlay.applyChanges(encodingManager, collection);
1118+
return new ChangeGraphResponse(updateCount);
1119+
} finally {
1120+
writeLock.unlock();
10711121
}
10721122
}
10731123

core/src/main/java/com/graphhopper/coll/GHBitSetImpl.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
*/
2525
public class GHBitSetImpl extends BitSet implements GHBitSet {
2626
public GHBitSetImpl() {
27+
super();
2728
}
2829

2930
public GHBitSetImpl(int nbits) {

reader-overlay-data/src/main/java/com/graphhopper/json/GHson.java renamed to core/src/main/java/com/graphhopper/json/GHJson.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
*
2626
* @author Peter Karich
2727
*/
28-
public interface GHson {
28+
public interface GHJson {
2929
/**
3030
* This method reads JSON data from the provided source and creates an instance of the provided
3131
* class.

reader-overlay-data/src/main/java/com/graphhopper/json/geo/Geometry.java renamed to core/src/main/java/com/graphhopper/json/geo/Geometry.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import com.graphhopper.util.shapes.GHPoint;
2222

2323
/**
24+
* This interface is used to define an area or location being used to block or change areas in the graph.
25+
*
2426
* @author Peter Karich
2527
*/
2628
public interface Geometry {

reader-overlay-data/src/main/java/com/graphhopper/json/geo/JsonFeature.java renamed to core/src/main/java/com/graphhopper/json/geo/JsonFeature.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,24 @@
2222
import java.util.Map;
2323

2424
/**
25+
* This class defines a properties where a geometry is associated. Typically read from GeoJSON but also from in-memory is possible.
26+
*
2527
* @author Peter Karich
2628
*/
2729
public class JsonFeature {
28-
String id;
29-
String type;
30-
BBox bbox;
31-
Geometry geometry;
32-
Map<String, Object> properties;
30+
final String id;
31+
final String type;
32+
final BBox bbox;
33+
final Geometry geometry;
34+
final Map<String, Object> properties;
35+
36+
public JsonFeature(String id, String type, BBox bbox, Geometry geometry, Map<String, Object> properties) {
37+
this.id = id;
38+
this.type = type;
39+
this.bbox = bbox;
40+
this.geometry = geometry;
41+
this.properties = properties;
42+
}
3343

3444
public String getId() {
3545
return id;

core/src/main/java/com/graphhopper/routing/AStar.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import com.graphhopper.util.DistancePlaneProjection;
2929
import com.graphhopper.util.EdgeExplorer;
3030
import com.graphhopper.util.EdgeIterator;
31+
import com.graphhopper.util.Helper;
3132
import com.graphhopper.util.Parameters;
3233

3334
import java.util.PriorityQueue;
@@ -54,7 +55,7 @@ public AStar(Graph graph, Weighting weighting, TraversalMode tMode) {
5455
int size = Math.min(Math.max(200, graph.getNodes() / 10), 2000);
5556
initCollections(size);
5657
BeelineWeightApproximator defaultApprox = new BeelineWeightApproximator(nodeAccess, weighting);
57-
defaultApprox.setDistanceCalc(new DistancePlaneProjection());
58+
defaultApprox.setDistanceCalc(Helper.DIST_PLANE);
5859
setApproximation(defaultApprox);
5960
}
6061

core/src/main/java/com/graphhopper/routing/AStarBidirection.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public AStarBidirection(Graph graph, Weighting weighting, TraversalMode tMode) {
7373
int size = Math.min(Math.max(200, graph.getNodes() / 10), 150_000);
7474
initCollections(size);
7575
BeelineWeightApproximator defaultApprox = new BeelineWeightApproximator(nodeAccess, weighting);
76-
defaultApprox.setDistanceCalc(new DistancePlaneProjection());
76+
defaultApprox.setDistanceCalc(Helper.DIST_PLANE);
7777
setApproximation(defaultApprox);
7878
}
7979

core/src/main/java/com/graphhopper/routing/ch/CHAlgoFactoryDecorator.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.graphhopper.routing.util.HintsMap;
2323
import com.graphhopper.routing.util.TraversalMode;
2424
import com.graphhopper.routing.weighting.AbstractWeighting;
25+
import com.graphhopper.routing.weighting.GenericWeighting;
2526
import com.graphhopper.routing.weighting.Weighting;
2627
import com.graphhopper.storage.*;
2728
import com.graphhopper.util.CmdArgs;
@@ -322,6 +323,9 @@ public void createPreparations(GraphHopperStorage ghStorage, TraversalMode trave
322323
traversalMode = getNodeBase();
323324

324325
for (Weighting weighting : getWeightings()) {
326+
if(weighting instanceof GenericWeighting){
327+
((GenericWeighting) weighting).setGraph(ghStorage);
328+
}
325329
PrepareContractionHierarchies tmpPrepareCH = new PrepareContractionHierarchies(
326330
new GHDirectory("", DAType.RAM_INT), ghStorage, ghStorage.getGraph(CHGraph.class, weighting),
327331
weighting, traversalMode);

core/src/main/java/com/graphhopper/routing/template/ViaRoutingTemplate.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,12 @@ public List<QueryResult> lookup(List<GHPoint> points, FlagEncoder encoder) {
6464
for (int placeIndex = 0; placeIndex < points.size(); placeIndex++) {
6565
GHPoint point = points.get(placeIndex);
6666
QueryResult res;
67-
if(ghRequest.hasPointHints()){
68-
res = locationIndex.findClosest(point.lat, point.lon, new NameSimilarityEdgeFilter(edgeFilter, ghRequest.getPointHint(placeIndex)));
69-
if(!res.isValid()){
67+
if (ghRequest.hasPointHints()) {
68+
res = locationIndex.findClosest(point.lat, point.lon, new NameSimilarityEdgeFilter(edgeFilter, ghRequest.getPointHints().get(placeIndex)));
69+
if (!res.isValid()) {
7070
res = locationIndex.findClosest(point.lat, point.lon, edgeFilter);
7171
}
72-
}else{
72+
} else {
7373
res = locationIndex.findClosest(point.lat, point.lon, edgeFilter);
7474
}
7575
if (!res.isValid())

0 commit comments

Comments
 (0)