From 8d560ce0e5f87adb235860664fa4a0189a2bf8fb Mon Sep 17 00:00:00 2001 From: Julian Psotta Date: Mon, 20 May 2019 18:29:56 +0200 Subject: [PATCH] feature-#349 list traversal countries --- CHANGELOG.md | 1 + .../ors/v2/services/routing/ResultTest.java | 170 +++++++++++++++++- .../ors/api/requests/common/APIEnums.java | 3 +- .../requests/routing/RouteRequestHandler.java | 29 ++- .../ors/routing/RouteExtraInfoFlag.java | 4 + .../heigit/ors/routing/RoutingProfile.java | 59 +++++- .../ors/routing/RoutingProfileManager.java | 13 +- .../heigit/ors/routing/RoutingRequest.java | 9 + .../extensions/ORSGraphHopper.java | 12 ++ .../graphhopper/extensions/ORSOSMReader.java | 10 +- .../reader/borders/CountryBordersReader.java | 14 ++ .../storages/BordersGraphStorage.java | 46 ++--- .../builders/BordersGraphStorageBuilder.java | 86 +++++---- .../pathprocessors/ExtraInfoProcessor.java | 66 ++++++- .../api/requests/routing/APIEnumsTest.java | 2 + .../BordersGraphStorageBuilderTest.java | 5 +- 16 files changed, 442 insertions(+), 87 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a3ce3d80f..4c619a1bf0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Makes docker and docker-compose deployment of openrouteservice more customizable (Issue #434) - Add the possibility to predefine standard maximum search radii in general and for each used profile in the config file (Issue #418) - Added a gpx schema validator into the api-tests, testing all gpx outputs while fixing the bug from (#496) +- Added information for countries a route traverses (#349) ### Fixed - isochrone reachfactor gives now more realistic results (#325) - v2 isochrones now respects max_locations in app.config (#482) 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..022fdea0bc 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 @@ -43,6 +43,7 @@ import java.util.List; import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.is; @@ -82,6 +83,7 @@ public ResultTest() { extraInfo.put("surface"); extraInfo.put("suitability"); extraInfo.put("steepness"); + extraInfo.put("countryinfo"); addParameter("extra_info", extraInfo); addParameter("preference", "fastest"); @@ -838,7 +840,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 +851,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 +2410,171 @@ public void testPreferQuiet() { .statusCode(200); } + @Test + public void testCountryTraversalNoBorderCrossing(){ + JSONObject body = new JSONObject(); + JSONArray noBorderCrossing = new JSONArray(); + JSONArray coord = new JSONArray(); + coord.put(8.692256212234497); + coord.put(49.405004518240005); + noBorderCrossing.put(coord); + coord = new JSONArray(); + coord.put(8.689970970153809); + coord.put(49.40532565875338); + noBorderCrossing.put(coord); + body.put("coordinates", noBorderCrossing); + JSONArray extraInfo = new JSONArray(); + extraInfo.put("countryinfo"); + body.put("extra_info", extraInfo); + + // 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); + } + + @Test + public void testCountryTraversalOuterBorder() { + JSONObject body = new JSONObject(); + JSONArray outerBorder = new JSONArray(); + JSONArray coord = new JSONArray(); + coord.put(8.688002); + coord.put(49.392946); + outerBorder.put(coord); + coord = new JSONArray(); + coord.put(8.687809); + coord.put(49.39472); + outerBorder.put(coord); + body.put("coordinates", outerBorder); + JSONArray extraInfo = new JSONArray(); + extraInfo.put("countryinfo"); + body.put("extra_info", extraInfo); + + // Outside of any borders + 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", empty()) + .statusCode(200); + } + + @Test + public void testCoutryTraversalCloseToBorder() { + JSONObject body = new JSONObject(); + JSONArray closeToBorder = new JSONArray(); + JSONArray coord = new JSONArray(); + coord.put(8.685869872570038); + coord.put(49.402674441283786); + closeToBorder.put(coord); + coord = new JSONArray(); + coord.put(8.687363862991333); + coord.put(49.4027128404518); + closeToBorder.put(coord); + body.put("coordinates", closeToBorder); + JSONArray extraInfo = new JSONArray(); + extraInfo.put("countryinfo"); + body.put("extra_info", extraInfo); + + // Close to a 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(3)) + .body("routes[0].extras.countryinfo.summary[0].value", is(3.0f)) + .body("routes[0].extras.countryinfo.summary[0].distance", is(108.0f)) + .body("routes[0].extras.countryinfo.summary[0].amount", is(100.0f)) + .statusCode(200); + } + + @Test + public void testCountryTraversalWithBorderCrossing() { + JSONObject body = new JSONObject(); + JSONArray borderCrossing = new JSONArray(); + JSONArray coord = new JSONArray(); + coord.put(8.685046434402466); + coord.put(49.40267269586634); + borderCrossing.put(coord); + coord = new JSONArray(); + coord.put(8.687556982040405); + coord.put(49.40271458586781); + borderCrossing.put(coord); + body.put("coordinates", borderCrossing); + JSONArray extraInfo = new JSONArray(); + extraInfo.put("countryinfo"); + body.put("extra_info", extraInfo); + + // With 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); + } + 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/api/requests/routing/RouteRequestHandler.java b/openrouteservice/src/main/java/heigit/ors/api/requests/routing/RouteRequestHandler.java index 05434d5d3b..3e040da109 100644 --- a/openrouteservice/src/main/java/heigit/ors/api/requests/routing/RouteRequestHandler.java +++ b/openrouteservice/src/main/java/heigit/ors/api/requests/routing/RouteRequestHandler.java @@ -23,10 +23,26 @@ import heigit.ors.api.requests.common.GenericHandler; import heigit.ors.common.DistanceUnit; import heigit.ors.common.StatusCode; -import heigit.ors.exceptions.*; +import heigit.ors.exceptions.EmptyElementException; +import heigit.ors.exceptions.IncompatibleParameterException; +import heigit.ors.exceptions.InternalServerException; +import heigit.ors.exceptions.ParameterOutOfRangeException; +import heigit.ors.exceptions.ParameterValueException; +import heigit.ors.exceptions.StatusCodeException; +import heigit.ors.exceptions.UnknownParameterValueException; import heigit.ors.geojson.GeometryJSON; import heigit.ors.localization.LocalizationManager; -import heigit.ors.routing.*; +import heigit.ors.routing.AvoidFeatureFlags; +import heigit.ors.routing.RouteExtraInfoFlag; +import heigit.ors.routing.RouteInstructionsFormat; +import heigit.ors.routing.RouteResult; +import heigit.ors.routing.RouteSearchParameters; +import heigit.ors.routing.RoutingErrorCodes; +import heigit.ors.routing.RoutingProfileManager; +import heigit.ors.routing.RoutingProfileType; +import heigit.ors.routing.RoutingRequest; +import heigit.ors.routing.WayPointBearing; +import heigit.ors.routing.WeightingMethod; import heigit.ors.routing.graphhopper.extensions.reader.borders.CountryBordersReader; import heigit.ors.routing.pathprocessors.BordersExtractor; import heigit.ors.util.DistanceUnitUtil; @@ -80,9 +96,14 @@ public RoutingRequest convertRouteRequest(RouteRequest request) throws StatusCo if (request.hasAttributes()) routingRequest.setAttributes(convertAttributes(request.getAttributes())); - if (request.hasExtraInfo()) + if (request.hasExtraInfo()){ routingRequest.setExtraInfo(convertExtraInfo(request.getExtraInfo())); - + for (APIEnums.ExtraInfo extra: request.getExtraInfo()) { + if (extra.compareTo(APIEnums.ExtraInfo.COUNTRY_INFO) == 0) { + routingRequest.setIncludeCountryInfo(true); + } + } + } if (request.hasLanguage()) routingRequest.setLanguage(convertLanguage(request.getLanguage())); 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/RoutingProfile.java b/openrouteservice/src/main/java/heigit/ors/routing/RoutingProfile.java index b3e293f36b..ebd05589f5 100644 --- a/openrouteservice/src/main/java/heigit/ors/routing/RoutingProfile.java +++ b/openrouteservice/src/main/java/heigit/ors/routing/RoutingProfile.java @@ -16,34 +16,62 @@ import com.graphhopper.GHRequest; import com.graphhopper.GHResponse; import com.graphhopper.GraphHopper; -import com.graphhopper.routing.util.*; +import com.graphhopper.routing.util.DefaultEdgeFilter; +import com.graphhopper.routing.util.EdgeFilter; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.FlagEncoder; +import com.graphhopper.routing.util.HintsMap; import com.graphhopper.routing.weighting.Weighting; -import com.graphhopper.storage.*; -import com.graphhopper.util.*; +import com.graphhopper.storage.CHGraph; +import com.graphhopper.storage.Graph; +import com.graphhopper.storage.GraphHopperStorage; +import com.graphhopper.storage.GraphStorage; +import com.graphhopper.storage.StorableProperties; +import com.graphhopper.util.CmdArgs; +import com.graphhopper.util.EdgeIteratorState; +import com.graphhopper.util.Helper; +import com.graphhopper.util.PMap; +import com.graphhopper.util.PointList; import com.graphhopper.util.shapes.BBox; import com.graphhopper.util.shapes.GHPoint; import com.typesafe.config.Config; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; -import heigit.ors.common.TravelRangeType; import heigit.ors.exceptions.InternalServerException; import heigit.ors.exceptions.StatusCodeException; -import heigit.ors.isochrones.*; +import heigit.ors.isochrones.Isochrone; +import heigit.ors.isochrones.IsochroneMap; +import heigit.ors.isochrones.IsochroneMapBuilderFactory; +import heigit.ors.isochrones.IsochroneSearchParameters; +import heigit.ors.isochrones.IsochronesErrorCodes; import heigit.ors.isochrones.statistics.StatisticsProvider; import heigit.ors.isochrones.statistics.StatisticsProviderConfiguration; import heigit.ors.isochrones.statistics.StatisticsProviderFactory; import heigit.ors.mapmatching.MapMatcher; import heigit.ors.mapmatching.RouteSegmentInfo; import heigit.ors.mapmatching.hmm.HiddenMarkovMapMatcher; -import heigit.ors.matrix.*; +import heigit.ors.matrix.MatrixErrorCodes; +import heigit.ors.matrix.MatrixRequest; +import heigit.ors.matrix.MatrixResult; +import heigit.ors.matrix.MatrixSearchContext; +import heigit.ors.matrix.MatrixSearchContextBuilder; import heigit.ors.matrix.algorithms.MatrixAlgorithm; import heigit.ors.matrix.algorithms.MatrixAlgorithmFactory; import heigit.ors.routing.configuration.RouteProfileConfiguration; -import heigit.ors.routing.graphhopper.extensions.*; +import heigit.ors.routing.graphhopper.extensions.GraphProcessContext; +import heigit.ors.routing.graphhopper.extensions.HeavyVehicleAttributes; +import heigit.ors.routing.graphhopper.extensions.ORSDefaultFlagEncoderFactory; +import heigit.ors.routing.graphhopper.extensions.ORSGraphHopper; +import heigit.ors.routing.graphhopper.extensions.ORSGraphStorageFactory; +import heigit.ors.routing.graphhopper.extensions.ORSWeightingFactory; import heigit.ors.routing.graphhopper.extensions.edgefilters.*; import heigit.ors.routing.graphhopper.extensions.storages.GraphStorageUtils; -import heigit.ors.routing.parameters.*; +import heigit.ors.routing.graphhopper.extensions.storages.builders.BordersGraphStorageBuilder; +import heigit.ors.routing.graphhopper.extensions.storages.builders.GraphStorageBuilder; +import heigit.ors.routing.parameters.ProfileParameters; +import heigit.ors.routing.parameters.VehicleParameters; +import heigit.ors.routing.parameters.WheelchairParameters; import heigit.ors.routing.traffic.RealTrafficDataProvider; import heigit.ors.routing.traffic.TrafficEdgeAnnotator; import heigit.ors.services.isochrones.IsochronesServiceSettings; @@ -60,7 +88,13 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.text.SimpleDateFormat; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; /** * This class generates {@link RoutingProfile} classes and is used by mostly all service classes e.g. @@ -150,6 +184,13 @@ public static ORSGraphHopper initGraphHopper(String osmFile, RouteProfileConfigu gh.importOrLoad(); + // Set the general country builder object for general use + for (GraphStorageBuilder builder : gpc.getStorageBuilders()) { + if (builder.getName().equals(BordersGraphStorageBuilder.builderName)) { + gh.setGeneralCbReader(((BordersGraphStorageBuilder) builder).getCbReader()); + } + } + if (LOGGER.isInfoEnabled()) { EncodingManager encodingMgr = gh.getEncodingManager(); GraphHopperStorage ghStorage = gh.getGraphHopperStorage(); diff --git a/openrouteservice/src/main/java/heigit/ors/routing/RoutingProfileManager.java b/openrouteservice/src/main/java/heigit/ors/routing/RoutingProfileManager.java index ab3d1894c5..ca82064e34 100644 --- a/openrouteservice/src/main/java/heigit/ors/routing/RoutingProfileManager.java +++ b/openrouteservice/src/main/java/heigit/ors/routing/RoutingProfileManager.java @@ -46,7 +46,12 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; -import java.util.concurrent.*; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorCompletionService; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; public class RoutingProfileManager { private static final Logger LOGGER = Logger.getLogger(RoutingProfileManager.class.getName()); @@ -340,8 +345,10 @@ public RouteResult computeRoute(RoutingRequest req) throws Exception { RoutingProfile rp = getRouteProfile(req, false); RouteSearchParameters searchParams = req.getSearchParameters(); PathProcessor pathProcessor = null; - - pathProcessor = new ExtraInfoProcessor(rp.getGraphhopper(), req); + if (req.getIncludeCountryInfo()) + pathProcessor = new ExtraInfoProcessor(rp.getGraphhopper(), req, rp.getGraphhopper().getGeneralCbReader()); + else + pathProcessor = new ExtraInfoProcessor(rp.getGraphhopper(), req); Coordinate[] coords = req.getCoordinates(); Coordinate c0 = coords[0]; diff --git a/openrouteservice/src/main/java/heigit/ors/routing/RoutingRequest.java b/openrouteservice/src/main/java/heigit/ors/routing/RoutingRequest.java index 045e67489d..87614e16c6 100644 --- a/openrouteservice/src/main/java/heigit/ors/routing/RoutingRequest.java +++ b/openrouteservice/src/main/java/heigit/ors/routing/RoutingRequest.java @@ -43,6 +43,7 @@ public class RoutingRequest extends ServiceRequest private boolean _continueStraight = false; private Boolean _suppressWarnings = false; private List _skipSegments = new ArrayList<>(); + private boolean _includeCountryInfo = false; public RoutingRequest() { @@ -213,4 +214,12 @@ public List getSkipSegments() { public void setSkipSegments(List skipSegments) { _skipSegments = skipSegments; } + + public boolean getIncludeCountryInfo() { + return _includeCountryInfo; + } + + public void setIncludeCountryInfo(boolean includeCountryInfo) { + this._includeCountryInfo = includeCountryInfo; + } } diff --git a/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/ORSGraphHopper.java b/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/ORSGraphHopper.java index 91d6e6ae52..6253b7d2e9 100644 --- a/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/ORSGraphHopper.java +++ b/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/ORSGraphHopper.java @@ -46,6 +46,7 @@ import heigit.ors.routing.graphhopper.extensions.edgefilters.core.AvoidFeaturesCoreEdgeFilter; import heigit.ors.routing.graphhopper.extensions.edgefilters.core.HeavyVehicleCoreEdgeFilter; import heigit.ors.routing.graphhopper.extensions.edgefilters.core.WheelchairCoreEdgeFilter; +import heigit.ors.routing.graphhopper.extensions.reader.borders.CountryBordersReader; import heigit.ors.routing.graphhopper.extensions.util.ORSParameters; import heigit.ors.routing.graphhopper.extensions.util.ORSParameters.Core; import heigit.ors.util.CoordTools; @@ -65,6 +66,7 @@ public class ORSGraphHopper extends GraphHopper { private final Logger logger = LoggerFactory.getLogger(this.getClass()); private GraphProcessContext _procCntx; + private CountryBordersReader _generalCbReader; private HashMap> osmId2EdgeIds; // one osm id can correspond to multiple edges private HashMap tmcEdges; @@ -624,4 +626,14 @@ protected void loadOrPrepareCoreLM() { ghStorage.getProperties().put(ORSParameters.CoreLandmark.PREPARE + "done", true); } } + + public CountryBordersReader getGeneralCbReader() { + return _generalCbReader; + } + + public void setGeneralCbReader(CountryBordersReader _generalCbReader) { + this._generalCbReader = _generalCbReader; + } + + } \ No newline at end of file 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..206499c0d9 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; @@ -44,6 +43,8 @@ public class BordersGraphStorageBuilder extends AbstractGraphStorageBuilder { private GeometryFactory gf; + public static String builderName = "Borders"; + public BordersGraphStorageBuilder() { gf = new GeometryFactory(); } @@ -119,13 +120,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 +142,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. @@ -297,4 +301,8 @@ public String[] findBorderCrossing(Coordinate[] coords) { return names.toArray(new String[names.size()]); } + + public CountryBordersReader getCbReader() { + return cbReader; + } } 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..cdde23b938 100644 --- a/openrouteservice/src/main/java/heigit/ors/routing/pathprocessors/ExtraInfoProcessor.java +++ b/openrouteservice/src/main/java/heigit/ors/routing/pathprocessors/ExtraInfoProcessor.java @@ -22,17 +22,36 @@ import com.graphhopper.storage.GraphExtension; import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.PointList; -import heigit.ors.routing.*; +import com.vividsolutions.jts.geom.Coordinate; +import heigit.ors.routing.RouteExtraInfo; +import heigit.ors.routing.RouteExtraInfoFlag; +import heigit.ors.routing.RoutingProfileType; +import heigit.ors.routing.RoutingRequest; import heigit.ors.routing.graphhopper.extensions.ORSGraphHopper; -import heigit.ors.routing.graphhopper.extensions.storages.*; +import heigit.ors.routing.graphhopper.extensions.reader.borders.CountryBordersPolygon; +import heigit.ors.routing.graphhopper.extensions.reader.borders.CountryBordersReader; +import heigit.ors.routing.graphhopper.extensions.storages.BordersGraphStorage; +import heigit.ors.routing.graphhopper.extensions.storages.GraphStorageUtils; +import heigit.ors.routing.graphhopper.extensions.storages.GreenIndexGraphStorage; +import heigit.ors.routing.graphhopper.extensions.storages.HillIndexGraphStorage; +import heigit.ors.routing.graphhopper.extensions.storages.NoiseIndexGraphStorage; +import heigit.ors.routing.graphhopper.extensions.storages.OsmIdGraphStorage; +import heigit.ors.routing.graphhopper.extensions.storages.RoadAccessRestrictionsGraphStorage; +import heigit.ors.routing.graphhopper.extensions.storages.TollwaysGraphStorage; +import heigit.ors.routing.graphhopper.extensions.storages.TrailDifficultyScaleGraphStorage; +import heigit.ors.routing.graphhopper.extensions.storages.WarningGraphExtension; +import heigit.ors.routing.graphhopper.extensions.storages.WayCategoryGraphStorage; +import heigit.ors.routing.graphhopper.extensions.storages.WaySurfaceTypeGraphStorage; import heigit.ors.routing.util.ElevationSmoother; import heigit.ors.routing.util.extrainfobuilders.RouteExtraInfoBuilder; 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 +61,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 +100,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; @@ -88,6 +111,13 @@ public class ExtraInfoProcessor extends PathProcessor { private byte[] buffer; private boolean _lastSegment; + private CountryBordersReader cbreader; + + public ExtraInfoProcessor(ORSGraphHopper graphhopper, RoutingRequest req, CountryBordersReader cbReader) throws Exception { + this(graphhopper, req); + this.cbreader = cbReader; + } + public ExtraInfoProcessor(ORSGraphHopper graphHopper, RoutingRequest req) throws Exception { _profileType = req.getSearchParameters().getProfileType(); @@ -205,6 +235,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 +313,8 @@ public List getExtras() extras.add(_osmIdInfo); if (_roadAccessRestrictionsInfo != null) extras.add(_roadAccessRestrictionsInfo); + if (_countryTraversalInfo != null) + extras.add(_countryTraversalInfo); return extras; } @@ -283,6 +322,25 @@ 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 && cbreader != null) { + short country1 = _extCountryTraversalInfo.getEdgeValue(EdgeIteratorStateHelper.getOriginalEdge(edge), BordersGraphStorage.Property.START); + short country2 = _extCountryTraversalInfo.getEdgeValue(EdgeIteratorStateHelper.getOriginalEdge(edge), BordersGraphStorage.Property.END); + // This check will correct the countries of an edge if the starting coordinate of the route lies in a different country than the start of the edge. + if (country1 != country2 && geom.getSize() > 0) { + Coordinate coordinate = new Coordinate(); + coordinate.x = geom.getLon(0); + coordinate.y = geom.getLat(0); + CountryBordersPolygon[] countries = cbreader.getCountry(coordinate); + if (countries.length >= 1) { + country1 = Short.parseShort(cbreader.getId(cbreader.getCountry(coordinate)[0].getName())); + } + } + if (_countryTraversalBuilder != null && country1 != 0) { + _countryTraversalBuilder.addSegment(country1, country1, 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/api/requests/routing/APIEnumsTest.java b/openrouteservice/src/test/java/heigit/ors/api/requests/routing/APIEnumsTest.java index ae06757194..43a566f424 100644 --- a/openrouteservice/src/test/java/heigit/ors/api/requests/routing/APIEnumsTest.java +++ b/openrouteservice/src/test/java/heigit/ors/api/requests/routing/APIEnumsTest.java @@ -33,6 +33,7 @@ public void testExtraInfoEnumCreation() throws ParameterValueException { Assert.assertEquals(APIEnums.ExtraInfo.TOLLWAYS, APIEnums.ExtraInfo.forValue("tollways")); Assert.assertEquals(APIEnums.ExtraInfo.TRAIL_DIFFICULTY, APIEnums.ExtraInfo.forValue("traildifficulty")); Assert.assertEquals(APIEnums.ExtraInfo.OSM_ID, APIEnums.ExtraInfo.forValue("osmid")); + Assert.assertEquals(APIEnums.ExtraInfo.COUNTRY_INFO, APIEnums.ExtraInfo.forValue("countryinfo")); APIEnums.ExtraInfo.forValue("invalid"); } @@ -47,6 +48,7 @@ public void testExtraInfoEnumValue() { Assert.assertEquals("tollways", APIEnums.ExtraInfo.TOLLWAYS.toString()); Assert.assertEquals("traildifficulty", APIEnums.ExtraInfo.TRAIL_DIFFICULTY.toString()); Assert.assertEquals("osmid", APIEnums.ExtraInfo.OSM_ID.toString()); + Assert.assertEquals("countryinfo", APIEnums.ExtraInfo.COUNTRY_INFO.toString()); } @Test(expected = ParameterValueException.class) 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..ce25af551d 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,8 @@ public void TestProcessWay() { _builder.processWay(rw2, cs2, null); - Assert.assertFalse(rw2.hasTag("country1")); - Assert.assertFalse(rw2.hasTag("country2")); + Assert.assertEquals("c1", rw2.getTag("country1")); + Assert.assertEquals("c1", rw2.getTag("country2")); } /**