Skip to content

Commit

Permalink
introduce new POST /route possibility (#1762)
Browse files Browse the repository at this point in the history
* introduce new POST /route endpoint, fixes #1689

* curbSide -> curbside

* gzip larger request bodies for POST /route and /matrix

* fix key access for GraphHopperWeb and POST /route

* rename GHRequest.put into putHint

* fix snap_prevention missing in matrix

* updated api-doc a bit

* improve docs
  • Loading branch information
karussell committed Oct 25, 2019
1 parent 58b6eb8 commit cf6a980
Show file tree
Hide file tree
Showing 18 changed files with 496 additions and 99 deletions.
30 changes: 24 additions & 6 deletions api/src/main/java/com/graphhopper/GHRequest.java
Expand Up @@ -33,11 +33,11 @@
* @author ratrun
*/
public class GHRequest {
private final List<GHPoint> points;
private List<GHPoint> points;
private final HintsMap hints = new HintsMap();
// List of favored start (1st element) and arrival heading (all other).
// Headings are north based azimuth (clockwise) in (0, 360) or NaN for equal preference
private final List<Double> favoredHeadings;
private List<Double> favoredHeadings;
private List<String> pointHints = new ArrayList<>();
private List<String> curbsides = new ArrayList<>();
private List<String> snapPreventions = new ArrayList<>();
Expand Down Expand Up @@ -101,7 +101,6 @@ public GHRequest(GHPoint startPlace, GHPoint endPlace) {

/**
* Set routing request
* <p>
*
* @param points List of stopover points in order: start, 1st stop, 2nd stop, ..., end
* @param favoredHeadings List of favored headings for starting (start point) and arrival (via
Expand All @@ -121,7 +120,6 @@ public GHRequest(List<GHPoint> points, List<Double> favoredHeadings) {

/**
* Set routing request
* <p>
*
* @param points List of stopover points in order: start, 1st stop, 2nd stop, ..., end
*/
Expand All @@ -131,7 +129,6 @@ public GHRequest(List<GHPoint> points) {

/**
* Add stopover point to routing request.
* <p>
*
* @param point geographical position (see GHPoint)
* @param favoredHeading north based azimuth (clockwise) in (0, 360) or NaN for equal preference
Expand All @@ -152,7 +149,6 @@ public GHRequest addPoint(GHPoint point, double favoredHeading) {

/**
* Add stopover point to routing request.
* <p>
*
* @param point geographical position (see GHPoint)
*/
Expand All @@ -161,10 +157,22 @@ public GHRequest addPoint(GHPoint point) {
return this;
}

public void setPoints(List<GHPoint> points) {
this.points = points;
if (favoredHeadings.isEmpty())
this.favoredHeadings = Collections.nCopies(points.size(), Double.NaN);
}

public void setHeadings(List<Double> favoredHeadings) {
this.favoredHeadings = favoredHeadings;
}

/**
* @return north based azimuth (clockwise) in (0, 360) or NaN for equal preference
*/
public double getFavoredHeading(int i) {
if (favoredHeadings.size() != points.size())
throw new IllegalStateException("Wrong size of headings " + favoredHeadings.size() + " vs. point count " + points.size());
return favoredHeadings.get(i);
}

Expand Down Expand Up @@ -243,6 +251,16 @@ public HintsMap getHints() {
return hints;
}

/**
* This method sets a key value pair in the hints and is equivalent to getHints().put(String, String) but unrelated
* to the setPointHints method. It is mainly used for deserialization with Jackson.
*
* @see #setPointHints(List)
*/
public void putHint(String fieldName, Object value) {
this.hints.put(fieldName, value);
}

public GHRequest setPointHints(List<String> pointHints) {
this.pointHints = pointHints;
return this;
Expand Down
@@ -1,3 +1,20 @@
/*
* 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.api;

import com.fasterxml.jackson.databind.JsonNode;
Expand All @@ -20,13 +37,14 @@
*/
public abstract class GHMatrixAbstractRequester {

static final String MATRIX_URL = "https://graphhopper.com/api/1/matrix";
protected final ObjectMapper objectMapper;
protected final Set<String> ignoreSet = new HashSet<>(10);
protected final Set<String> ignoreSet = new HashSet<>();
protected final String serviceUrl;
private OkHttpClient downloader;

public GHMatrixAbstractRequester() {
this("https://graphhopper.com/api/1/matrix");
this(MATRIX_URL);
}

public GHMatrixAbstractRequester(String serviceUrl) {
Expand Down Expand Up @@ -69,17 +87,6 @@ protected String getJson(String url) throws IOException {
}
}

protected String postJson(String url, JsonNode data) throws IOException {
Request okRequest = new Request.Builder().url(url).post(RequestBody.create(MT_JSON, data.toString())).build();
ResponseBody body = null;
try {
body = downloader.newCall(okRequest).execute().body();
return body.string();
} finally {
Helper.close(body);
}
}

protected JsonNode toJSON(String url, String str) {
try {
return objectMapper.readTree(str);
Expand Down
@@ -1,12 +1,32 @@
/*
* 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.api;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.graphhopper.jackson.PathWrapperDeserializer;
import com.graphhopper.util.Helper;
import com.graphhopper.util.shapes.GHPoint;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -15,22 +35,29 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.graphhopper.api.GraphHopperMatrixWeb.MT_JSON;

/**
* @author Peter Karich
*/
public class GHMatrixBatchRequester extends GHMatrixAbstractRequester {
final JsonNodeFactory factory = JsonNodeFactory.instance;
private final Logger logger = LoggerFactory.getLogger(getClass());
private int maxIterations = 100;
private long sleepAfterGET = 1000;
int unzippedLength = 1000;

public GHMatrixBatchRequester() {
super();
this(MATRIX_URL);
}

public GHMatrixBatchRequester(String serviceUrl) {
super(serviceUrl);
this(serviceUrl, new OkHttpClient.Builder().
connectTimeout(5, TimeUnit.SECONDS).
readTimeout(5, TimeUnit.SECONDS).
addInterceptor(new GzipRequestInterceptor()). // gzip the request
build());
}

public GHMatrixBatchRequester(String serviceUrl, OkHttpClient client) {
Expand All @@ -55,14 +82,14 @@ public GHMatrixBatchRequester setSleepAfterGET(long sleepAfterGETMillis) {

@Override
public MatrixResponse route(GHMRequest ghRequest) {
ObjectNode requestJson = factory.objectNode();
ObjectNode requestJson = objectMapper.createObjectNode();

List<String> outArraysList = new ArrayList<>(ghRequest.getOutArrays());
if (outArraysList.isEmpty()) {
outArraysList.add("weights");
}

ArrayNode outArrayListJson = factory.arrayNode();
ArrayNode outArrayListJson = objectMapper.createArrayNode();
for (String str : outArraysList) {
outArrayListJson.add(str);
}
Expand All @@ -83,6 +110,7 @@ public MatrixResponse route(GHMRequest ghRequest) {
requestJson.putArray("to_curbsides").addAll(createStringList(ghRequest.getToPointHints()));
}

requestJson.putArray("snap_preventions").addAll(createStringList(ghRequest.getSnapPreventions()));
requestJson.putArray("out_arrays").addAll(outArrayListJson);
requestJson.put("vehicle", ghRequest.getVehicle());
requestJson.put("elevation", hasElevation);
Expand Down Expand Up @@ -178,18 +206,34 @@ public MatrixResponse route(GHMRequest ghRequest) {
return matrixResponse;
}

private final ArrayNode createStringList(List<String> list) {
ArrayNode outList = factory.arrayNode();
protected String postJson(String url, JsonNode data) throws IOException {
String stringData = data.toString();
Request.Builder builder = new Request.Builder().url(url).post(RequestBody.create(MT_JSON, stringData));
// force avoiding our GzipRequestInterceptor for smaller requests ~30 locations
if (stringData.length() < unzippedLength)
builder.header("Content-Encoding", "identity");
Request okRequest = builder.build();
ResponseBody body = null;
try {
body = getDownloader().newCall(okRequest).execute().body();
return body.string();
} finally {
Helper.close(body);
}
}

private ArrayNode createStringList(List<String> list) {
ArrayNode outList = objectMapper.createArrayNode();
for (String str : list) {
outList.add(str);
}
return outList;
}

protected final ArrayNode createPointList(List<GHPoint> list) {
ArrayNode outList = factory.arrayNode();
private ArrayNode createPointList(List<GHPoint> list) {
ArrayNode outList = objectMapper.createArrayNode();
for (GHPoint p : list) {
ArrayNode entry = factory.arrayNode();
ArrayNode entry = objectMapper.createArrayNode();
entry.add(p.lon);
entry.add(p.lat);
outList.add(entry);
Expand Down
Expand Up @@ -9,7 +9,6 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
* @author Peter Karich
Expand All @@ -21,15 +20,13 @@ public GHMatrixSyncRequester() {
initIgnore();
}

public GHMatrixSyncRequester(String serviceUrl, OkHttpClient client) {
super(serviceUrl, client);
public GHMatrixSyncRequester(String serviceUrl) {
super(serviceUrl);
initIgnore();
}

public GHMatrixSyncRequester(String serviceUrl) {
super(serviceUrl, new OkHttpClient.Builder().
connectTimeout(15, TimeUnit.SECONDS).
readTimeout(15, TimeUnit.SECONDS).build());
public GHMatrixSyncRequester(String serviceUrl, OkHttpClient client) {
super(serviceUrl, client);
initIgnore();
}

Expand Down Expand Up @@ -70,15 +67,22 @@ public MatrixResponse route(GHMRequest ghRequest) {
}

for (String type : outArraysList) {
if (!type.isEmpty()) {
if (!type.isEmpty())
outArrayStr += "&";
}

outArrayStr += "out_array=" + type;
}

String snapPreventionStr = "";
for (String snapPrevention : ghRequest.getSnapPreventions()) {
if (!snapPreventionStr.isEmpty())
snapPreventionStr += "&";

snapPreventionStr += "snap_prevention=" + snapPrevention;
}

String url = buildURL("", ghRequest);
url += "&" + pointsStr + "&" + pointHintsStr + "&" + curbsidesStr + "&" + outArrayStr;
url += "&" + pointsStr + "&" + pointHintsStr + "&" + curbsidesStr + "&" + outArrayStr + "&" + snapPreventionStr;
if (!Helper.isEmpty(ghRequest.getVehicle())) {
url += "&vehicle=" + ghRequest.getVehicle();
}
Expand Down
Expand Up @@ -4,14 +4,13 @@
import okhttp3.MediaType;

/**
*
* @author Peter Karich
*/
public class GraphHopperMatrixWeb {

public static final String SERVICE_URL = "service_url";
static final String SERVICE_URL = "service_url";
public static final String KEY = "key";
public static final MediaType MT_JSON = MediaType.parse("application/json; charset=utf-8");
static final MediaType MT_JSON = MediaType.parse("application/json; charset=utf-8");
private final GHMatrixAbstractRequester requester;
private String key;

Expand All @@ -37,10 +36,10 @@ public GraphHopperMatrixWeb setKey(String key) {
}

public MatrixResponse route(GHMRequest request) {
if (!Helper.isEmpty(key)) {
if (!Helper.isEmpty(key))
request.getHints().put(KEY, key);
}

if (!request.getPathDetails().isEmpty())
throw new IllegalArgumentException("Path details are not supported for the Matrix API");
request.compactPointHints();
return requester.route(request);
}
Expand Down

0 comments on commit cf6a980

Please sign in to comment.