Skip to content

Commit

Permalink
refactor: restructure ExportRequest.computeExport(), rename API param…
Browse files Browse the repository at this point in the history
…eter "debug" to "additional_info"
  • Loading branch information
takb committed Dec 10, 2024
1 parent 9b7530f commit 3f8ad0d
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 104 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ public class ExportApiRequest extends APIRequest {
public static final String PARAM_PROFILE = "profile";
public static final String PARAM_FORMAT = "format";
public static final String PARAM_GEOMETRY = "geometry";

public static final String PARAM_DEBUG = "debug";
public static final String PARAM_ADDITIONAL_INFO = "additional_info";

@Schema(name = PARAM_ID, description = "Arbitrary identification string of the request reflected in the meta information.",
example = "export_request")
Expand All @@ -45,9 +44,9 @@ public class ExportApiRequest extends APIRequest {
@JsonProperty(PARAM_GEOMETRY)
private boolean geometry = true;

@Schema(name = PARAM_DEBUG, hidden = true)
@JsonProperty(PARAM_DEBUG)
private boolean debug;
@Schema(name = PARAM_ADDITIONAL_INFO, hidden = true)
@JsonProperty(PARAM_ADDITIONAL_INFO)
private boolean additionalInfo;

@JsonCreator
public ExportApiRequest(@JsonProperty(value = PARAM_BBOX, required = true) List<List<Double>> bbox) {
Expand All @@ -67,8 +66,8 @@ public void setId(String id) {
this.hasId = true;
}

public boolean debug() {
return debug;
public boolean additionalInfo() {
return additionalInfo;
}

public List<List<Double>> getBbox() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public static TopoJsonExportResponse fromExportResult(ExportResult exportResult)
for (Map.Entry<Pair<Integer, Integer>, Double> edgeWeight : exportResult.getEdgeWeights().entrySet()) {
Pair<Integer, Integer> fromTo = edgeWeight.getKey();

LineString lineString = (LineString) exportResult.getEdgeExtras().get(fromTo).get("geometry");
LineString lineString = exportResult.getEdgeGeometries().get(fromTo);
arcsLocal.add(Arc.builder().coordinates(makeCoordinateList(lineString, bbox)).build());
arcCount++;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ private org.heigit.ors.export.ExportRequest convertExportRequest(ExportApiReques
}

exportRequest.setBoundingBox(convertBBox(exportApiRequest.getBbox()));
exportRequest.setDebug(exportApiRequest.debug());
exportRequest.setAdditionalEdgeInfo(exportApiRequest.additionalInfo());
exportRequest.setTopoJson(exportApiRequest.getResponseType().equals(APIEnums.ExportResponseType.TOPOJSON));
exportRequest.setUseRealGeometry(exportApiRequest.getGeometry());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ void expectNodesAndEdges() {
.body("containsKey('edges')", is(true))
.body("containsKey('nodes_count')", is(true))
.body("containsKey('edges_count')", is(true))
.body("containsKey('edges_extra')", is(false))
.statusCode(200);
}

Expand Down Expand Up @@ -246,4 +247,27 @@ void expectTopoJsonFallbackNoOsmIdMode() {
.body("objects.network.geometries[0].properties.containsKey('weight')", is(true))
.statusCode(200);
}

@Test
void expectAdditionalInfo() {
JSONObject body = new JSONObject();
body.put("bbox", getParameter("bboxProper"));
body.put("additional_info", true);
given()
.headers(jsonContent)
.pathParam("profile", "wheelchair")
.body(body.toString())
.when()
.post(getEndPointPath() + "/{profile}/json")
.then().log().ifValidationFails()
.assertThat()
.body("containsKey('nodes')", is(true))
.body("containsKey('edges')", is(true))
.body("containsKey('edges_extra')", is(true))
.body("edges_extra[0].extra.containsKey('osm_id')", is(true))
.body("edges_extra[0].extra.containsKey('ors_id')", is(true))
.body("nodes_count", is(59))
.body("edges_count", is(128))
.statusCode(200);
}
}
180 changes: 85 additions & 95 deletions ors-engine/src/main/java/org/heigit/ors/export/ExportRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,14 @@ public class ExportRequest extends ServiceRequest {
private String profileName;
private int profileType = -1;

private boolean debug;
private boolean additionalEdgeInfo;
private boolean topoJson;
private boolean useRealGeometry;

private static final int NO_TIME = -1;
private OsmIdGraphStorage osmIdGraphStorage;
private WheelchairAttributesGraphStorage wheelchairAttributesGraphStorage;
private Weighting weighting;

public String getProfileName() {
return profileName;
Expand All @@ -53,24 +56,16 @@ public void setProfileName(String profileName) {
this.profileName = profileName;
}

public BBox getBoundingBox() {
return this.boundingBox;
}

public void setBoundingBox(BBox bbox) {
this.boundingBox = bbox;
}

public int getProfileType() {
return profileType;
}

public void setProfileType(int profileType) {
this.profileType = profileType;
}

public void setDebug(boolean debug) {
this.debug = debug;
public void setAdditionalEdgeInfo(boolean additionalEdgeInfo) {
this.additionalEdgeInfo = additionalEdgeInfo;
}

public void setTopoJson(boolean equals) {
Expand All @@ -84,113 +79,108 @@ public void setUseRealGeometry(boolean useRealGeometry) {
public ExportResult computeExport(RoutingProfile routingProfile) {
ExportResult res = new ExportResult();

// Prepare graph data access
GraphHopper gh = routingProfile.getGraphhopper();
String encoderName = RoutingProfileType.getEncoderName(getProfileType());
String encoderName = RoutingProfileType.getEncoderName(profileType);
Graph graph = gh.getGraphHopperStorage().getBaseGraph();

NodeAccess nodeAccess = graph.getNodeAccess();
PMap hintsMap = new PMap();
int weightingMethod = WeightingMethod.FASTEST;
ProfileTools.setWeightingMethod(hintsMap, weightingMethod, getProfileType(), false);
String localProfileName = ProfileTools.makeProfileName(encoderName, hintsMap.getString("weighting_method", ""), false);
Weighting weighting = gh.createWeighting(gh.getProfile(localProfileName), hintsMap);

FlagEncoder flagEncoder = gh.getEncodingManager().getEncoder(encoderName);
EdgeExplorer explorer = graph.createEdgeExplorer(AccessFilter.outEdges(flagEncoder.getAccessEnc()));
ProfileTools.setWeightingMethod(hintsMap, WeightingMethod.FASTEST, profileType, false);
weighting = gh.createWeighting(gh.getProfile(ProfileTools.makeProfileName(encoderName, hintsMap.getString("weighting_method", ""), false)), hintsMap);
osmIdGraphStorage = GraphStorageUtils.getGraphExtension(gh.getGraphHopperStorage(), OsmIdGraphStorage.class);
wheelchairAttributesGraphStorage = GraphStorageUtils.getGraphExtension(gh.getGraphHopperStorage(), WheelchairAttributesGraphStorage.class);

// filter graph for nodes in Bounding Box
LocationIndex index = gh.getLocationIndex();
NodeAccess nodeAccess = graph.getNodeAccess();

Set<Integer> nodesInBBox = new HashSet<>();
index.query(boundingBox, edgeId -> {
// According to GHUtility.getEdgeFromEdgeKey, edgeIds are calculated as edgeKey/2.
EdgeIteratorState edge = graph.getEdgeIteratorStateForKey(edgeId * 2);
int baseNode = edge.getBaseNode();
int adjNode = edge.getAdjNode();
if (boundingBox.contains(nodeAccess.getLat(baseNode), nodeAccess.getLon(baseNode))) {
nodesInBBox.add(baseNode);
}
if (boundingBox.contains(nodeAccess.getLat(adjNode), nodeAccess.getLon(adjNode))) {
nodesInBBox.add(adjNode);
}
});

Set<Integer> nodesInBBox = nodesInBBox(gh.getLocationIndex(), nodeAccess, graph);
LOGGER.debug("Found %d nodes in bbox.".formatted(nodesInBBox.size()));

if (nodesInBBox.isEmpty()) {
// without nodes, no export can be calculated
if (nodesInBBox.isEmpty()) { // without nodes, no export can be calculated
res.setWarning(new ExportWarning(ExportWarning.EMPTY_BBOX));
return res;
}
Map<Long, TopoGeometry> topoGeometries = res.getTopoGeometries();
OsmIdGraphStorage osmIdGraphStorage = GraphStorageUtils.getGraphExtension(gh.getGraphHopperStorage(), OsmIdGraphStorage.class);
boolean osmIdsAvailable = osmIdGraphStorage != null;

// calculate node coordinates
// iterate over all edges and add them to the result object
for (int from : nodesInBBox) {
Coordinate fromCoords = new Coordinate(nodeAccess.getLon(from), nodeAccess.getLat(from));
res.addLocation(from, fromCoords);
EdgeIterator iter = explorer.setBaseNode(from);
EdgeIterator iter = graph.createEdgeExplorer(AccessFilter.outEdges(gh.getEncodingManager().getEncoder(encoderName).getAccessEnc())).setBaseNode(from);
while (iter.next()) {
int to = iter.getAdjNode();
if (nodesInBBox.contains(to)) {
Pair<Integer, Integer> p = new Pair<>(from, to);
Map<String, Object> extra = new HashMap<>();
res.addEdge(p, weighting.calcEdgeWeight(iter, false, NO_TIME));
LOGGER.debug("Edge %d: from %d to %d".formatted(iter.getEdge(), from, to));

if (topoJson) {
LineString geo;
if (useRealGeometry) {
geo = iter.fetchWayGeometry(FetchMode.ALL).toLineString(false);
} else {
Coordinate toCoords = new Coordinate(nodeAccess.getLon(to), nodeAccess.getLat(to));
geo = geometryFactory.createLineString(new Coordinate[]{fromCoords, toCoords});
}
if (osmIdsAvailable) {
boolean reverse = iter.getEdgeKey() % 2 == 1;
TopoGeometry topoGeometry = topoGeometries.computeIfAbsent(osmIdGraphStorage.getEdgeValue(iter.getEdge()), x ->
new TopoGeometry(weighting.getSpeedCalculator().getSpeed(iter, reverse, NO_TIME),
weighting.getSpeedCalculator().getSpeed(iter, !reverse, NO_TIME))
);
topoGeometry.getArcs().compute(iter.getEdge(), (k, v) -> {
if (v != null) {
topoGeometry.setBothDirections(true);
return v;
} else {
return reverse ? new TopoArc(geo.reverse(), iter.getDistance(), iter.getAdjNode(), iter.getBaseNode()) :
new TopoArc(geo, iter.getDistance(), iter.getBaseNode(), iter.getAdjNode());
}
});
} else {
extra.put("geometry", geo);
}
}

if (debug) {
extra.put("edge_id", iter.getEdge());
WheelchairAttributesGraphStorage storage = GraphStorageUtils.getGraphExtension(gh.getGraphHopperStorage(), WheelchairAttributesGraphStorage.class);
if (storage != null) {
WheelchairAttributes attributes = new WheelchairAttributes();
byte[] buffer = new byte[WheelchairAttributesGraphStorage.BYTE_COUNT];
storage.getEdgeValues(iter.getEdge(), attributes, buffer);
if (attributes.hasValues()) {
extra.put("incline", attributes.getIncline());
extra.put("surface_quality_known", attributes.isSurfaceQualityKnown());
extra.put("suitable", attributes.isSuitable());
}
}
if (osmIdsAvailable) {
extra.put("osm_id", osmIdGraphStorage.getEdgeValue(iter.getEdge()));
}
LineString geo;
if (useRealGeometry) {
geo = iter.fetchWayGeometry(FetchMode.ALL).toLineString(false);
} else {
Coordinate toCoords = new Coordinate(nodeAccess.getLon(to), nodeAccess.getLat(to));
geo = geometryFactory.createLineString(new Coordinate[]{fromCoords, toCoords});
}

if (!extra.isEmpty()) {
res.addEdgeExtra(p, extra);
if (topoJson && osmIdGraphStorage != null) {
addEdgeToTopoGeometries(res, iter, geo);
} else {
addEdgeToResultObject(res, iter, geo, from, to);
}
}
}
}
return res;
}

private Set<Integer> nodesInBBox(LocationIndex index, NodeAccess nodeAccess, Graph graph) {
Set<Integer> ret = new HashSet<>();
index.query(boundingBox, edgeId -> {
// According to GHUtility.getEdgeFromEdgeKey, edgeIds are calculated as edgeKey/2.
EdgeIteratorState edge = graph.getEdgeIteratorStateForKey(edgeId * 2);
int baseNode = edge.getBaseNode();
int adjNode = edge.getAdjNode();
if (this.boundingBox.contains(nodeAccess.getLat(baseNode), nodeAccess.getLon(baseNode))) {
ret.add(baseNode);
}
if (this.boundingBox.contains(nodeAccess.getLat(adjNode), nodeAccess.getLon(adjNode))) {
ret.add(adjNode);
}
});
return ret;
}

private void addEdgeToTopoGeometries(ExportResult res, EdgeIterator iter, LineString geo) {
boolean reverse = iter.getEdgeKey() % 2 == 1;
TopoGeometry topoGeometry = res.getTopoGeometries().computeIfAbsent(osmIdGraphStorage.getEdgeValue(iter.getEdge()), x ->
new TopoGeometry(weighting.getSpeedCalculator().getSpeed(iter, reverse, NO_TIME),
weighting.getSpeedCalculator().getSpeed(iter, !reverse, NO_TIME))
);
topoGeometry.getArcs().compute(iter.getEdge(), (k, v) -> {
if (v != null) {
topoGeometry.setBothDirections(true);
return v;
} else {
return reverse ? new TopoArc(geo.reverse(), iter.getDistance(), iter.getAdjNode(), iter.getBaseNode()) :
new TopoArc(geo, iter.getDistance(), iter.getBaseNode(), iter.getAdjNode());
}
});
}

private void addEdgeToResultObject(ExportResult res, EdgeIterator iter, LineString geo, int from, int to) {
Pair<Integer, Integer> p = new Pair<>(from, to);
res.addEdge(p, weighting.calcEdgeWeight(iter, false, NO_TIME));
res.getEdgeGeometries().put(p, geo);
if (additionalEdgeInfo) {
Map<String, Object> extra = new HashMap<>();
if (osmIdGraphStorage != null) {
extra.put("osm_id", osmIdGraphStorage.getEdgeValue(iter.getEdge()));
}
extra.put("ors_id", iter.getEdge());
if (wheelchairAttributesGraphStorage != null) {
WheelchairAttributes attributes = new WheelchairAttributes();
byte[] buffer = new byte[WheelchairAttributesGraphStorage.BYTE_COUNT];
wheelchairAttributesGraphStorage.getEdgeValues(iter.getEdge(), attributes, buffer);
if (attributes.hasValues()) {
extra.put("incline", attributes.getIncline());
extra.put("surface_quality_known", attributes.isSurfaceQualityKnown());
extra.put("suitable", attributes.isSuitable());
}
}
res.addEdgeExtra(p, extra);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
public class ExportResult {
private final Map<Integer, Coordinate> locations;
private final Map<Pair<Integer, Integer>, Double> edgeWeights;
private final Map<Pair<Integer, Integer>, LineString> edgeGeometries;
private final Map<Long, TopoGeometry> topoGeometries;
private Map<Pair<Integer, Integer>, Map<String, Object>> edgeExtras;
@Setter
Expand All @@ -22,6 +23,7 @@ public class ExportResult {
public ExportResult() {
this.locations = new HashMap<>();
this.edgeWeights = new HashMap<>();
this.edgeGeometries = new HashMap<>();
this.topoGeometries = new HashMap<>();
this.warning = null;
}
Expand Down

0 comments on commit 3f8ad0d

Please sign in to comment.