diff --git a/openrouteservice-api-tests/src/test/java/heigit/ors/v2/services/routing/ResultTest.java b/openrouteservice-api-tests/src/test/java/heigit/ors/v2/services/routing/ResultTest.java index 896405c7b7..d3e9eb2fa3 100644 --- a/openrouteservice-api-tests/src/test/java/heigit/ors/v2/services/routing/ResultTest.java +++ b/openrouteservice-api-tests/src/test/java/heigit/ors/v2/services/routing/ResultTest.java @@ -82,6 +82,7 @@ public ResultTest() { extraInfo.put("surface"); extraInfo.put("suitability"); extraInfo.put("steepness"); + extraInfo.put("countryinfo"); addParameter("extra_info", extraInfo); addParameter("preference", "fastest"); @@ -838,7 +839,7 @@ public void testExtras() { given() .header("Accept", "application/json") .header("Content-Type", "application/json") - .pathParam("profile", getParameter("bikeProfile")) + .pathParam("profile", getParameter("carProfile")) .body(body.toString()) .when() .post(getEndPointPath() + "/{profile}") @@ -849,6 +850,7 @@ public void testExtras() { .body("routes[0].extras.containsKey('surface')", is(true)) .body("routes[0].extras.containsKey('suitability')", is(true)) .body("routes[0].extras.containsKey('steepness')", is(true)) + .body("routes[0].extras.containsKey('countryinfo')", is(true)) .statusCode(200); } @@ -2407,6 +2409,87 @@ public void testPreferQuiet() { .statusCode(200); } + @Test + public void testCountryTraversalExtra() { + JSONObject body = new JSONObject(); + JSONArray coordinatesBoundaryCrossing = new JSONArray(); + JSONArray coord1 = new JSONArray(); + coord1.put(8.685046434402466); + coord1.put(49.40267269586634); + coordinatesBoundaryCrossing.put(coord1); + JSONArray coord2 = new JSONArray(); + coord2.put(8.687556982040405); + coord2.put(49.40271458586781); + coordinatesBoundaryCrossing.put(coord2); + body.put("coordinates", coordinatesBoundaryCrossing); + JSONArray extraInfo = new JSONArray(); + extraInfo.put("countryinfo"); + body.put("extra_info", extraInfo); + + // Border crossing + given() + .header("Accept", "application/json") + .header("Content-Type", "application/json") + .pathParam("profile", getParameter("carProfile")) + .body(body.toString()) + .when() + .post(getEndPointPath() + "/{profile}/json") + .then().log().ifValidationFails() + .assertThat() + .body("any { it.key == 'routes' }", is(true)) + .body("routes[0].containsKey('extras')", is(true)) + .body("routes[0].extras.containsKey('countryinfo')", is(true)) + .body("routes[0].extras.countryinfo.containsKey('values')", is(true)) + .body("routes[0].extras.countryinfo.containsKey('summary')", is(true)) + .body("routes[0].extras.countryinfo.values[0][0]", is(0)) + .body("routes[0].extras.countryinfo.values[0][1]", is(2)) + .body("routes[0].extras.countryinfo.values[0][2]", is(2)) + .body("routes[0].extras.countryinfo.summary[0].value", is(2.0f)) + .body("routes[0].extras.countryinfo.summary[0].distance", is(150.4f)) + .body("routes[0].extras.countryinfo.summary[0].amount", is(82.88f)) + .body("routes[0].extras.countryinfo.values[1][0]", is(2)) + .body("routes[0].extras.countryinfo.values[1][1]", is(3)) + .body("routes[0].extras.countryinfo.values[1][2]", is(3)) + .body("routes[0].extras.countryinfo.summary[1].value", is(3.0f)) + .body("routes[0].extras.countryinfo.summary[1].distance", is(31.1f)) + .body("routes[0].extras.countryinfo.summary[1].amount", is(17.12f)) + .statusCode(200); + + JSONArray coordinatesNoBoundaryCrossing = new JSONArray(); + coord1 = new JSONArray(); + coord1.put(8.692256212234497); + coord1.put(49.405004518240005); + coordinatesNoBoundaryCrossing.put(coord1); + coord2 = new JSONArray(); + coord2.put(8.689970970153809); + coord2.put(49.40532565875338); + coordinatesNoBoundaryCrossing.put(coord2); + body.put("coordinates", coordinatesNoBoundaryCrossing); + + // No border crossing + given() + .header("Accept", "application/json") + .header("Content-Type", "application/json") + .pathParam("profile", getParameter("carProfile")) + .body(body.toString()) + .when() + .post(getEndPointPath() + "/{profile}/json") + .then().log().ifValidationFails() + .assertThat() + .body("any { it.key == 'routes' }", is(true)) + .body("routes[0].containsKey('extras')", is(true)) + .body("routes[0].extras.containsKey('countryinfo')", is(true)) + .body("routes[0].extras.countryinfo.containsKey('values')", is(true)) + .body("routes[0].extras.countryinfo.containsKey('summary')", is(true)) + .body("routes[0].extras.countryinfo.values[0][0]", is(0)) + .body("routes[0].extras.countryinfo.values[0][1]", is(2)) + .body("routes[0].extras.countryinfo.values[0][2]", is(4)) + .body("routes[0].extras.countryinfo.summary[0].value", is(4.0f)) + .body("routes[0].extras.countryinfo.summary[0].distance", is(169.2f)) + .body("routes[0].extras.countryinfo.summary[0].amount", is(100.0f)) + .statusCode(200); + } + private JSONArray constructCoords(String coordString) { JSONArray coordinates = new JSONArray(); String[] coordPairs = coordString.split("\\|"); diff --git a/openrouteservice/src/main/java/heigit/ors/api/requests/common/APIEnums.java b/openrouteservice/src/main/java/heigit/ors/api/requests/common/APIEnums.java index 8d314675f6..9058d6ab83 100644 --- a/openrouteservice/src/main/java/heigit/ors/api/requests/common/APIEnums.java +++ b/openrouteservice/src/main/java/heigit/ors/api/requests/common/APIEnums.java @@ -63,7 +63,8 @@ public enum ExtraInfo { TOLLWAYS("tollways"), TRAIL_DIFFICULTY("traildifficulty"), OSM_ID("osmid"), - ROAD_ACCESS_RESTRICTIONS("roadaccessrestrictions"); + ROAD_ACCESS_RESTRICTIONS("roadaccessrestrictions"), + COUNTRY_INFO("countryinfo"); private final String value; diff --git a/openrouteservice/src/main/java/heigit/ors/routing/RouteExtraInfoFlag.java b/openrouteservice/src/main/java/heigit/ors/routing/RouteExtraInfoFlag.java index 52d3b99168..ca24e16855 100644 --- a/openrouteservice/src/main/java/heigit/ors/routing/RouteExtraInfoFlag.java +++ b/openrouteservice/src/main/java/heigit/ors/routing/RouteExtraInfoFlag.java @@ -28,6 +28,7 @@ public class RouteExtraInfoFlag { public static final int TrailDifficulty = 512; public static final int OsmId = 1024; public static final int RoadAccessRestrictions = 2048; + public static final int CountryInfo = 4096; public static boolean isSet(int extraInfo, int value) { return (extraInfo & value) == value; @@ -78,6 +79,9 @@ public static int getFromString(String value) { case "roadaccessrestrictions": res |= RoadAccessRestrictions; break; + case "countryinfo": + res |= CountryInfo; + break; } } diff --git a/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/ORSOSMReader.java b/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/ORSOSMReader.java index 21e2940628..1fab1b88b2 100644 --- a/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/ORSOSMReader.java +++ b/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/ORSOSMReader.java @@ -20,7 +20,7 @@ import com.graphhopper.storage.GraphHopperStorage; import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.Helper; -import com.vividsolutions.jts.geom.*; +import com.vividsolutions.jts.geom.Coordinate; import heigit.ors.routing.RoutingProfile; import heigit.ors.routing.graphhopper.extensions.reader.osmfeatureprocessors.OSMFeatureFilter; import heigit.ors.routing.graphhopper.extensions.reader.osmfeatureprocessors.WheelchairWayFilter; @@ -31,9 +31,13 @@ import org.apache.log4j.Logger; import java.io.InvalidObjectException; -import java.util.*; - +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; +import java.util.Set; public class ORSOSMReader extends OSMReader { diff --git a/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/reader/borders/CountryBordersReader.java b/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/reader/borders/CountryBordersReader.java index b550a63ce4..9c5d5cf9b3 100644 --- a/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/reader/borders/CountryBordersReader.java +++ b/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/reader/borders/CountryBordersReader.java @@ -13,6 +13,8 @@ */ package heigit.ors.routing.graphhopper.extensions.reader.borders; +import com.graphhopper.util.PointList; +import com.graphhopper.util.shapes.GHPoint3D; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.Point; @@ -381,6 +383,18 @@ public static int getCountryIdByISOCode(String code) { return currentInstance != null ? currentInstance.isoCodes.getOrDefault(code.toUpperCase(), 0) : 0; } + public static CountryBordersPolygon[] getCountriesByPointList(PointList pointList) { + CountryBordersPolygon[] countries = new CountryBordersPolygon[0]; + for (GHPoint3D point : + pointList) { + Coordinate coordinate = new Coordinate(point.lon, point.lat); + CountryBordersPolygon[] countries2 = currentInstance.getCandidateCountry(coordinate); + CountryBordersPolygon[] countries23 = currentInstance.getCandidateCountry(coordinate); + + } + return countries; + } + /** * Read information from the id csv. This includes a unique identifier, the local name of the country and the * English name of the country. Optionally reads ISO codes from column 4 and 5 (expecting them to contain the diff --git a/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/storages/BordersGraphStorage.java b/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/storages/BordersGraphStorage.java index da65ef4fec..4fee2f88c9 100644 --- a/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/storages/BordersGraphStorage.java +++ b/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/storages/BordersGraphStorage.java @@ -1,15 +1,15 @@ /* This file is part of Openrouteservice. * - * Openrouteservice is free software; you can redistribute it and/or modify it under the terms of the - * GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 + * Openrouteservice is free software; you can redistribute it and/or modify it under the terms of the + * GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 * of the License, or (at your option) any later version. - * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. - * You should have received a copy of the GNU Lesser General Public License along with this library; - * if not, see . + * You should have received a copy of the GNU Lesser General Public License along with this library; + * if not, see . */ package heigit.ors.routing.graphhopper.extensions.storages; @@ -19,11 +19,15 @@ import com.graphhopper.storage.GraphExtension; import com.graphhopper.storage.RAMDirectory; +import org.apache.log4j.Logger; + /** * Graph storage class for the Border Restriction routing */ public class BordersGraphStorage implements GraphExtension { - public enum Property { TYPE, START, END }; + private static final Logger LOGGER = Logger.getLogger(BordersGraphStorage.class.getName()); + + public enum Property { TYPE, START, END} /* pointer for no entry */ protected final int NO_ENTRY = -1; private final int EF_BORDER = 0; // byte location of border type @@ -40,7 +44,6 @@ public enum Property { TYPE, START, END }; private int edgesCount; // number of edges with custom values public BordersGraphStorage() { - //EF_BORDER = 0; int edgeEntryIndex = 0; edgeEntryBytes = edgeEntryIndex + 6; // item uses 3 short values which are 2 bytes length each @@ -52,11 +55,10 @@ public BordersGraphStorage() { * * This method takes the internal ID of the edge and adds the information obtained from the Borders CSV file to it * so that the values can be taken into account when generating a route. - * - * @param edgeId Internal ID of the graph edge - * @param borderType Level of border crossing (0 - No border, 1 - controlled border, 2 - open border= - * @param start ID of the country that the edge starts in - * @param end ID of the country that the edge ends in + * @param edgeId Internal ID of the graph edge + * @param borderType Level of border crossing (0 - No border, 1 - controlled border, 2 - open border= + * @param start ID of the country that the edge starts in + * @param end ID of the country that the edge ends in */ public void setEdgeValue(int edgeId, short borderType, short start, short end) { edgesCount++; @@ -74,24 +76,28 @@ public void setEdgeValue(int edgeId, short borderType, short start, short end) { /** * Get the specified custom value of the edge that was assigned to it in the setValueEdge method

- * + *

* The method takes an identifier to the edge and then gets the requested value for the edge from the storage * - * @param edgeId Internal ID of the edge to get values for - * @param prop The property of the edge to get (TYPE - border type (0,1,2), START - the ID of the country - * the edge starts in, END - the ID of the country the edge ends in. - * @return The value of the requested property + * @param edgeId Internal ID of the edge to get values for + * @param prop The property of the edge to get (TYPE - border type (0,1,2), START - the ID of the country + * the edge starts in, END - the ID of the country the edge ends in. + * @return The value of the requested property */ public short getEdgeValue(int edgeId, Property prop) { + // TODO maybe implement a second function that accesses only the country data long edgePointer = (long) edgeId * edgeEntryBytes; - short border = 0, start = 0, end = 0; + short border; + short start; + short end; + short genuineCountry; + short edge = orsEdges.getShort(edgePointer); border = orsEdges.getShort(edgePointer + EF_BORDER); start = orsEdges.getShort(edgePointer + EF_START); end = orsEdges.getShort(edgePointer + EF_END); switch (prop) { case TYPE: - return border; case START: return start; diff --git a/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/storages/builders/BordersGraphStorageBuilder.java b/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/storages/builders/BordersGraphStorageBuilder.java index 8b0df345ff..a418b06f44 100644 --- a/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/storages/builders/BordersGraphStorageBuilder.java +++ b/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/storages/builders/BordersGraphStorageBuilder.java @@ -1,15 +1,15 @@ /* This file is part of Openrouteservice. * - * Openrouteservice is free software; you can redistribute it and/or modify it under the terms of the - * GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 + * Openrouteservice is free software; you can redistribute it and/or modify it under the terms of the + * GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 * of the License, or (at your option) any later version. - * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. - * You should have received a copy of the GNU Lesser General Public License along with this library; - * if not, see . + * You should have received a copy of the GNU Lesser General Public License along with this library; + * if not, see . */ package heigit.ors.routing.graphhopper.extensions.storages.builders; @@ -20,7 +20,6 @@ import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.LineString; -import com.vividsolutions.jts.geom.Point; import heigit.ors.exceptions.MissingConfigParameterException; import heigit.ors.routing.graphhopper.extensions.reader.borders.CountryBordersPolygon; import heigit.ors.routing.graphhopper.extensions.reader.borders.CountryBordersReader; @@ -119,13 +118,15 @@ public void processWay(ReaderWay way) { public void processWay(ReaderWay way, Coordinate[] coords, HashMap> nodeTags) { // Process the way using the geometry provided // if we don't have the reader object, then we can't do anything - if(cbReader != null) { + if (cbReader != null) { String[] countries = findBorderCrossing(coords); - // If we find that the length of countries is more than one, then it does cross a border if (countries.length > 1 && !countries[0].equals(countries[1])) { way.setTag("country1", countries[0]); way.setTag("country2", countries[1]); + } else if (countries.length == 1){ + way.setTag("country1", countries[0]); + way.setTag("country2", countries[0]); } } } @@ -139,35 +140,33 @@ public void processWay(ReaderWay way, Coordinate[] coords, HashMap 2){ +// countries = countries; +// } // Now we have a list of all the countries that the nodes are in - if this is more than one it is likely it is // crossing a border, but not certain as in some disputed areas, countries overlap and so it may not cross any // border. diff --git a/openrouteservice/src/main/java/heigit/ors/routing/pathprocessors/ExtraInfoProcessor.java b/openrouteservice/src/main/java/heigit/ors/routing/pathprocessors/ExtraInfoProcessor.java index 0480db94f6..791d908f81 100644 --- a/openrouteservice/src/main/java/heigit/ors/routing/pathprocessors/ExtraInfoProcessor.java +++ b/openrouteservice/src/main/java/heigit/ors/routing/pathprocessors/ExtraInfoProcessor.java @@ -30,9 +30,11 @@ import heigit.ors.routing.util.extrainfobuilders.SimpleRouteExtraInfoBuilder; import heigit.ors.routing.util.extrainfobuilders.SteepnessExtraInfoBuilder; -import java.util.*; +import java.util.ArrayList; +import java.util.List; public class ExtraInfoProcessor extends PathProcessor { + private WaySurfaceTypeGraphStorage _extWaySurface; private WayCategoryGraphStorage _extWayCategory; private GreenIndexGraphStorage _extGreenIndex; @@ -42,7 +44,8 @@ public class ExtraInfoProcessor extends PathProcessor { private HillIndexGraphStorage _extHillIndex; private OsmIdGraphStorage _extOsmId; private RoadAccessRestrictionsGraphStorage _extRoadAccessRestrictions; - + private BordersGraphStorage _extCountryTraversalInfo; + private RouteExtraInfo _surfaceInfo; private RouteExtraInfoBuilder _surfaceInfoBuilder; @@ -80,6 +83,9 @@ public class ExtraInfoProcessor extends PathProcessor { private RouteExtraInfo _roadAccessRestrictionsInfo; private RouteExtraInfoBuilder _roadAccessRestrictionsBuilder; + private RouteExtraInfo _countryTraversalInfo; + private RouteExtraInfoBuilder _countryTraversalBuilder; + private List warningExtensions; private int _profileType = RoutingProfileType.UNKNOWN; @@ -205,6 +211,13 @@ public ExtraInfoProcessor(ORSGraphHopper graphHopper, RoutingRequest req) throws _roadAccessRestrictionsBuilder = new SimpleRouteExtraInfoBuilder(_roadAccessRestrictionsInfo); } + if (includeExtraInfo(extraInfo, RouteExtraInfoFlag.CountryInfo)) { + _extCountryTraversalInfo = GraphStorageUtils.getGraphExtension(graphHopper.getGraphHopperStorage(), BordersGraphStorage.class); + if (_extCountryTraversalInfo != null) { + _countryTraversalInfo = new RouteExtraInfo("countryinfo", _extCountryTraversalInfo); + _countryTraversalBuilder = new SimpleRouteExtraInfoBuilder(_countryTraversalInfo); + } + } buffer = new byte[4]; } @@ -276,6 +289,8 @@ public List getExtras() extras.add(_osmIdInfo); if (_roadAccessRestrictionsInfo != null) extras.add(_roadAccessRestrictionsInfo); + if (_countryTraversalInfo != null) + extras.add(_countryTraversalInfo); return extras; } @@ -283,6 +298,13 @@ public List getExtras() public void processEdge(EdgeIteratorState edge, boolean isLastEdge, PointList geom) { double dist = edge.getDistance(); + // TODO Add extra info for crossed countries + if (_extCountryTraversalInfo != null){ + short country = _extCountryTraversalInfo.getEdgeValue(EdgeIteratorStateHelper.getOriginalEdge(edge), BordersGraphStorage.Property.START); + if (_countryTraversalBuilder != null && country != 0) { + _countryTraversalBuilder.addSegment(country, country, geom, dist, isLastEdge && _lastSegment); + } + } if (_extWaySurface != null && _wayTypeInfo != null || _surfaceInfo != null) { WaySurfaceDescription wsd = _extWaySurface.getEdgeValue(EdgeIteratorStateHelper.getOriginalEdge(edge), buffer); diff --git a/openrouteservice/src/test/java/heigit/ors/routing/graphhopper/extensions/storages/builders/BordersGraphStorageBuilderTest.java b/openrouteservice/src/test/java/heigit/ors/routing/graphhopper/extensions/storages/builders/BordersGraphStorageBuilderTest.java index 1fa0624f2a..68955c8292 100644 --- a/openrouteservice/src/test/java/heigit/ors/routing/graphhopper/extensions/storages/builders/BordersGraphStorageBuilderTest.java +++ b/openrouteservice/src/test/java/heigit/ors/routing/graphhopper/extensions/storages/builders/BordersGraphStorageBuilderTest.java @@ -16,7 +16,6 @@ import com.graphhopper.reader.ReaderWay; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.GeometryFactory; -import com.vividsolutions.jts.geom.LineString; import heigit.ors.routing.graphhopper.extensions.reader.borders.CountryBordersHierarchy; import heigit.ors.routing.graphhopper.extensions.reader.borders.CountryBordersPolygon; import heigit.ors.routing.graphhopper.extensions.reader.borders.CountryBordersReader; @@ -118,8 +117,10 @@ public void TestProcessWay() { _builder.processWay(rw2, cs2, null); - Assert.assertFalse(rw2.hasTag("country1")); - Assert.assertFalse(rw2.hasTag("country2")); +// Assert.assertFalse(rw2.hasTag("country1")); +// Assert.assertFalse(rw2.hasTag("country2")); + Assert.assertEquals("c1", rw2.getTag("country1")); + Assert.assertEquals("c1", rw2.getTag("country2")); } /**