diff --git a/.editorconfig b/.editorconfig index 30746a602df..677441984fa 100644 --- a/.editorconfig +++ b/.editorconfig @@ -19,3 +19,11 @@ ij_formatter_tags_enabled = true ij_smart_tabs = false ij_visual_guides = none ij_wrap_on_typing = false + +[*.json] +indent_size = 2 +indent_style = space + +[*.{yml,yaml}] +indent_size = 2 +indent_style = space diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cb6d6e9600f..773dd6e3b1c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,11 +2,11 @@ name: Build and Test on: push jobs: build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: - java-version: [ 20, 21-ea ] + java-version: [ 22, 23-ea ] steps: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 diff --git a/.github/workflows/publish-github-packages.yml b/.github/workflows/publish-github-packages.yml index 83be4b2b42b..d015eabf504 100644 --- a/.github/workflows/publish-github-packages.yml +++ b/.github/workflows/publish-github-packages.yml @@ -9,7 +9,7 @@ on: jobs: publish: if: github.repository_owner == 'graphhopper' - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 permissions: contents: read packages: write diff --git a/.github/workflows/publish-maven-central.yml b/.github/workflows/publish-maven-central.yml index 74779486322..de089b4892c 100644 --- a/.github/workflows/publish-maven-central.yml +++ b/.github/workflows/publish-maven-central.yml @@ -8,12 +8,12 @@ on: jobs: maven-central-publish: if: github.repository_owner == 'graphhopper' - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: - java-version: 8 + java-version: 17 distribution: temurin server-id: ossrh server-username: MAVEN_USERNAME diff --git a/CHANGELOG.md b/CHANGELOG.md index 37fcf28a06d..de0f0924fa2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,19 @@ -### 8.0 [not yet released] +### 9.0 [not yet released] + +- max_slope is now a signed decimal, see #2955 +- move sac_scale handling out of foot_access parser and made foot safer via lowering to sac_scale<2, same for hike sac_scale<5 +- removed shortest+fastest weightings, #2938 +- u_turn_costs information is no longer stored in profile. Use the TurnCostsConfig instead +- the custom models do no longer include the speed, access and priority encoded values only implicitly, see docs/migration/config-migration-08-09.md +- conditional access restriction tags are no longer considered from vehicle tag parsers and instead a car_temporal_access encoded value (similarly for bike + foot) can be used in a custom model. This fixes #2477. More details are accessible via path details named according to the OSM tags e.g. for access:conditional it is "access_conditional" (i.e. converted from OSM access:conditional). See #2863 and #2965. +- replaced (Vehicle)EncodedValueFactory and (Vehicle)TagParserFactory with ImportRegistry, #2935 +- encoded values used in custom models are added automatically, no need to add them to graph.encoded_values anymore, #2935 +- removed the ability to sort the graph (graph.do_sort) due to incomplete support, #2919 +- minor changes for import hooks, #2917 +- removed wheelchair vehicle and related parsers, with currently no complete replacement as it needs to be redone properly with a custom model +- removed deprecated PMap.put + +### 8.0 [18 Oct 2023] - access "turn"-EncodedValue of EncodingManager through separate methods, see #2884 - removed fastest weighting for public usage, use custom instead, see #2866 diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 05aa2f13342..a049fc12115 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -43,6 +43,7 @@ Here is an overview: * Janekdererste, GUI for public transit * jansoe, many improvements regarding A* algorithm, forcing direction, roundabouts etc * jansonhanson, general host config + * jessLryan, max elevation can now be negative * joe-akeem, improvements like #2158 * JohannesPelzer, improved GPX information and various other things * karussell, one of the core developers @@ -89,4 +90,4 @@ Here is an overview: ## Translations A lot people helped to create translations - thanks! -See [this spreadsheet](https://docs.google.com/spreadsheet/ccc?key=0AmukcXek0JP6dGM4R1VTV2d3TkRSUFVQakhVeVBQRHc#gid=0) +See [this spreadsheet](https://docs.google.com/spreadsheets/d/18z00Rbt6QvLIkayEV9P89vW9oU0QbTVsjRk9nz1CeFY/edit#gid=0) diff --git a/README.md b/README.md index f7542f47364..549d7df62e8 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,13 @@ Released versions of the fork follow this naming schema: `-osm ![Build Status](https://github.com/graphhopper/graphhopper/actions/workflows/build.yml/badge.svg?branch=master) -GraphHopper is a fast and memory-efficient routing engine released under Apache License 2.0. It can be used as a Java library or standalone web server to calculate the distance, time, turn-by-turn instructions and many road attributes for a route between two or more points. Beyond this "A-to-B" routing it supports ["snap to road"](README.md#Map-Matching), [Isochrone calculation](README.md#Analysis), [mobile navigation](README.md#mobile-apps) and [more](README.md#Features). GraphHopper uses OpenStreetMap and GTFS data by default and it can import [other data sources too](README.md#OpenStreetMap-Support). +GraphHopper is a fast and memory-efficient routing engine released under Apache License 2.0. +It can be used as a Java library or standalone web server to calculate the distance, time, +turn-by-turn instructions and many road attributes for a route between two or more points. +Beyond this "A-to-B" routing it supports ["snap to road"](README.md#Map-Matching), +[Isochrone calculation](README.md#Analysis), [mobile navigation](README.md#mobile-apps) and +[more](README.md#Features). GraphHopper uses OpenStreetMap and GTFS data by default and it +can import [other data sources too](README.md#OpenStreetMap-Support). # Community @@ -39,15 +45,18 @@ We even have [good first issues](https://github.com/graphhopper/graphhopper/issu To get started you can try [GraphHopper Maps](README.md#graphhopper-maps), read through our documentation and install the GraphHopper Web Service locally. -* 8.x: [documentation](https://github.com/graphhopper/graphhopper/blob/8.x/docs/index.md) - , [web service jar](https://repo1.maven.org/maven2/com/graphhopper/graphhopper-web/8.0/graphhopper-web-8.0.jar) - , [announcement](https://www.graphhopper.com/blog/2023/10/18/graphhopper-routing-engine-8-0-released/) +* 9.x: [documentation](https://github.com/graphhopper/graphhopper/blob/9.x/docs/index.md) + , [web service jar](https://repo1.maven.org/maven2/com/graphhopper/graphhopper-web/9.1/graphhopper-web-9.1.jar) + , [announcement](https://www.graphhopper.com/blog/2024/04/23/graphhopper-routing-engine-9-0-released) * unstable master: [documentation](https://github.com/graphhopper/graphhopper/blob/master/docs/index.md)
Click to see older releases * See our [changelog file](./CHANGELOG.md) for Java API Changes. +* 8.x: [documentation](https://github.com/graphhopper/graphhopper/blob/8.x/docs/index.md) + , [web service jar](https://repo1.maven.org/maven2/com/graphhopper/graphhopper-web/8.0/graphhopper-web-8.0.jar) + , [announcement](https://www.graphhopper.com/blog/2023/10/18/graphhopper-routing-engine-8-0-released/) * 7.x: [documentation](https://github.com/graphhopper/graphhopper/blob/7.x/docs/index.md) , [web service jar](https://repo1.maven.org/maven2/com/graphhopper/graphhopper-web/7.0/graphhopper-web-7.0.jar) , [announcement](https://www.graphhopper.com/blog/2023/03/14/graphhopper-routing-engine-7-0-released/) @@ -102,7 +111,7 @@ To get started you can try [GraphHopper Maps](README.md#graphhopper-maps), read ## Installation -To install the [GraphHopper Maps](https://graphhopper.com/maps/) UI and the web service locally you [need a JVM](https://adoptium.net) (>= Java 8) and do: +To install the [GraphHopper Maps](https://graphhopper.com/maps/) UI and the web service locally you [need a JVM](https://adoptium.net) (>= Java 17) and do: ```bash wget https://repo1.maven.org/maven2/com/graphhopper/graphhopper-web/8.0/graphhopper-web-8.0.jar https://raw.githubusercontent.com/graphhopper/graphhopper/8.x/config-example.yml http://download.geofabrik.de/europe/germany/berlin-latest.osm.pbf @@ -253,7 +262,7 @@ Here is a list of the more detailed features: * OpenStreetMap integration: stores and considers road type, speed limit, the surface, barriers, access restrictions, ferries, [conditional access restrictions](https://github.com/graphhopper/graphhopper/pull/621), ... * GraphHopper is fast. And with the so called "Contraction Hierarchies" it can be even faster (enabled by default). * Memory efficient data structures, algorithms and [the low and high level API](./docs/core/low-level-api.md) is tuned towards ease of use and efficiency - * Pre-built routing profiles: car, bike, racing bike, mountain bike, foot, hike, motorcycle, wheelchair, ... + * Pre-built routing profiles: car, bike, racing bike, mountain bike, foot, hike, motorcycle, ... * [Customization of these profiles](./docs/core/profiles.md#custom-profiles) are possible and e.g. get truck routing or support for cargo bikes and [many other changes](https://www.graphhopper.com/blog/2020/05/31/examples-for-customizable-routing/) * Provides a powerful [web API](./docs/web/api-doc.md) that exposes the data from OpenStreetMap and allows customizing the vehicle profiles per request. With JavaScript and Java clients. * Does [map matching](./map-matching) diff --git a/appveyor.yml b/appveyor.yml index 6e39e4f4ecb..b74cca4d5a6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,7 +5,7 @@ image: Visual Studio 2022 environment: MAVEN_OPTS: -Xmx1g JAVA_OPTS: -Xmx1g - JAVA_HOME: C:\Program Files\Java\jdk1.8.0 + JAVA_HOME: C:\Program Files\Java\jdk17 install: - java -version - mvn --version @@ -20,4 +20,4 @@ cache: - C:\maven\ - C:\Users\appveyor\.m2 artifacts: -- path: web/target/*.jar \ No newline at end of file +- path: web/target/*.jar diff --git a/benchmark/benchmark.sh b/benchmark/benchmark.sh index cdbc4c4d71f..a063553032e 100755 --- a/benchmark/benchmark.sh +++ b/benchmark/benchmark.sh @@ -18,8 +18,8 @@ set -o xtrace defaultGraphDir=measurements/ defaultResultsDir=measurements/results/$(date '+%d-%m-%Y-%s%N')/ defaultSummaryDir=measurements/ -defaultSmallMap=core/files/andorra.osm.pbf -defaultBigMap=core/files/andorra.osm.pbf +defaultSmallMap=map-matching/files/leipzig_germany.osm.pbf +defaultBigMap=map-matching/files/leipzig_germany.osm.pbf defaultUseMeasurementTimeAsRefTime=false GRAPH_DIR=${1:-$defaultGraphDir} @@ -105,7 +105,7 @@ measurement.repeats=1 \ measurement.run_slow_routing=false \ measurement.weighting=custom \ measurement.custom_model_file=benchmark/very_custom.json \ -graph.encoded_values=max_width,max_height,toll,hazmat \ +graph.encoded_values=max_width,max_height,toll,hazmat,road_access,road_class \ measurement.ch.node=true \ measurement.ch.edge=false \ measurement.lm=true \ diff --git a/benchmark/very_custom.json b/benchmark/very_custom.json index ddd48ba0772..55a2d54b62f 100644 --- a/benchmark/very_custom.json +++ b/benchmark/very_custom.json @@ -1,11 +1,13 @@ // this is a heavily customized model used to benchmark routing with a custom weighting { "speed": [ + { "if": "true", "limit_to": "car_average_speed" }, { "if": "road_class == MOTORWAY", "multiply_by": "0.85" }, { "else_if": "road_class == PRIMARY", "multiply_by": "0.9" }, { "if": "true", "limit_to": "110" } ], "priority": [ + { "if": "!car_access", "multiply_by": "0" }, { "if": "road_access == PRIVATE", "multiply_by": "0" }, { "if": "max_height < 3.8", "multiply_by": "0" }, { "if": "max_width < 2.5", "multiply_by": "0" }, @@ -14,4 +16,4 @@ { "if": "toll != NO", "multiply_by": "0.5" }, { "if": "hazmat == NO", "multiply_by": "0" } ] -} \ No newline at end of file +} diff --git a/client-hc/pom.xml b/client-hc/pom.xml index 5683b9c9977..e7994913d6e 100644 --- a/client-hc/pom.xml +++ b/client-hc/pom.xml @@ -22,14 +22,14 @@ 4.0.0 directions-api-client-hc - 8.0-osm-reader-callbacks + 9.1-osm-reader-callbacks-SNAPSHOT jar GraphHopper Directions API hand-crafted Java Client. com.graphhopper graphhopper-parent - 8.0-osm-reader-callbacks + 9.1-osm-reader-callbacks-SNAPSHOT diff --git a/client-hc/src/main/java/com/graphhopper/api/GHMatrixAbstractRequester.java b/client-hc/src/main/java/com/graphhopper/api/GHMatrixAbstractRequester.java index fe9e2297a52..273185d4d78 100644 --- a/client-hc/src/main/java/com/graphhopper/api/GHMatrixAbstractRequester.java +++ b/client-hc/src/main/java/com/graphhopper/api/GHMatrixAbstractRequester.java @@ -23,10 +23,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.graphhopper.util.Helper; import com.graphhopper.util.shapes.GHPoint; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.ResponseBody; +import okhttp3.*; import java.io.IOException; import java.util.*; @@ -306,7 +303,10 @@ protected String buildURLNoHints(String path, GHMRequest ghRequest) { return url; } - protected String postJson(String url, JsonNode data) throws IOException { + protected record JsonResult(String body, int statusCode, Map> headers) { + } + + protected JsonResult 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)); builder.header(X_GH_CLIENT_VERSION, Version.GH_VERSION_FROM_MAVEN); @@ -316,8 +316,9 @@ protected String postJson(String url, JsonNode data) throws IOException { Request okRequest = builder.build(); ResponseBody body = null; try { - body = getDownloader().newCall(okRequest).execute().body(); - return body.string(); + Response rsp = getDownloader().newCall(okRequest).execute(); + body = rsp.body(); + return new JsonResult(body.string(), rsp.code(), rsp.headers().toMultimap()); } finally { Helper.close(body); } diff --git a/client-hc/src/main/java/com/graphhopper/api/GHMatrixBatchRequester.java b/client-hc/src/main/java/com/graphhopper/api/GHMatrixBatchRequester.java index 2833b682a2e..e3adaba7d18 100644 --- a/client-hc/src/main/java/com/graphhopper/api/GHMatrixBatchRequester.java +++ b/client-hc/src/main/java/com/graphhopper/api/GHMatrixBatchRequester.java @@ -18,10 +18,11 @@ package com.graphhopper.api; import com.fasterxml.jackson.databind.JsonNode; -import com.graphhopper.jackson.ResponsePathDeserializer; +import com.graphhopper.jackson.ResponsePathDeserializerHelper; import com.graphhopper.util.Helper; import okhttp3.OkHttpClient; import okhttp3.Request; +import okhttp3.Response; import okhttp3.ResponseBody; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -84,20 +85,22 @@ public MatrixResponse route(GHMRequest ghRequest) { withTimes, withDistances, withWeights); try { String postUrl = buildURLNoHints("/calculate", ghRequest); - String postResponseStr = postJson(postUrl, requestJson); + JsonResult jsonResult = postJson(postUrl, requestJson); + matrixResponse.setHeaders(jsonResult.headers()); boolean debug = ghRequest.getHints().getBool("debug", false); if (debug) { - logger.info("POST URL:" + postUrl + ", request:" + requestJson + ", response: " + postResponseStr); + logger.info("POST URL:" + postUrl + ", request:" + requestJson + ", response: " + jsonResult); } - JsonNode responseJson = fromStringToJSON(postUrl, postResponseStr); + JsonNode responseJson = fromStringToJSON(postUrl, jsonResult.body()); if (responseJson.has("message")) { - matrixResponse.addErrors(ResponsePathDeserializer.readErrors(objectMapper, responseJson)); + matrixResponse.setStatusCode(jsonResult.statusCode()); + matrixResponse.addErrors(ResponsePathDeserializerHelper.readErrors(objectMapper, responseJson)); return matrixResponse; } if (!responseJson.has("job_id")) { throw new IllegalStateException("Response should contain job_id but was " - + postResponseStr + ", json:" + requestJson + ",url:" + postUrl); + + jsonResult + ", request:" + requestJson + ",url:" + postUrl); } final String id = responseJson.get("job_id").asText(); @@ -109,19 +112,20 @@ public MatrixResponse route(GHMRequest ghRequest) { } String getUrl = buildURLNoHints("/solution/" + id, ghRequest); - String getResponseStr; + JsonResult rsp; try { - getResponseStr = getJson(getUrl); + rsp = getJson(getUrl); } catch (SocketTimeoutException ex) { // if timeout exception try once again: - getResponseStr = getJson(getUrl); + rsp = getJson(getUrl); } - JsonNode getResponseJson = fromStringToJSON(getUrl, getResponseStr); + JsonNode getResponseJson = fromStringToJSON(getUrl, rsp.body()); if (debug) { - logger.info(i + " GET URL:" + getUrl + ", response: " + getResponseStr); + logger.info(i + " GET URL:" + getUrl + ", response: " + rsp); } - matrixResponse.addErrors(ResponsePathDeserializer.readErrors(objectMapper, getResponseJson)); + matrixResponse.addErrors(ResponsePathDeserializerHelper.readErrors(objectMapper, getResponseJson)); + matrixResponse.setStatusCode(rsp.statusCode()); if (matrixResponse.hasErrors()) { break; } @@ -155,14 +159,15 @@ public MatrixResponse route(GHMRequest ghRequest) { return matrixResponse; } - protected String getJson(String url) throws IOException { + protected JsonResult getJson(String url) throws IOException { Request okRequest = new Request.Builder().url(url) .header(X_GH_CLIENT_VERSION, GH_VERSION_FROM_MAVEN) .build(); ResponseBody body = null; try { - body = getDownloader().newCall(okRequest).execute().body(); - return body.string(); + Response rsp = getDownloader().newCall(okRequest).execute(); + body = rsp.body(); + return new JsonResult(body.string(), rsp.code(), rsp.headers().toMultimap()); } finally { Helper.close(body); } diff --git a/client-hc/src/main/java/com/graphhopper/api/GHMatrixSyncRequester.java b/client-hc/src/main/java/com/graphhopper/api/GHMatrixSyncRequester.java index 08cddc96538..ccae9c4bb4f 100644 --- a/client-hc/src/main/java/com/graphhopper/api/GHMatrixSyncRequester.java +++ b/client-hc/src/main/java/com/graphhopper/api/GHMatrixSyncRequester.java @@ -1,7 +1,7 @@ package com.graphhopper.api; import com.fasterxml.jackson.databind.JsonNode; -import com.graphhopper.jackson.ResponsePathDeserializer; +import com.graphhopper.jackson.ResponsePathDeserializerHelper; import okhttp3.OkHttpClient; import java.io.IOException; @@ -40,13 +40,16 @@ public MatrixResponse route(GHMRequest ghRequest) { try { String postUrl = buildURLNoHints("", ghRequest); - JsonNode responseJson = fromStringToJSON(postUrl, postJson(postUrl, requestJson)); + JsonResult jsonResult = postJson(postUrl, requestJson); + JsonNode responseJson = fromStringToJSON(postUrl, jsonResult.body()); + matrixResponse.setHeaders(jsonResult.headers()); + matrixResponse.setStatusCode(jsonResult.statusCode()); if (responseJson.has("message")) { - matrixResponse.addErrors(ResponsePathDeserializer.readErrors(objectMapper, responseJson)); + matrixResponse.addErrors(ResponsePathDeserializerHelper.readErrors(objectMapper, responseJson)); return matrixResponse; } - matrixResponse.addErrors(ResponsePathDeserializer.readErrors(objectMapper, responseJson)); + matrixResponse.addErrors(ResponsePathDeserializerHelper.readErrors(objectMapper, responseJson)); if (!matrixResponse.hasErrors()) matrixResponse.addErrors(readUsableEntityError(ghRequest.getOutArrays(), responseJson)); diff --git a/client-hc/src/main/java/com/graphhopper/api/GraphHopperWeb.java b/client-hc/src/main/java/com/graphhopper/api/GraphHopperWeb.java index ffd168b9d78..cadd26ed779 100644 --- a/client-hc/src/main/java/com/graphhopper/api/GraphHopperWeb.java +++ b/client-hc/src/main/java/com/graphhopper/api/GraphHopperWeb.java @@ -26,19 +26,16 @@ import com.graphhopper.GHResponse; import com.graphhopper.ResponsePath; import com.graphhopper.jackson.Jackson; -import com.graphhopper.jackson.ResponsePathDeserializer; +import com.graphhopper.jackson.ResponsePathDeserializerHelper; import com.graphhopper.util.CustomModel; import com.graphhopper.util.Helper; import com.graphhopper.util.PMap; import com.graphhopper.util.Parameters; import com.graphhopper.util.shapes.GHPoint; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.ResponseBody; +import okhttp3.*; -import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.TimeUnit; @@ -68,7 +65,7 @@ public class GraphHopperWeb { private String optimize = "false"; private boolean postRequest = true; private int maxUnzippedLength = 1000; - private final Set ignoreSet; + private final Set ignoreSetForGet; private final Set ignoreSetForPost; public static final String TIMEOUT = "timeout"; @@ -94,24 +91,26 @@ public GraphHopperWeb(String serviceUrl) { ignoreSetForPost.add("elevation"); ignoreSetForPost.add("optimize"); ignoreSetForPost.add("points_encoded"); + ignoreSetForPost.add("points_encoded_multiplier"); - ignoreSet = new HashSet<>(); - ignoreSet.add(KEY); - ignoreSet.add(CALC_POINTS); - ignoreSet.add("calcpoints"); - ignoreSet.add(INSTRUCTIONS); - ignoreSet.add("elevation"); - ignoreSet.add("optimize"); + ignoreSetForGet = new HashSet<>(); + ignoreSetForGet.add(KEY); + ignoreSetForGet.add(CALC_POINTS); + ignoreSetForGet.add("calcpoints"); + ignoreSetForGet.add(INSTRUCTIONS); + ignoreSetForGet.add("elevation"); + ignoreSetForGet.add("optimize"); // some parameters are in the request: - ignoreSet.add("algorithm"); - ignoreSet.add("locale"); - ignoreSet.add("point"); + ignoreSetForGet.add("algorithm"); + ignoreSetForGet.add("locale"); + ignoreSetForGet.add("point"); // some are special and need to be avoided - ignoreSet.add("points_encoded"); - ignoreSet.add("pointsencoded"); - ignoreSet.add("type"); + ignoreSetForGet.add("points_encoded"); + ignoreSetForGet.add("pointsencoded"); + ignoreSetForGet.add("points_encoded_multiplier"); + ignoreSetForGet.add("type"); objectMapper = Jackson.newObjectMapper(); } @@ -139,9 +138,8 @@ public GraphHopperWeb setKey(String key) { return this; } - /** - * Use new endpoint 'POST /route' instead of 'GET /route' + * If false it will use the 'GET /route' endpoint instead of the default 'POST /route'. */ public GraphHopperWeb setPostRequest(boolean postRequest) { this.postRequest = postRequest; @@ -179,8 +177,6 @@ public GraphHopperWeb setElevation(boolean withElevation) { * location is optimized according to the overall best route and returned * this way i.e. the traveling salesman problem is solved under the hood. * Note that in this case the request takes longer and costs more credits. - * For more details see: - * https://github.com/graphhopper/directions-api/blob/master/FAQ.md#what-is-one-credit */ public GraphHopperWeb setOptimize(String optimize) { this.optimize = optimize; @@ -195,24 +191,26 @@ public GHResponse route(GHRequest ghRequest) { ghRequest.getHints().remove("turn_description"); // do not include in request Request okRequest = postRequest ? createPostRequest(ghRequest) : createGetRequest(ghRequest); - rspBody = getClientForRequest(ghRequest).newCall(okRequest).execute().body(); + Response rsp = getClientForRequest(ghRequest).newCall(okRequest).execute(); + rspBody = rsp.body(); JsonNode json = objectMapper.reader().readTree(rspBody.byteStream()); GHResponse res = new GHResponse(); - res.addErrors(ResponsePathDeserializer.readErrors(objectMapper, json)); + res.addErrors(ResponsePathDeserializerHelper.readErrors(objectMapper, json)); if (res.hasErrors()) return res; JsonNode paths = json.get("paths"); for (JsonNode path : paths) { - ResponsePath altRsp = ResponsePathDeserializer.createResponsePath(objectMapper, path, tmpElevation, tmpTurnDescription); + ResponsePath altRsp = ResponsePathDeserializerHelper.createResponsePath(objectMapper, path, tmpElevation, tmpTurnDescription); res.add(altRsp); } + for (Map.Entry> entry : rsp.headers().toMultimap().entrySet()) { + res.getHints().putObject(entry.getKey(), entry.getValue()); + } JsonNode b = json.get("hints"); - PMap hints = new PMap(); - b.fields().forEachRemaining(f -> hints.putObject(f.getKey(), Helper.toObject(f.getValue().asText()))); - res.setHints(hints); + b.fields().forEachRemaining(f -> res.getHints().putObject(f.getKey(), Helper.toObject(f.getValue().asText()))); return res; @@ -240,7 +238,7 @@ Request createPostRequest(GHRequest ghRequest) { String tmpServiceURL = ghRequest.getHints().getString(SERVICE_URL, routeServiceUrl); String url = tmpServiceURL + "?"; if (!Helper.isEmpty(key)) - url += "key=" + key; + url += "key=" + encodeURL(key); ObjectNode requestJson = requestToJson(ghRequest); String body; @@ -278,6 +276,7 @@ ObjectNode requestToJson(GHRequest ghRequest) { requestJson.put("algorithm", ghRequest.getAlgorithm()); requestJson.put("points_encoded", true); + requestJson.put("points_encoded_multiplier", 1e6); requestJson.put(INSTRUCTIONS, ghRequest.getHints().getBool(INSTRUCTIONS, instructions)); requestJson.put(CALC_POINTS, ghRequest.getHints().getBool(CALC_POINTS, calcPoints)); requestJson.put("elevation", ghRequest.getHints().getBool("elevation", elevation)); @@ -329,6 +328,7 @@ Request createGetRequest(GHRequest ghRequest) { + "&type=" + type + "&instructions=" + tmpInstructions + "&points_encoded=true" + + "&points_encoded_multiplier=1000000" + "&calc_points=" + tmpCalcPoints + "&algorithm=" + ghRequest.getAlgorithm() + "&locale=" + ghRequest.getLocale().toString() @@ -336,7 +336,7 @@ Request createGetRequest(GHRequest ghRequest) { + "&optimize=" + tmpOptimize; for (String details : ghRequest.getPathDetails()) { - url += "&" + Parameters.Details.PATH_DETAILS + "=" + details; + url += "&" + Parameters.Details.PATH_DETAILS + "=" + encodeURL(details); } // append *all* point hints if at least one is not empty @@ -369,7 +369,7 @@ Request createGetRequest(GHRequest ghRequest) { String urlValue = entry.getValue().toString(); // use lower case conversion for check only! - if (ignoreSet.contains(toLowerCase(urlKey))) { + if (ignoreSetForGet.contains(toLowerCase(urlKey))) { continue; } @@ -385,16 +385,20 @@ Request createGetRequest(GHRequest ghRequest) { public String export(GHRequest ghRequest) { String str = "Creating request failed"; + ResponseBody body = null; try { if (postRequest) throw new IllegalArgumentException("GPX export only works for GET requests, make sure to use `setPostRequest(false)`"); Request okRequest = createGetRequest(ghRequest); - str = getClientForRequest(ghRequest).newCall(okRequest).execute().body().string(); + body = getClientForRequest(ghRequest).newCall(okRequest).execute().body(); + str = body.string(); return str; } catch (Exception ex) { throw new RuntimeException("Problem while fetching export " + ghRequest.getPoints() + ", error: " + ex.getMessage() + " response: " + str, ex); + } finally { + Helper.close(body); } } @@ -426,10 +430,6 @@ private ArrayNode createPointList(List list) { } private static String encodeURL(String str) { - try { - return URLEncoder.encode(str, "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } + return URLEncoder.encode(str, StandardCharsets.UTF_8); } } diff --git a/client-hc/src/main/java/com/graphhopper/api/MatrixResponse.java b/client-hc/src/main/java/com/graphhopper/api/MatrixResponse.java index fd12787ce65..63e342cec5b 100644 --- a/client-hc/src/main/java/com/graphhopper/api/MatrixResponse.java +++ b/client-hc/src/main/java/com/graphhopper/api/MatrixResponse.java @@ -1,9 +1,6 @@ package com.graphhopper.api; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Objects; +import java.util.*; /** * This class defines the response for a M-to-N requests. @@ -12,6 +9,7 @@ */ public class MatrixResponse { + private Map> headers = new HashMap<>(); private String debugInfo = ""; private final List errors = new ArrayList<>(4); private final List disconnectedPoints = new ArrayList<>(0); @@ -22,6 +20,7 @@ public class MatrixResponse { private double[][] weights = new double[0][]; private final int fromCount; private final int toCount; + private int statusCode; public MatrixResponse() { this(10, 10, true, true, true); @@ -49,6 +48,20 @@ public MatrixResponse(int fromCap, int toCap, boolean withTimes, boolean withDis throw new IllegalArgumentException("Please specify times, distances or weights that should be calculated by the matrix"); } + public void setHeaders(Map> headers) { + this.headers = headers; + } + + public Map> getHeaders() { + return headers; + } + + public String getHeader(String key, String defaultValue) { + List res = headers.get(key); + if (!res.isEmpty()) return res.get(0); + return defaultValue; + } + public void setFromRow(int row, long[] timeRow, int[] distanceRow, double[] weightRow) { if (times.length > 0) { check(timeRow.length, toCount, "to times"); @@ -176,6 +189,14 @@ public boolean hasErrors() { return !errors.isEmpty(); } + public int getStatusCode() { + return statusCode; + } + + public void setStatusCode(int statusCode) { + this.statusCode = statusCode; + } + public List getErrors() { return errors; } diff --git a/client-hc/src/test/java/com/graphhopper/api/AbstractGHMatrixWebTester.java b/client-hc/src/test/java/com/graphhopper/api/AbstractGHMatrixWebTester.java index 9553c07f071..6fc83ce6f7c 100644 --- a/client-hc/src/test/java/com/graphhopper/api/AbstractGHMatrixWebTester.java +++ b/client-hc/src/test/java/com/graphhopper/api/AbstractGHMatrixWebTester.java @@ -22,7 +22,7 @@ public abstract class AbstractGHMatrixWebTester { protected final ObjectMapper objectMapper = new ObjectMapper(); - abstract GraphHopperMatrixWeb createMatrixClient(String json) throws IOException; + abstract GraphHopperMatrixWeb createMatrixClient(String json, int errorCode) throws IOException; abstract GHMatrixAbstractRequester createRequester(String url); @@ -42,7 +42,7 @@ public static GHMRequest createRequest() { @Test public void testReadingMatrixWithError() throws IOException { String ghMatrix = readFile(new InputStreamReader(getClass().getResourceAsStream("matrix_error.json"))); - GraphHopperMatrixWeb matrixWeb = createMatrixClient(ghMatrix); + GraphHopperMatrixWeb matrixWeb = createMatrixClient(ghMatrix, 400); GHMRequest req = createRequest(); MatrixResponse rsp = matrixWeb.route(req); @@ -54,7 +54,7 @@ public void testReadingMatrixWithError() throws IOException { @Test public void testReadingWeights() throws IOException { String ghMatrix = readFile(new InputStreamReader(getClass().getResourceAsStream("matrix-weights-only.json"))); - GraphHopperMatrixWeb matrixWeb = createMatrixClient(ghMatrix); + GraphHopperMatrixWeb matrixWeb = createMatrixClient(ghMatrix, 400); GHMRequest req = createRequest(); MatrixResponse rsp = matrixWeb.route(req); @@ -72,7 +72,7 @@ public void testReadingWeights() throws IOException { @Test public void testReadingMatrixConnectionsNotFound_noFailFast() throws IOException { String ghMatrix = readFile(new InputStreamReader(getClass().getResourceAsStream("matrix-connection-not-found-fail-fast.json"))); - GraphHopperMatrixWeb matrixWeb = createMatrixClient(ghMatrix); + GraphHopperMatrixWeb matrixWeb = createMatrixClient(ghMatrix, 400); GHMRequest req = new GHMRequest(); req.setPoints(Arrays.asList( @@ -86,6 +86,7 @@ public void testReadingMatrixConnectionsNotFound_noFailFast() throws IOException MatrixResponse rsp = matrixWeb.route(req); assertFalse(rsp.hasErrors()); assertTrue(rsp.hasProblems()); + assertEquals(400, rsp.getStatusCode()); assertEquals(Double.MAX_VALUE, rsp.getWeight(0, 1), 1.e-3); assertEquals(0, rsp.getWeight(0, 0), 1.e-3); @@ -102,7 +103,7 @@ public void testReadingMatrixConnectionsNotFound_noFailFast() throws IOException @Test public void testReadingMatrixPointsNotFound_noFailFast() throws IOException { String ghMatrix = readFile(new InputStreamReader(getClass().getResourceAsStream("matrix-point-not-found-fail-fast.json"))); - GraphHopperMatrixWeb matrixWeb = createMatrixClient(ghMatrix); + GraphHopperMatrixWeb matrixWeb = createMatrixClient(ghMatrix, 400); GHMRequest req = new GHMRequest(); req.setPoints(Arrays.asList( @@ -117,6 +118,7 @@ public void testReadingMatrixPointsNotFound_noFailFast() throws IOException { MatrixResponse rsp = matrixWeb.route(req); assertFalse(rsp.hasErrors()); assertTrue(rsp.hasProblems()); + assertEquals(400, rsp.getStatusCode()); assertEquals(Double.MAX_VALUE, rsp.getWeight(1, 0), 1.e-3); assertEquals(Double.MAX_VALUE, rsp.getWeight(1, 1), 1.e-3); @@ -159,22 +161,24 @@ public void testReadingMatrixPointsNotFound_noFailFast() throws IOException { @Test public void testReadingGoogleThrowsException() throws IOException { String ghMatrix = readFile(new InputStreamReader(getClass().getResourceAsStream("google-matrix1.json"))); - GraphHopperMatrixWeb matrixWeb = createMatrixClient(ghMatrix); + GraphHopperMatrixWeb matrixWeb = createMatrixClient(ghMatrix, 400); GHMRequest req = createRequest(); MatrixResponse rsp = matrixWeb.route(req); assertTrue(rsp.hasErrors()); + assertEquals(400, rsp.getStatusCode()); } @Test public void testReadingWeights_TimesAndDistances() throws IOException { String ghMatrix = readFile(new InputStreamReader(getClass().getResourceAsStream("matrix.json"))); - GraphHopperMatrixWeb matrixWeb = createMatrixClient(ghMatrix); + GraphHopperMatrixWeb matrixWeb = createMatrixClient(ghMatrix, 200); GHMRequest req = createRequest(); req.setOutArrays(Arrays.asList("weights", "distances", "times")); MatrixResponse rsp = matrixWeb.route(req); assertFalse(rsp.hasErrors()); + assertEquals(200, rsp.getStatusCode()); assertEquals(9475., rsp.getDistance(0, 1), .1); assertEquals(9734., rsp.getDistance(1, 2), .1); diff --git a/client-hc/src/test/java/com/graphhopper/api/Examples.java b/client-hc/src/test/java/com/graphhopper/api/Examples.java index 887742d070f..34eaf3314d3 100644 --- a/client-hc/src/test/java/com/graphhopper/api/Examples.java +++ b/client-hc/src/test/java/com/graphhopper/api/Examples.java @@ -75,7 +75,7 @@ public void routing() { InstructionList il = res.getInstructions(); for (Instruction i : il) { // System.out.println(i.getName()); - + // to get the translated turn instructions you call: // System.out.println(i.getTurnDescription(null)); // Note, that you can control the language only in via the request setLocale method and cannot change it only the client side @@ -85,6 +85,9 @@ public void routing() { for (PathDetail detail : pathDetails) { // System.out.println(detail.getValue()); } + + // get headers + // System.out.println(fullRes.getHeader("x-ratelimit-remaining", "0")); } @Test @@ -106,7 +109,9 @@ public void matrix() { if (responseSymm.hasErrors()) throw new RuntimeException(responseSymm.getErrors().toString()); // get time from first to second point: - // System.out.println(response.getTime(0, 1)); + // System.out.println(responseSymm.getTime(0, 1)); + // get header information: + // System.out.println(responseSymm.getHeader("x-ratelimit-remaining", "0")); // Option 2: for an asymmetric matrix do: ghmRequest = new GHMRequest(); @@ -121,6 +126,6 @@ public void matrix() { throw new RuntimeException(responseAsymm.getErrors().toString()); // get time from first to second point: - // System.out.println(response.getTime(0, 1)); + // System.out.println(responseAsymm.getTime(0, 1)); } } diff --git a/client-hc/src/test/java/com/graphhopper/api/GHMatrixBatchTest.java b/client-hc/src/test/java/com/graphhopper/api/GHMatrixBatchTest.java index e75ea9c5ce1..3e61c7f6875 100644 --- a/client-hc/src/test/java/com/graphhopper/api/GHMatrixBatchTest.java +++ b/client-hc/src/test/java/com/graphhopper/api/GHMatrixBatchTest.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.databind.JsonNode; import java.io.IOException; +import java.util.HashMap; /** * @author Peter Karich @@ -10,19 +11,19 @@ public class GHMatrixBatchTest extends AbstractGHMatrixWebTester { @Override - GraphHopperMatrixWeb createMatrixClient(final String jsonTmp) { + GraphHopperMatrixWeb createMatrixClient(final String jsonTmp, int statusCode) { return new GraphHopperMatrixWeb(new GHMatrixBatchRequester("") { private final String json = jsonTmp; @Override - protected String postJson(String url, JsonNode data) throws IOException { - return "{\"job_id\": \"1\"}"; + protected JsonResult postJson(String url, JsonNode data) { + return new JsonResult("{\"job_id\": \"1\"}", statusCode, new HashMap<>()); } @Override - protected String getJson(String url) throws IOException { - return json; + protected JsonResult getJson(String url) { + return new JsonResult(json, statusCode, new HashMap<>()); } }.setSleepAfterGET(0)); } diff --git a/client-hc/src/test/java/com/graphhopper/api/GHMatrixSyncTest.java b/client-hc/src/test/java/com/graphhopper/api/GHMatrixSyncTest.java index 67b9a6a7558..2ded245a662 100644 --- a/client-hc/src/test/java/com/graphhopper/api/GHMatrixSyncTest.java +++ b/client-hc/src/test/java/com/graphhopper/api/GHMatrixSyncTest.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.databind.JsonNode; import java.io.IOException; +import java.util.HashMap; /** * @author Peter Karich @@ -10,7 +11,7 @@ public class GHMatrixSyncTest extends AbstractGHMatrixWebTester { @Override - GraphHopperMatrixWeb createMatrixClient(String jsonStr) throws IOException { + GraphHopperMatrixWeb createMatrixClient(String jsonStr, int errorCode) throws IOException { JsonNode json = objectMapper.readTree(jsonStr); // for test we grab the solution from the "batch json" @@ -22,8 +23,8 @@ GraphHopperMatrixWeb createMatrixClient(String jsonStr) throws IOException { return new GraphHopperMatrixWeb(new GHMatrixSyncRequester("") { @Override - protected String postJson(String url, JsonNode data) throws IOException { - return finalJsonStr; + protected JsonResult postJson(String url, JsonNode data) { + return new JsonResult(finalJsonStr, errorCode, new HashMap<>()); } }); } diff --git a/client-hc/src/test/java/com/graphhopper/api/GraphHopperWebTest.java b/client-hc/src/test/java/com/graphhopper/api/GraphHopperWebTest.java index 067883d953d..83ba11b9d31 100644 --- a/client-hc/src/test/java/com/graphhopper/api/GraphHopperWebTest.java +++ b/client-hc/src/test/java/com/graphhopper/api/GraphHopperWebTest.java @@ -43,12 +43,12 @@ public void testGetClientForRequest(boolean usePost) { public void profileIncludedAsGiven() { GraphHopperWeb hopper = new GraphHopperWeb("https://localhost:8000/route"); // no vehicle -> no vehicle - assertEquals("https://localhost:8000/route?profile=&type=json&instructions=true&points_encoded=true" + + assertEquals("https://localhost:8000/route?profile=&type=json&instructions=true&points_encoded=true&points_encoded_multiplier=1000000" + "&calc_points=true&algorithm=&locale=en_US&elevation=false&optimize=false", hopper.createGetRequest(new GHRequest()).url().toString()); // vehicle given -> vehicle used in url - assertEquals("https://localhost:8000/route?profile=my_car&type=json&instructions=true&points_encoded=true" + + assertEquals("https://localhost:8000/route?profile=my_car&type=json&instructions=true&points_encoded=true&points_encoded_multiplier=1000000" + "&calc_points=true&algorithm=&locale=en_US&elevation=false&optimize=false", hopper.createGetRequest(new GHRequest().setProfile("my_car")).url().toString()); } @@ -59,7 +59,7 @@ public void headings() { GHRequest req = new GHRequest(new GHPoint(42.509225, 1.534728), new GHPoint(42.512602, 1.551558)). setHeadings(Arrays.asList(10.0, 90.0)). setProfile("car"); - assertEquals("http://localhost:8080/route?profile=car&point=42.509225,1.534728&point=42.512602,1.551558&type=json&instructions=true&points_encoded=true" + + assertEquals("http://localhost:8080/route?profile=car&point=42.509225,1.534728&point=42.512602,1.551558&type=json&instructions=true&points_encoded=true&points_encoded_multiplier=1000000" + "&calc_points=true&algorithm=&locale=en_US&elevation=false&optimize=false&heading=10.0&heading=90.0", hopper.createGetRequest(req).url().toString()); } @@ -119,4 +119,4 @@ public void customModel() throws JsonProcessingException { CustomModel cm = objectMapper.readValue("{\"distance_influence\":null}", CustomModel.class); assertNull(cm.getDistanceInfluence()); } -} \ No newline at end of file +} diff --git a/config-example.yml b/config-example.yml index 4f929f28345..ae0bc91b42f 100644 --- a/config-example.yml +++ b/config-example.yml @@ -14,12 +14,12 @@ graphhopper: # # In general a profile consists of the following # - name (required): a unique string identifier for the profile - # - vehicle (required): refers to the `graph.vehicles` used for this profile - # - weighting (required): the weighting used for this profile like custom - # - turn_costs (true/false, default: false): whether or not turn restrictions should be applied for this profile. + # - weighting (optional): by default 'custom' + # - turn_costs (optional): + # vehicle_types: [motorcar, motor_vehicle] (vehicle types used for vehicle-specific turn restrictions) + # u_turn_costs: 60 (time-penalty for doing a u-turn in seconds) # # Depending on the above fields there are other properties that can be used, e.g. - # - u_turn_costs: 60 (time-penalty for doing a u-turn in seconds (only possible when `turn_costs: true`)). # - custom_model_files: when you specified "weighting: custom" you need to set one or more json files which are searched in # custom_models.directory or the working directory that defines the custom_model. If you want an empty model you can # set "custom_model_files: [] @@ -29,24 +29,30 @@ graphhopper: # profiles (see below). Or at least limit the number of `routing.max_visited_nodes`. profiles: - - name: car - vehicle: car - custom_model: - distance_influence: 70 -# turn_costs: true -# u_turn_costs: 60 - + - name: car +# turn_costs: +# vehicle_types: [motorcar, motor_vehicle] +# u_turn_costs: 60 + custom_model_files: [car.json] + +# - name: foot +# custom_model_files: [foot.json, foot_elevation.json] +# # - name: bike -# # to use the bike vehicle make sure to not ignore cycleways etc., see import.osm.ignored_highways below -# vehicle: bike # custom_model_files: [bike.json, bike_elevation.json] +# +# - name: racingbike +# custom_model_files: [racingbike.json, bike_elevation.json] +# +# - name: mtb +# custom_model_files: [mtb.json, bike_elevation.json] # instead of the inbuilt custom models (see ./core/src/main/resources/com/graphhopper/custom_models) # you can specify a folder where to find your own custom model files # custom_models.directory: custom_models # Speed mode: - # Its possible to speed up routing by doing a special graph preparation (Contraction Hierarchies, CH). This requires + # It's possible to speed up routing by doing a special graph preparation (Contraction Hierarchies, CH). This requires # more RAM/disk space for holding the prepared graph but also means less memory usage per request. Using the following # list you can define for which of the above routing profiles such preparation shall be performed. Note that to support # profiles with `turn_costs: true` a more elaborate preparation is required (longer preparation time and more memory @@ -66,23 +72,14 @@ graphhopper: profiles_lm: [] - #### Vehicles #### - - # The vehicle defines the base for how the routing of a profile behaves. It can be adjusted with the turn_costs=true - # option or, only for the roads vehicle, there is the transportation_mode option: - # name=mycustomvehicle,turn_costs=true,transportation_mode=MOTOR_VEHICLE - # But you should prefer to configure the turn_costs via the profile configuration. - # Other standard vehicles: foot,bike,mtb,racingbike,wheelchair - - #### Encoded Values #### # Add additional information to every edge. Used for path details (#1548) and custom models (docs/core/custom-models.md) # Default values are: road_class,road_class_link,road_environment,max_speed,road_access # More are: surface,smoothness,max_width,max_height,max_weight,max_weight_except,hgv,max_axle_load,max_length, # hazmat,hazmat_tunnel,hazmat_water,lanes,osm_way_id,toll,track_type,mtb_rating,hike_rating,horse_rating, - # country,curvature,average_slope,max_slope - # graph.encoded_values: surface,toll,track_type + # country,curvature,average_slope,max_slope,car_temporal_access,bike_temporal_access,foot_temporal_access + graph.encoded_values: car_access, car_average_speed #### Speed, hybrid and flexible mode #### @@ -132,45 +129,44 @@ graphhopper: # graph.elevation.way_point_max_distance: 10 - #### Country-dependent defaults for max speeds #### + #### Country-dependent defaults for max speeds #### - # This features sets a maximum speed in 'max_speed' encoded value if no maxspeed tag was found. It is country-dependent - # and based on several rules. See https://github.com/westnordost/osm-legal-default-speeds - # To use it uncomment the following, then enable urban density below and add 'country' to graph.encoded_values - # max_speed_calculator.enabled: true + # This features sets a maximum speed in 'max_speed' encoded value if no maxspeed tag was found. It is country-dependent + # and based on several rules. See https://github.com/westnordost/osm-legal-default-speeds + # To use it uncomment the following, then enable urban density below and add 'country' to graph.encoded_values + # max_speed_calculator.enabled: true - #### Urban density (built-up areas) #### + #### Urban density (built-up areas) #### - # This feature allows classifying roads into 'rural', 'residential' and 'city' areas (encoded value 'urban_density') - # Use 1 or more threads to enable the feature - # graph.urban_density.threads: 8 - # Use higher/lower sensitivities if too little/many roads fall into the according categories. - # Using smaller radii will speed up the classification, but only change these values if you know what you are doing. - # If you do not need the (rather slow) city classification set city_radius to zero. - # graph.urban_density.residential_radius: 400 - # graph.urban_density.residential_sensitivity: 6000 - # graph.urban_density.city_radius: 1500 - # graph.urban_density.city_sensitivity: 1000 + # This feature allows classifying roads into 'rural', 'residential' and 'city' areas (encoded value 'urban_density') + # Use 1 or more threads to enable the feature + # graph.urban_density.threads: 8 + # Use higher/lower sensitivities if too little/many roads fall into the according categories. + # Using smaller radii will speed up the classification, but only change these values if you know what you are doing. + # If you do not need the (rather slow) city classification set city_radius to zero. + # graph.urban_density.residential_radius: 400 + # graph.urban_density.residential_sensitivity: 6000 + # graph.urban_density.city_radius: 1500 + # graph.urban_density.city_sensitivity: 1000 - #### Subnetworks #### + #### Subnetworks #### - # In many cases the road network consists of independent components without any routes going in between. In - # the most simple case you can imagine an island without a bridge or ferry connection. The following parameter - # allows setting a minimum size (number of edges) for such detached components. This can be used to reduce the number - # of cases where a connection between locations might not be found. + # In many cases the road network consists of independent components without any routes going in between. In + # the most simple case you can imagine an island without a bridge or ferry connection. The following parameter + # allows setting a minimum size (number of edges) for such detached components. This can be used to reduce the number + # of cases where a connection between locations might not be found. prepare.min_network_size: 200 prepare.subnetworks.threads: 1 + #### Routing #### - #### Routing #### + # You can define the maximum visited nodes when routing. This may result in not found connections if there is no + # connection between two points within the given visited nodes. The default is Integer.MAX_VALUE. Useful for flexibility mode + # routing.max_visited_nodes: 1000000 - # You can define the maximum visited nodes when routing. This may result in not found connections if there is no - # connection between two points within the given visited nodes. The default is Integer.MAX_VALUE. Useful for flexibility mode - # routing.max_visited_nodes: 1000000 - - # The maximum time in milliseconds after which a routing request will be aborted. This has some routing algorithm + # The maximum time in milliseconds after which a routing request will be aborted. This has some routing algorithm # specific caveats, but generally it should allow the prevention of long-running requests. The default is Long.MAX_VALUE # routing.timeout_ms: 300000 @@ -198,14 +194,10 @@ graphhopper: # will write way names in the preferred language (language code as defined in ISO 639-1 or ISO 639-2): # datareader.preferred_language: en - # Sort the graph after import to make requests roughly ~10% faster. Note that this requires significantly more RAM on import. - # graph.do_sort: true - - #### Custom Areas #### # GraphHopper reads GeoJSON polygon files including their properties from this directory and makes them available - # to all tag parsers, vehicles and custom models. All GeoJSON Features require to have the "id" property. + # to all tag parsers and custom models. All GeoJSON Features require to have the "id" property. # Country borders are included automatically (see countries.geojson). # custom_areas.directory: path/to/custom_areas diff --git a/core/files/conditional-restrictions.osm.xml b/core/files/conditional-restrictions.osm.xml new file mode 100644 index 00000000000..091d3c70fad --- /dev/null +++ b/core/files/conditional-restrictions.osm.xml @@ -0,0 +1,393 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/files/update-translations.sh b/core/files/update-translations.sh index 268a0d4dfed..a4c0e16abd7 100755 --- a/core/files/update-translations.sh +++ b/core/files/update-translations.sh @@ -7,7 +7,7 @@ translations="en_US SKIP SKIP ar ast az bg bn_BN ca cs_CZ da_DK de_DE el eo es f file=$1 # You can execute the following -# curl -L 'https://docs.google.com/spreadsheets/d/10HKSFmxGVEIO92loVQetVmjXT0qpf3EA2jxuQSSYTdU/export?format=tsv&gid=0' > tmp.tsv +# curl -L 'https://docs.google.com/spreadsheets/d/e/2PACX-1vTjOxfOBVw9VvEroPw30w77XA-JCCbraf4GeL9URMgK0kjfS-YT5R8TT6PACF8O7o6fhPKMsWKFf9M-/pub?output=tsv' > tmp.tsv # ./files/update-translations.sh tmp.tsv && rm tmp.tsv INDEX=1 @@ -16,6 +16,6 @@ for tr in $translations; do if [[ "x$tr" = "xSKIP" ]]; then continue fi - echo -e '# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh\n' > $destination/$tr.txt + echo -e '# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh\n' > $destination/$tr.txt tail -n+5 "$file" | cut -s -f1,$INDEX --output-delimiter='=' >> $destination/$tr.txt done diff --git a/core/nb-configuration.xml b/core/nb-configuration.xml deleted file mode 100644 index ef9a362e2e2..00000000000 --- a/core/nb-configuration.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - false - all - - diff --git a/core/pom.xml b/core/pom.xml index 031febea294..49e45e92a72 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -5,7 +5,6 @@ graphhopper-core GraphHopper Core - 8.0-osm-reader-callbacks jar GraphHopper is a fast and memory efficient Java road routing engine @@ -14,7 +13,7 @@ com.graphhopper graphhopper-parent - 8.0-osm-reader-callbacks + 9.1-osm-reader-callbacks-SNAPSHOT @@ -86,7 +85,7 @@ de.westnordost osm-legal-default-speeds-jvm - 1.3 + 1.4 diff --git a/core/src/main/java/com/graphhopper/GraphHopper.java b/core/src/main/java/com/graphhopper/GraphHopper.java index 1c133a3c4fb..e60d409e073 100644 --- a/core/src/main/java/com/graphhopper/GraphHopper.java +++ b/core/src/main/java/com/graphhopper/GraphHopper.java @@ -39,9 +39,14 @@ import com.graphhopper.routing.subnetwork.PrepareRoutingSubnetworks.PrepareJob; import com.graphhopper.routing.util.*; import com.graphhopper.routing.util.countryrules.CountryRuleFactory; -import com.graphhopper.routing.util.parsers.*; +import com.graphhopper.routing.util.parsers.OSMBikeNetworkTagParser; +import com.graphhopper.routing.util.parsers.OSMFootNetworkTagParser; +import com.graphhopper.routing.util.parsers.OSMMtbNetworkTagParser; +import com.graphhopper.routing.util.parsers.TagParser; import com.graphhopper.routing.weighting.Weighting; +import com.graphhopper.routing.weighting.custom.CustomModelParser; import com.graphhopper.routing.weighting.custom.CustomWeighting; +import com.graphhopper.routing.weighting.custom.NameValidator; import com.graphhopper.storage.*; import com.graphhopper.storage.index.LocationIndex; import com.graphhopper.storage.index.LocationIndexTree; @@ -60,6 +65,8 @@ import java.nio.file.Paths; import java.text.DateFormat; import java.util.*; +import java.util.function.BiFunction; +import java.util.function.Function; import java.util.stream.Collectors; import static com.graphhopper.util.GHUtility.readCountries; @@ -79,6 +86,7 @@ public class GraphHopper { // utils private final TranslationMap trMap = new TranslationMap().doImport(); boolean removeZipped = true; + boolean calcChecksums = false; // for country rules: private CountryRuleFactory countryRuleFactory = null; // for custom areas: @@ -86,13 +94,12 @@ public class GraphHopper { // for graph: private BaseGraph baseGraph; private StorableProperties properties; - private EncodingManager encodingManager; + protected EncodingManager encodingManager; private OSMParsers osmParsers; private int defaultSegmentSize = -1; private String ghLocation = ""; private DAType dataAccessDefaultType = DAType.RAM_STORE; private final LinkedHashMap dataAccessConfig = new LinkedHashMap<>(); - private boolean sortGraph = false; private boolean elevation = false; private LockFactory lockFactory = new NativeFSLockFactory(); private boolean allowWrites = true; @@ -123,15 +130,11 @@ public class GraphHopper { // for data reader private String osmFile; private ElevationProvider eleProvider = ElevationProvider.NOOP; - private VehicleEncodedValuesFactory vehicleEncodedValuesFactory = new DefaultVehicleEncodedValuesFactory(); - private VehicleTagParserFactory vehicleTagParserFactory = new DefaultVehicleTagParserFactory(); - private EncodedValueFactory encodedValueFactory = new DefaultEncodedValueFactory(); - private TagParserFactory tagParserFactory = new DefaultTagParserFactory(); + private ImportRegistry importRegistry = new DefaultImportRegistry(); private PathDetailsBuilderFactory pathBuilderFactory = new PathDetailsBuilderFactory(); private String dateRangeParserString = ""; private String encodedValuesString = ""; - private String vehiclesString = ""; public GraphHopper setEncodedValuesString(String encodedValuesString) { this.encodedValuesString = encodedValuesString; @@ -142,15 +145,6 @@ public String getEncodedValuesString() { return encodedValuesString; } - public GraphHopper setVehiclesString(String vehiclesString) { - this.vehiclesString = vehiclesString; - return this; - } - - public String getVehiclesString() { - return vehiclesString; - } - public EncodingManager getEncodingManager() { if (encodingManager == null) throw new IllegalStateException("EncodingManager not yet built"); @@ -257,8 +251,8 @@ public GraphHopper setStoreOnFlush(boolean storeOnFlush) { *
      * {@code
      *   hopper.setProfiles(
-     *     new Profile("my_car").setVehicle("car"),
-     *     new Profile("your_bike").setVehicle("bike")
+     *     new Profile("my_car"),
+     *     new Profile("your_bike")
      *   );
      *   hopper.getCHPreparationHandler().setCHProfiles(
      *     new CHProfile("my_car"),
@@ -351,6 +345,11 @@ public GraphHopper setOSMFile(String osmFile) {
         return this;
     }
 
+    public GraphHopper setMaxSpeedCalculator(MaxSpeedCalculator maxSpeedCalculator) {
+        this.maxSpeedCalculator = maxSpeedCalculator;
+        return this;
+    }
+
     /**
      * The underlying graph used in algorithms.
      *
@@ -404,15 +403,6 @@ protected void setLocationIndex(LocationIndex locationIndex) {
         this.locationIndex = locationIndex;
     }
 
-    /**
-     * Sorts the graph which requires more RAM while import. See #12
-     */
-    public GraphHopper setSortGraph(boolean sortGraph) {
-        ensureNotLoaded();
-        this.sortGraph = sortGraph;
-        return this;
-    }
-
     public boolean isAllowWrites() {
         return allowWrites;
     }
@@ -430,36 +420,13 @@ public TranslationMap getTranslationMap() {
         return trMap;
     }
 
-    public GraphHopper setVehicleEncodedValuesFactory(VehicleEncodedValuesFactory factory) {
-        this.vehicleEncodedValuesFactory = factory;
-        return this;
-    }
-
-    public EncodedValueFactory getEncodedValueFactory() {
-        return this.encodedValueFactory;
-    }
-
-    public GraphHopper setEncodedValueFactory(EncodedValueFactory factory) {
-        this.encodedValueFactory = factory;
+    public GraphHopper setImportRegistry(ImportRegistry importRegistry) {
+        this.importRegistry = importRegistry;
         return this;
     }
 
-    public VehicleTagParserFactory getVehicleTagParserFactory() {
-        return this.vehicleTagParserFactory;
-    }
-
-    public GraphHopper setVehicleTagParserFactory(VehicleTagParserFactory factory) {
-        this.vehicleTagParserFactory = factory;
-        return this;
-    }
-
-    public TagParserFactory getTagParserFactory() {
-        return this.tagParserFactory;
-    }
-
-    public GraphHopper setTagParserFactory(TagParserFactory factory) {
-        this.tagParserFactory = factory;
-        return this;
+    public ImportRegistry getImportRegistry() {
+        return importRegistry;
     }
 
     public GraphHopper setCustomAreasDirectory(String customAreasDirectory) {
@@ -534,7 +501,6 @@ public GraphHopper init(GraphHopperConfig ghConfig) {
         if (ghConfig.getBool("max_speed_calculator.enabled", false))
             maxSpeedCalculator = new MaxSpeedCalculator(MaxSpeedCalculator.createLegalDefaultSpeeds());
 
-        sortGraph = ghConfig.getBool("graph.do_sort", sortGraph);
         removeZipped = ghConfig.getBool("graph.remove_zipped", removeZipped);
 
         if (!ghConfig.getString("spatial_rules.location", "").isEmpty())
@@ -550,11 +516,10 @@ public GraphHopper init(GraphHopperConfig ghConfig) {
         String customModelFolder = ghConfig.getString("custom_models.directory", ghConfig.getString("custom_model_folder", ""));
         setProfiles(GraphHopper.resolveCustomModelFiles(customModelFolder, ghConfig.getProfiles(), globalAreas));
 
-        if (ghConfig.has("graph.vehicles") && ghConfig.has("graph.flag_encoders"))
-            throw new IllegalArgumentException("Remove graph.flag_encoders as it cannot be used in parallel with graph.vehicles");
+        if (ghConfig.has("graph.vehicles"))
+            throw new IllegalArgumentException("The option graph.vehicles is no longer supported. Use the appropriate turn_costs and custom_model instead, see docs/migration/config-migration-08-09.md");
         if (ghConfig.has("graph.flag_encoders"))
-            logger.warn("The option graph.flag_encoders is deprecated and will be removed. Replace with graph.vehicles");
-        vehiclesString = ghConfig.getString("graph.vehicles", ghConfig.getString("graph.flag_encoders", vehiclesString));
+            throw new IllegalArgumentException("The option graph.flag_encoders is no longer supported.");
 
         encodedValuesString = ghConfig.getString("graph.encoded_values", encodedValuesString);
         dateRangeParserString = ghConfig.getString("datareader.date_range_parser_day", dateRangeParserString);
@@ -593,7 +558,7 @@ public GraphHopper init(GraphHopperConfig ghConfig) {
         if (!ghConfig.has("import.osm.ignored_highways"))
             throw new IllegalArgumentException("Missing 'import.osm.ignored_highways'. Not using this parameter can decrease performance, see config-example.yml for more details");
         String ignoredHighwaysString = ghConfig.getString("import.osm.ignored_highways", "");
-        if ((ignoredHighwaysString.contains("footway") || ignoredHighwaysString.contains("path")) && ghConfig.getProfiles().stream().map(Profile::getName).anyMatch(p -> p.contains("foot") || p.contains("hike") || p.contains("wheelchair")))
+        if ((ignoredHighwaysString.contains("footway") || ignoredHighwaysString.contains("path")) && ghConfig.getProfiles().stream().map(Profile::getName).anyMatch(p -> p.contains("foot") || p.contains("hike")))
             throw new IllegalArgumentException("You should not use import.osm.ignored_highways=footway or =path in conjunction with pedestrian profiles. This is probably an error in your configuration.");
         if ((ignoredHighwaysString.contains("cycleway") || ignoredHighwaysString.contains("path")) && ghConfig.getProfiles().stream().map(Profile::getName).anyMatch(p -> p.contains("mtb") || p.contains("bike")))
             throw new IllegalArgumentException("You should not use import.osm.ignored_highways=cycleway or =path in conjunction with bicycle profiles. This is probably an error in your configuration");
@@ -628,132 +593,90 @@ public GraphHopper init(GraphHopperConfig ghConfig) {
                     + " should be less or equal to landmark count of " + lmPreparationHandler.getLandmarks());
         routerConfig.setActiveLandmarkCount(activeLandmarkCount);
 
+        calcChecksums = ghConfig.getBool("graph.calc_checksums", false);
+
         return this;
     }
 
-    protected EncodingManager buildEncodingManager(Map vehiclesByName, List encodedValueStrings,
-                                                   boolean withUrbanDensity, boolean withMaxSpeedEst, Collection profiles) {
+    protected EncodingManager buildEncodingManager(Map encodedValuesWithProps,
+                                                   Map activeImportUnits,
+                                                   Map> restrictionVehicleTypesByProfile) {
+        List encodedValues = new ArrayList<>(activeImportUnits.entrySet().stream()
+                .map(e -> {
+                    Function f = e.getValue().getCreateEncodedValue();
+                    return f == null ? null : f.apply(encodedValuesWithProps.getOrDefault(e.getKey(), new PMap()));
+                })
+                .filter(Objects::nonNull)
+                .toList());
+        profilesByName.values().forEach(profile -> encodedValues.add(Subnetwork.create(profile.getName())));
+
+        List sortedEVs = getEVSortIndex(profilesByName);
+        encodedValues.sort(Comparator.comparingInt(ev -> sortedEVs.indexOf(ev.getName())));
+
         EncodingManager.Builder emBuilder = new EncodingManager.Builder();
-        vehiclesByName.forEach((name, vehicleStr) -> emBuilder.add(vehicleEncodedValuesFactory.createVehicleEncodedValues(name, new PMap(vehicleStr))));
-        profiles.forEach(profile -> emBuilder.add(Subnetwork.create(profile.getName())));
-        if (withMaxSpeedEst)
-            emBuilder.add(MaxSpeedEstimated.create());
-        if (withUrbanDensity)
-            emBuilder.add(UrbanDensity.create());
-        encodedValueStrings.forEach(s -> emBuilder.add(encodedValueFactory.create(s, new PMap())));
+        encodedValues.forEach(emBuilder::add);
+        restrictionVehicleTypesByProfile.entrySet().stream()
+                .filter(e -> !e.getValue().isEmpty())
+                .forEach(e -> emBuilder.addTurnCostEncodedValue(TurnRestriction.create(e.getKey())));
         return emBuilder.build();
     }
 
-    protected OSMParsers buildOSMParsers(Map vehiclesByName, List encodedValueStrings,
+    protected List getEVSortIndex(Map profilesByName) {
+        return Collections.emptyList();
+    }
+
+    protected OSMParsers buildOSMParsers(Map encodedValuesWithProps,
+                                         Map activeImportUnits,
+                                         Map> restrictionVehicleTypesByProfile,
                                          List ignoredHighways, String dateRangeParserString) {
+        ImportUnitSorter sorter = new ImportUnitSorter(activeImportUnits);
+        Map sortedImportUnits = new LinkedHashMap<>();
+        sorter.sort().forEach(name -> sortedImportUnits.put(name, activeImportUnits.get(name)));
+        DateRangeParser dateRangeParser = DateRangeParser.createInstance(dateRangeParserString);
+        List sortedParsers = new ArrayList<>();
+        sortedImportUnits.forEach((name, importUnit) -> {
+            BiFunction createTagParser = importUnit.getCreateTagParser();
+            if (createTagParser != null)
+                sortedParsers.add(createTagParser.apply(encodingManager, encodedValuesWithProps.getOrDefault(name, new PMap().putObject("date_range_parser", dateRangeParser))));
+        });
+
         OSMParsers osmParsers = new OSMParsers();
         ignoredHighways.forEach(osmParsers::addIgnoredHighway);
-        for (String s : encodedValueStrings) {
-            TagParser tagParser = tagParserFactory.create(encodingManager, s, new PMap());
-            if (tagParser != null)
-                osmParsers.addWayTagParser(tagParser);
-        }
-
-        // this needs to be in sync with the default EVs added in EncodingManager.Builder#build. ideally I would like to remove
-        // all these defaults and just use the config as the single source of truth
-        if (!encodedValueStrings.contains(Roundabout.KEY))
-            osmParsers.addWayTagParser(new OSMRoundaboutParser(encodingManager.getBooleanEncodedValue(Roundabout.KEY)));
-        if (!encodedValueStrings.contains(RoadClass.KEY))
-            osmParsers.addWayTagParser(new OSMRoadClassParser(encodingManager.getEnumEncodedValue(RoadClass.KEY, RoadClass.class)));
-        if (!encodedValueStrings.contains(RoadClassLink.KEY))
-            osmParsers.addWayTagParser(new OSMRoadClassLinkParser(encodingManager.getBooleanEncodedValue(RoadClassLink.KEY)));
-        if (!encodedValueStrings.contains(RoadEnvironment.KEY))
-            osmParsers.addWayTagParser(new OSMRoadEnvironmentParser(encodingManager.getEnumEncodedValue(RoadEnvironment.KEY, RoadEnvironment.class)));
-        if (!encodedValueStrings.contains(MaxSpeed.KEY))
-            osmParsers.addWayTagParser(new OSMMaxSpeedParser(encodingManager.getDecimalEncodedValue(MaxSpeed.KEY)));
-        if (!encodedValueStrings.contains(RoadAccess.KEY))
-            osmParsers.addWayTagParser(new OSMRoadAccessParser(encodingManager.getEnumEncodedValue(RoadAccess.KEY, RoadAccess.class), OSMRoadAccessParser.toOSMRestrictions(TransportationMode.CAR)));
-        if (!encodedValueStrings.contains(FerrySpeed.KEY))
-            osmParsers.addWayTagParser(new FerrySpeedCalculator(encodingManager.getDecimalEncodedValue(FerrySpeed.KEY)));
-        if (encodingManager.hasEncodedValue(AverageSlope.KEY) || encodingManager.hasEncodedValue(MaxSlope.KEY)) {
-            if (!encodingManager.hasEncodedValue(AverageSlope.KEY) || !encodingManager.hasEncodedValue(MaxSlope.KEY))
-                throw new IllegalArgumentException("Enable both, average_slope and max_slope");
-            osmParsers.addWayTagParser(new SlopeCalculator(encodingManager.getDecimalEncodedValue(MaxSlope.KEY),
-                    encodingManager.getDecimalEncodedValue(AverageSlope.KEY)));
-        }
+        sortedParsers.forEach(osmParsers::addWayTagParser);
 
         if (maxSpeedCalculator != null) {
-            if (!encodingManager.hasEncodedValue(Country.KEY))
-                throw new IllegalArgumentException("max_speed_calculator needs country");
-            if (!encodingManager.hasEncodedValue(UrbanDensity.KEY))
-                throw new IllegalArgumentException("max_speed_calculator needs urban_density");
+            maxSpeedCalculator.checkEncodedValues(encodingManager);
             osmParsers.addWayTagParser(maxSpeedCalculator.getParser());
         }
 
-        if (encodingManager.hasEncodedValue(Curvature.KEY))
-            osmParsers.addWayTagParser(new CurvatureCalculator(encodingManager.getDecimalEncodedValue(Curvature.KEY)));
+        if (encodingManager.hasEncodedValue(BikeNetwork.KEY))
+            osmParsers.addRelationTagParser(relConfig -> new OSMBikeNetworkTagParser(encodingManager.getEnumEncodedValue(BikeNetwork.KEY, RouteNetwork.class), relConfig));
+        if (encodingManager.hasEncodedValue(MtbNetwork.KEY))
+            osmParsers.addRelationTagParser(relConfig -> new OSMMtbNetworkTagParser(encodingManager.getEnumEncodedValue(MtbNetwork.KEY, RouteNetwork.class), relConfig));
+        if (encodingManager.hasEncodedValue(FootNetwork.KEY))
+            osmParsers.addRelationTagParser(relConfig -> new OSMFootNetworkTagParser(encodingManager.getEnumEncodedValue(FootNetwork.KEY, RouteNetwork.class), relConfig));
 
-        DateRangeParser dateRangeParser = DateRangeParser.createInstance(dateRangeParserString);
-        Set added = new HashSet<>();
-        vehiclesByName.forEach((name, vehicleStr) -> {
-            VehicleTagParsers vehicleTagParsers = vehicleTagParserFactory.createParsers(encodingManager, name,
-                    new PMap(vehicleStr).putObject("date_range_parser", dateRangeParser));
-            if (vehicleTagParsers == null)
-                return;
-            vehicleTagParsers.getTagParsers().forEach(tagParser -> {
-                if (tagParser == null) return;
-                if (tagParser instanceof BikeCommonAccessParser) {
-                    if (encodingManager.hasEncodedValue(BikeNetwork.KEY) && added.add(BikeNetwork.KEY))
-                        osmParsers.addRelationTagParser(relConfig -> new OSMBikeNetworkTagParser(encodingManager.getEnumEncodedValue(BikeNetwork.KEY, RouteNetwork.class), relConfig));
-                    if (encodingManager.hasEncodedValue(Smoothness.KEY) && added.add(Smoothness.KEY))
-                        osmParsers.addWayTagParser(new OSMSmoothnessParser(encodingManager.getEnumEncodedValue(Smoothness.KEY, Smoothness.class)));
-                } else if (tagParser instanceof FootAccessParser) {
-                    if (encodingManager.hasEncodedValue(FootNetwork.KEY) && added.add(FootNetwork.KEY))
-                        osmParsers.addRelationTagParser(relConfig -> new OSMFootNetworkTagParser(encodingManager.getEnumEncodedValue(FootNetwork.KEY, RouteNetwork.class), relConfig));
-                }
-                String turnRestrictionKey = TurnRestriction.key(new PMap(vehicleStr).getString("name", name));
-                if (encodingManager.hasTurnEncodedValue(turnRestrictionKey)
-                        // need to make sure we do not add the same restriction parsers multiple times
-                        && osmParsers.getRestrictionTagParsers().stream().noneMatch(r -> r.getTurnRestrictionEnc().getName().equals(turnRestrictionKey))) {
-                    List restrictions = tagParser instanceof AbstractAccessParser
-                            ? ((AbstractAccessParser) tagParser).getRestrictions()
-                            : OSMRoadAccessParser.toOSMRestrictions(TransportationMode.valueOf(new PMap(vehicleStr).getString("transportation_mode", "VEHICLE")));
-                    osmParsers.addRestrictionTagParser(new RestrictionTagParser(restrictions, encodingManager.getTurnBooleanEncodedValue(turnRestrictionKey)));
-                }
-            });
-            vehicleTagParsers.getTagParsers().forEach(tagParser -> {
-                if (tagParser == null) return;
-                osmParsers.addWayTagParser(tagParser);
-
-                if (tagParser instanceof BikeCommonAccessParser && encodingManager.hasEncodedValue(GetOffBike.KEY) && added.add(GetOffBike.KEY))
-                    osmParsers.addWayTagParser(new OSMGetOffBikeParser(encodingManager.getBooleanEncodedValue(GetOffBike.KEY), ((BikeCommonAccessParser) tagParser).getAccessEnc()));
-            });
+        restrictionVehicleTypesByProfile.forEach((profile, restrictionVehicleTypes) -> {
+            osmParsers.addRestrictionTagParser(new RestrictionTagParser(
+                    restrictionVehicleTypes, encodingManager.getTurnBooleanEncodedValue(TurnRestriction.key(profile))));
         });
         return osmParsers;
     }
 
-    public static List getEncodedValueStrings(String encodedValuesStr) {
-        return Arrays.stream(encodedValuesStr.split(","))
-                .map(String::trim)
-                .filter(s -> !s.isEmpty())
-                .collect(Collectors.toList());
+    public static Map parseEncodedValueString(String encodedValuesStr) {
+        Map encodedValuesWithProps = new LinkedHashMap<>();
+        Arrays.stream(encodedValuesStr.split(","))
+                .filter(evStr -> !evStr.isBlank())
+                .forEach(evStr -> encodedValuesWithProps.put(evStr.trim().split("\\|")[0], new PMap(evStr)));
+        return encodedValuesWithProps;
     }
 
-    public static Map getVehiclesByName(String vehiclesStr, Collection profiles) {
-        Map vehiclesMap = new LinkedHashMap<>();
-        for (String encoderStr : vehiclesStr.split(",")) {
-            String name = encoderStr.split("\\|")[0].trim();
-            if (name.isEmpty())
-                continue;
-            if (vehiclesMap.containsKey(name))
-                throw new IllegalArgumentException("Duplicate vehicle: " + name + " in: " + encoderStr);
-            vehiclesMap.put(name, encoderStr);
-        }
-        Map vehiclesFromProfiles = new LinkedHashMap<>();
-        for (Profile profile : profiles) {
-            // if a profile uses a vehicle with turn costs make sure we add that vehicle with turn costs
-            String vehicle = profile.getVehicle().trim();
-            if (!vehiclesFromProfiles.containsKey(vehicle) || profile.isTurnCosts())
-                vehiclesFromProfiles.put(vehicle, vehicle + (profile.isTurnCosts() ? "|turn_costs=true" : ""));
-        }
-        // vehicles from profiles are only taken into account when they were not given explicitly
-        vehiclesFromProfiles.forEach(vehiclesMap::putIfAbsent);
-        return vehiclesMap;
+    private static Map> getRestrictionVehicleTypesByProfile(Collection profiles) {
+        Map> result = new LinkedHashMap<>();
+        for (Profile profile : profiles)
+            if (profile.hasTurnCosts())
+                result.put(profile.getName(), profile.getTurnCostsConfig().getVehicleTypes());
+        return result;
     }
 
     private static ElevationProvider createElevationProvider(GraphHopperConfig ghConfig) {
@@ -866,15 +789,11 @@ public void importAndClose() {
      * Creates the graph from OSM data.
      */
     protected void process(boolean closeEarly) {
+        prepareImport();
+        if (encodingManager == null)
+            throw new IllegalStateException("The EncodingManager must be created in `prepareImport()`");
         GHDirectory directory = new GHDirectory(ghLocation, dataAccessDefaultType);
         directory.configure(dataAccessConfig);
-        boolean withUrbanDensity = urbanDensityCalculationThreads > 0;
-        boolean withMaxSpeedEstimation = maxSpeedCalculator != null;
-        Map vehiclesByName = getVehiclesByName(vehiclesString, profilesByName.values());
-        List encodedValueStrings = getEncodedValueStrings(encodedValuesString);
-        encodingManager = buildEncodingManager(vehiclesByName, encodedValueStrings, withUrbanDensity,
-                withMaxSpeedEstimation, profilesByName.values());
-        osmParsers = buildOSMParsers(vehiclesByName, encodedValueStrings, osmReaderConfig.getIgnoredHighways(), dateRangeParserString);
         baseGraph = new BaseGraph.Builder(getEncodingManager())
                 .setDir(directory)
                 .set3D(hasElevation())
@@ -893,9 +812,14 @@ protected void process(boolean closeEarly) {
                     throw new RuntimeException("To avoid multiple writers we need to obtain a write lock but it failed. In " + ghLocation, lock.getObtainFailedReason());
             }
             ensureWriteAccess();
+
             importOSM();
+            postImportOSM();
             cleanUp();
-            postImport();
+
+            properties.put("profiles", getProfilesString());
+            writeEncodingManagerToProperties();
+
             postProcessing(closeEarly);
             flush();
         } finally {
@@ -904,25 +828,69 @@ protected void process(boolean closeEarly) {
         }
     }
 
-    protected void postImport() {
+    protected void prepareImport() {
+        Map encodedValuesWithProps = parseEncodedValueString(encodedValuesString);
+        NameValidator nameValidator = s -> importRegistry.createImportUnit(s) != null;
+        Set missing = new LinkedHashSet<>();
+        profilesByName.values().
+                forEach(profile -> CustomModelParser.findVariablesForEncodedValuesString(profile.getCustomModel(), nameValidator, s -> "").
+                        forEach(var -> {
+                            if (!encodedValuesWithProps.containsKey(var)) missing.add(var);
+                            encodedValuesWithProps.putIfAbsent(var, new PMap());
+                        }));
+        if (!missing.isEmpty()) {
+            String encodedValuesString = encodedValuesWithProps.entrySet().stream()
+                    .map(e -> e.getKey() + (e.getValue().isEmpty() ? "" : ("|" + e.getValue().toMap().entrySet().stream().map(p -> p.getKey() + "=" + p.getValue()).collect(Collectors.joining("|")))))
+                    .collect(Collectors.joining(", "));
+            throw new IllegalArgumentException("Encoded values missing: " + String.join(", ", missing) + ".\n" +
+                    "To avoid that certain encoded values are automatically removed when you change the custom model later, you need to set the encoded values manually:\n" +
+                    "graph.encoded_values: " + encodedValuesString);
+        }
+
+        // these are used in the snap prevention filter (avoid motorway, tunnel, etc.) so they have to be there
+        encodedValuesWithProps.putIfAbsent(RoadClass.KEY, new PMap());
+        encodedValuesWithProps.putIfAbsent(RoadEnvironment.KEY, new PMap());
+        // used by instructions...
+        encodedValuesWithProps.putIfAbsent(Roundabout.KEY, new PMap());
+        encodedValuesWithProps.putIfAbsent(RoadClassLink.KEY, new PMap());
+        encodedValuesWithProps.putIfAbsent(MaxSpeed.KEY, new PMap());
+
+        Map> restrictionVehicleTypesByProfile = getRestrictionVehicleTypesByProfile(profilesByName.values());
+
+        if (urbanDensityCalculationThreads > 0)
+            encodedValuesWithProps.put(UrbanDensity.KEY, new PMap());
+        if (maxSpeedCalculator != null)
+            encodedValuesWithProps.put(MaxSpeedEstimated.KEY, new PMap());
+
+        Map activeImportUnits = new LinkedHashMap<>();
+        ArrayDeque deque = new ArrayDeque<>(encodedValuesWithProps.keySet());
+        while (!deque.isEmpty()) {
+            String ev = deque.removeFirst();
+            ImportUnit importUnit = importRegistry.createImportUnit(ev);
+            if (importUnit == null)
+                throw new IllegalArgumentException("Unknown encoded value: " + ev);
+            if (activeImportUnits.put(ev, importUnit) == null)
+                deque.addAll(importUnit.getRequiredImportUnits());
+        }
+        encodingManager = buildEncodingManager(encodedValuesWithProps, activeImportUnits, restrictionVehicleTypesByProfile);
+        osmParsers = buildOSMParsers(encodedValuesWithProps, activeImportUnits, restrictionVehicleTypesByProfile, osmReaderConfig.getIgnoredHighways(), dateRangeParserString);
+    }
+
+    protected void postImportOSM() {
         // Important note: To deal with via-way turn restrictions we introduce artificial edges in OSMReader (#2689).
         // These are simply copies of real edges. Any further modifications of the graph edges must take care of keeping
         // the artificial edges in sync with their real counterparts. So if an edge attribute shall be changed this change
         // must also be applied to the corresponding artificial edge.
-        if (sortGraph) {
-            BaseGraph newGraph = GHUtility.newGraph(baseGraph);
-            GHUtility.sortDFS(baseGraph, newGraph);
-            logger.info("graph sorted (" + getMemInfo() + ")");
-            baseGraph = newGraph;
-        }
 
-        if (hasElevation())
-            interpolateBridgesTunnelsAndFerries();
+        calculateUrbanDensity();
 
         if (maxSpeedCalculator != null) {
             maxSpeedCalculator.fillMaxSpeed(getBaseGraph(), encodingManager);
             maxSpeedCalculator.close();
         }
+
+        if (hasElevation())
+            interpolateBridgesTunnelsAndFerries();
     }
 
     protected void importOSM() {
@@ -963,9 +931,6 @@ protected void importOSM() {
         properties.put("datareader.import.date", f.format(new Date()));
         if (reader.getDataDate() != null)
             properties.put("datareader.data.date", f.format(reader.getDataDate()));
-
-        calculateUrbanDensity();
-        writeEncodingManagerToProperties();
     }
 
     protected void createBaseGraphAndProperties() {
@@ -976,7 +941,7 @@ protected void createBaseGraphAndProperties() {
             maxSpeedCalculator.createDataAccessForParser(baseGraph.getDirectory());
     }
 
-    protected void calculateUrbanDensity() {
+    private void calculateUrbanDensity() {
         if (encodingManager.hasEncodedValue(UrbanDensity.KEY)) {
             EnumEncodedValue urbanDensityEnc = encodingManager.getEnumEncodedValue(UrbanDensity.KEY, UrbanDensity.class);
             if (!encodingManager.hasEncodedValue(RoadClass.KEY))
@@ -990,7 +955,7 @@ protected void calculateUrbanDensity() {
         }
     }
 
-    protected void writeEncodingManagerToProperties() {
+    private void writeEncodingManagerToProperties() {
         EncodingManager.putEncodingManagerIntoProperties(encodingManager, properties);
     }
 
@@ -1102,22 +1067,10 @@ private String getProfilesString() {
         return profilesByName.values().stream().map(p -> p.getName() + "|" + p.getVersion()).collect(Collectors.joining(","));
     }
 
-    private void checkProfilesConsistency() {
+    public void checkProfilesConsistency() {
         if (profilesByName.isEmpty())
             throw new IllegalArgumentException("There has to be at least one profile");
-        EncodingManager encodingManager = getEncodingManager();
         for (Profile profile : profilesByName.values()) {
-            if (!encodingManager.getVehicles().contains(profile.getVehicle()))
-                throw new IllegalArgumentException("Unknown vehicle '" + profile.getVehicle() + "' in profile: " + profile + ". " +
-                        "Available vehicles: " + String.join(",", encodingManager.getVehicles()));
-            BooleanEncodedValue turnRestrictionEnc = encodingManager.hasTurnEncodedValue(TurnRestriction.key(profile.getVehicle()))
-                    ? encodingManager.getTurnBooleanEncodedValue(TurnRestriction.key(profile.getVehicle()))
-                    : null;
-            if (profile.isTurnCosts() && turnRestrictionEnc == null) {
-                throw new IllegalArgumentException("The profile '" + profile.getName() + "' was configured with " +
-                        "'turn_costs=true', but the corresponding vehicle '" + profile.getVehicle() + "' does not support turn costs." +
-                        "\nYou need to add `|turn_costs=true` to the vehicle in `graph.vehicles`");
-            }
             try {
                 createWeighting(profile, new PMap());
             } catch (IllegalArgumentException e) {
@@ -1174,7 +1127,7 @@ private List createCHConfigs(List chProfiles) {
         List chConfigs = new ArrayList<>();
         for (CHProfile chProfile : chProfiles) {
             Profile profile = profilesByName.get(chProfile.getProfile());
-            if (profile.isTurnCosts()) {
+            if (profile.hasTurnCosts()) {
                 chConfigs.add(CHConfig.edgeBased(profile.getName(), createWeighting(profile, new PMap())));
             } else {
                 chConfigs.add(CHConfig.nodeBased(profile.getName(), createWeighting(profile, new PMap())));
@@ -1210,6 +1163,7 @@ private List createLMConfigs(List lmProfiles) {
      * @param closeEarly release resources as early as possible
      */
     protected void postProcessing(boolean closeEarly) {
+        calcChecksums();
         initLocationIndex();
         importPublicTransit();
 
@@ -1300,6 +1254,37 @@ protected LocationIndex createLocationIndex(Directory dir) {
         return tmpIndex;
     }
 
+    private void calcChecksums() {
+        if (!calcChecksums) return;
+        logger.info("Calculating checksums for {} profiles", profilesByName.size());
+        StopWatch sw = StopWatch.started();
+        double[] checksums_fwd = new double[profilesByName.size()];
+        double[] checksums_bwd = new double[profilesByName.size()];
+        List weightings = profilesByName.values().stream().map(profile -> createWeighting(profile, new PMap())).toList();
+        AllEdgesIterator edge = baseGraph.getAllEdges();
+        while (edge.next()) {
+            for (int i = 0; i < profilesByName.size(); i++) {
+                double weightFwd = weightings.get(i).calcEdgeWeight(edge, false);
+                if (Double.isInfinite(weightFwd)) weightFwd = -1;
+                weightFwd *= (i % 2 == 0) ? -1 : 1;
+                double weightBwd = weightings.get(i).calcEdgeWeight(edge, true);
+                if (Double.isInfinite(weightBwd)) weightBwd = -1;
+                weightBwd *= (i % 2 == 0) ? -1 : 1;
+                checksums_fwd[i] += weightFwd;
+                checksums_bwd[i] += weightBwd;
+            }
+        }
+        int index = 0;
+        for (Profile profile : profilesByName.values()) {
+            properties.put("checksum.fwd." + profile.getName(), checksums_fwd[index]);
+            properties.put("checksum.bwd." + profile.getName(), checksums_bwd[index]);
+            logger.info("checksum.fwd." + profile.getName() + ": " + checksums_fwd[index]);
+            logger.info("checksum.bwd." + profile.getName() + ": " + checksums_bwd[index]);
+            index++;
+        }
+        logger.info("Calculating checksums took: " + sw.stop().getTimeString());
+    }
+
     /**
      * Initializes the location index after the import is done.
      */
@@ -1411,7 +1396,6 @@ protected void cleanUp() {
         preparation.setMinNetworkSize(minNetworkSize);
         preparation.setThreads(subnetworksThreads);
         preparation.doWork();
-        properties.put("profiles", getProfilesString());
         logger.info("nodes: " + Helper.nf(baseGraph.getNodes()) + ", edges: " + Helper.nf(baseGraph.getEdges()));
     }
 
@@ -1555,13 +1539,15 @@ public static List resolveCustomModelFiles(String customModelFolder, Li
                             String string;
                             // 1. try to load custom model from jar
                             InputStream is = GHUtility.class.getResourceAsStream("/com/graphhopper/custom_models/" + file);
+                            // dropwizard makes it very hard to find out the folder of config.yml -> use an extra parameter for the folder
+                            Path customModelFile = Paths.get(customModelFolder).resolve(file);
                             if (is != null) {
+                                if (Files.exists(customModelFile))
+                                    throw new RuntimeException("Custom model file name '" + file + "' is already used for built-in profiles. Use another name");
                                 string = readJSONFileWithoutComments(new InputStreamReader(is));
                             } else {
                                 // 2. try to load custom model file from external location
-                                // dropwizard makes it very hard to find out the folder of config.yml -> use an extra parameter for the folder
-                                string = readJSONFileWithoutComments(Paths.get(customModelFolder).
-                                        resolve(file).toFile().getAbsolutePath());
+                                string = readJSONFileWithoutComments(customModelFile.toFile().getAbsolutePath());
                             }
                             customModel = CustomModel.merge(customModel, jsonOM.readValue(string, CustomModel.class));
                         } catch (IOException ex) {
diff --git a/core/src/main/java/com/graphhopper/GraphHopperConfig.java b/core/src/main/java/com/graphhopper/GraphHopperConfig.java
index 9e31d958d75..b85a6c61069 100644
--- a/core/src/main/java/com/graphhopper/GraphHopperConfig.java
+++ b/core/src/main/java/com/graphhopper/GraphHopperConfig.java
@@ -23,6 +23,7 @@
 import com.graphhopper.config.CHProfile;
 import com.graphhopper.config.LMProfile;
 import com.graphhopper.config.Profile;
+import com.graphhopper.jackson.ResponsePathSerializer;
 import com.graphhopper.util.PMap;
 
 import java.util.ArrayList;
@@ -39,17 +40,24 @@ public class GraphHopperConfig {
     private List profiles = new ArrayList<>();
     private List chProfiles = new ArrayList<>();
     private List lmProfiles = new ArrayList<>();
+    private List copyrights = new ArrayList<>();
     private final PMap map;
 
     public GraphHopperConfig() {
         this(new PMap());
+        // This includes the required attribution for OpenStreetMap.
+        // Do not hesitate to  mention us and link us in your about page
+        // https://support.graphhopper.com/support/search/solutions?term=attribution
+        copyrights.add("GraphHopper");
+        copyrights.add("OpenStreetMap contributors");
     }
 
     public GraphHopperConfig(GraphHopperConfig otherConfig) {
         map = new PMap(otherConfig.map);
-        profiles = new ArrayList<>(otherConfig.profiles);
-        chProfiles = new ArrayList<>(otherConfig.chProfiles);
-        lmProfiles = new ArrayList<>(otherConfig.lmProfiles);
+        otherConfig.profiles.forEach(p -> profiles.add(new Profile(p)));
+        otherConfig.chProfiles.forEach(p -> chProfiles.add(new CHProfile(p)));
+        otherConfig.lmProfiles.forEach(p -> lmProfiles.add(new LMProfile(p)));
+        copyrights.addAll(otherConfig.copyrights);
     }
 
     public GraphHopperConfig(PMap pMap) {
@@ -85,6 +93,14 @@ public GraphHopperConfig setLMProfiles(List lmProfiles) {
         return this;
     }
 
+    public List getCopyrights() {
+        return copyrights;
+    }
+
+    public void setCopyrights(List copyrights) {
+        this.copyrights = copyrights;
+    }
+
     // We can add explicit configuration properties to GraphHopperConfig (for example to allow lists or nested objects),
     // everything else is stored in a HashMap
     @JsonAnySetter
diff --git a/core/src/main/java/com/graphhopper/config/CHProfile.java b/core/src/main/java/com/graphhopper/config/CHProfile.java
index c7148f6ea23..3b30359efc9 100644
--- a/core/src/main/java/com/graphhopper/config/CHProfile.java
+++ b/core/src/main/java/com/graphhopper/config/CHProfile.java
@@ -35,6 +35,10 @@ private CHProfile() {
         // default constructor needed for jackson
     }
 
+    public CHProfile(CHProfile profile) {
+        this.profile = profile.profile;
+    }
+
     public CHProfile(String profile) {
         setProfile(profile);
     }
diff --git a/core/src/main/java/com/graphhopper/config/LMProfile.java b/core/src/main/java/com/graphhopper/config/LMProfile.java
index db48138b2e4..e40bb6cb50b 100644
--- a/core/src/main/java/com/graphhopper/config/LMProfile.java
+++ b/core/src/main/java/com/graphhopper/config/LMProfile.java
@@ -37,6 +37,12 @@ private LMProfile() {
         // default constructor needed for jackson
     }
 
+    public LMProfile(LMProfile profile) {
+        this.profile = profile.profile;
+        this.preparationProfile = profile.preparationProfile;
+        this.maximumLMWeight = profile.maximumLMWeight;
+    }
+
     public LMProfile(String profile) {
         setProfile(profile);
     }
diff --git a/core/src/main/java/com/graphhopper/config/Profile.java b/core/src/main/java/com/graphhopper/config/Profile.java
index 22d7165b6a6..f2be2672748 100644
--- a/core/src/main/java/com/graphhopper/config/Profile.java
+++ b/core/src/main/java/com/graphhopper/config/Profile.java
@@ -20,6 +20,7 @@
 
 import com.fasterxml.jackson.annotation.JsonAnySetter;
 import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
 import com.graphhopper.util.CustomModel;
 import com.graphhopper.util.Helper;
 import com.graphhopper.util.PMap;
@@ -33,10 +34,9 @@
  * @see LMProfile
  */
 public class Profile {
-    private String name = "car";
-    private String vehicle = "car";
+    private String name;
+    private TurnCostsConfig turnCostsConfig;
     private String weighting = "custom";
-    private boolean turnCosts = false;
     private PMap hints = new PMap();
 
     public static void validateProfileName(String profileName) {
@@ -54,6 +54,13 @@ public Profile(String name) {
         setCustomModel(new CustomModel());
     }
 
+    public Profile(Profile p) {
+        setName(p.getName());
+        setTurnCostsConfig(p.getTurnCostsConfig());
+        setWeighting(p.getWeighting());
+        hints = new PMap(p.getHints());
+    }
+
     public String getName() {
         return name;
     }
@@ -64,13 +71,14 @@ public Profile setName(String name) {
         return this;
     }
 
-    public String getVehicle() {
-        return vehicle;
+    public Profile setTurnCostsConfig(TurnCostsConfig turnCostsConfig) {
+        this.turnCostsConfig = turnCostsConfig;
+        return this;
     }
 
-    public Profile setVehicle(String vehicle) {
-        this.vehicle = vehicle;
-        return this;
+    @JsonProperty("turn_costs")
+    public TurnCostsConfig getTurnCostsConfig() {
+        return turnCostsConfig;
     }
 
     public String getWeighting() {
@@ -93,13 +101,8 @@ public CustomModel getCustomModel() {
         return getHints().getObject(CustomModel.KEY, null);
     }
 
-    public boolean isTurnCosts() {
-        return turnCosts;
-    }
-
-    public Profile setTurnCosts(boolean turnCosts) {
-        this.turnCosts = turnCosts;
-        return this;
+    public boolean hasTurnCosts() {
+        return turnCostsConfig != null;
     }
 
     @JsonIgnore
@@ -109,6 +112,10 @@ public PMap getHints() {
 
     @JsonAnySetter
     public Profile putHint(String key, Object value) {
+        if (key.equals("u_turn_costs"))
+            throw new IllegalArgumentException("u_turn_costs no longer accepted in profile. Use the turn costs configuration instead, see docs/migration/config-migration-08-09.md");
+        if (key.equals("vehicle"))
+            throw new IllegalArgumentException("vehicle no longer accepted in profile, see docs/migration/config-migration-08-09.md");
         this.hints.putObject(key, value);
         return this;
     }
@@ -128,7 +135,7 @@ public boolean equals(Object o) {
 
     private String createContentString() {
         // used to check against stored custom models, see #2026
-        return "name=" + name + "|vehicle=" + vehicle + "|weighting=" + weighting + "|turnCosts=" + turnCosts + "|hints=" + hints;
+        return "name=" + name + "|turn_costs={" + turnCostsConfig + "}|weighting=" + weighting + "|hints=" + hints;
     }
 
     @Override
diff --git a/core/src/main/java/com/graphhopper/config/TurnCostsConfig.java b/core/src/main/java/com/graphhopper/config/TurnCostsConfig.java
new file mode 100644
index 00000000000..f5c8b8c904d
--- /dev/null
+++ b/core/src/main/java/com/graphhopper/config/TurnCostsConfig.java
@@ -0,0 +1,77 @@
+package com.graphhopper.config;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.List;
+import java.util.Set;
+
+public class TurnCostsConfig {
+    public static final int INFINITE_U_TURN_COSTS = -1;
+    private int uTurnCosts = INFINITE_U_TURN_COSTS;
+    private List vehicleTypes;
+    // ensure that no typos can occur like motor_car vs motorcar or bike vs bicycle
+    private static final Set ALL_SUPPORTED = Set.of(
+            "agricultural", "atv", "auto_rickshaw",
+            "bdouble", "bicycle", "bus", "caravan", "carpool", "coach", "delivery", "destination",
+            "emergency", "foot", "golf_cart", "goods", "hazmat", "hgv", "hgv:trailer", "hov",
+            "minibus", "mofa", "moped", "motorcar", "motorcycle", "motor_vehicle", "motorhome",
+            "nev", "ohv", "psv", "residents",
+            "share_taxi", "small_electric_vehicle", "speed_pedelec",
+            "taxi", "trailer", "tourist_bus");
+
+    public static TurnCostsConfig car() {
+        return new TurnCostsConfig(List.of("motorcar", "motor_vehicle"));
+    }
+
+    public static TurnCostsConfig bike() {
+        return new TurnCostsConfig(List.of("bicycle"));
+    }
+
+    // jackson
+    public TurnCostsConfig() {
+    }
+
+    public TurnCostsConfig(List vehicleTypes) {
+        this.vehicleTypes = check(vehicleTypes);
+    }
+
+    public TurnCostsConfig(List vehicleTypes, int uTurnCost) {
+        this.vehicleTypes = check(vehicleTypes);
+        this.uTurnCosts = uTurnCost;
+    }
+
+    public void setVehicleTypes(List vehicleTypes) {
+        this.vehicleTypes = check(vehicleTypes);
+    }
+
+    List check(List restrictions) {
+        if (restrictions == null || restrictions.isEmpty())
+            throw new IllegalArgumentException("turn_costs cannot have empty vehicle_types");
+        for (String r : restrictions) {
+            if (!ALL_SUPPORTED.contains(r))
+                throw new IllegalArgumentException("Currently we do not support the restriction: " + r);
+        }
+        return restrictions;
+    }
+
+    @JsonProperty("vehicle_types")
+    public List getVehicleTypes() {
+        check(vehicleTypes);
+        return vehicleTypes;
+    }
+
+    public TurnCostsConfig setUTurnCosts(int uTurnCosts) {
+        this.uTurnCosts = uTurnCosts;
+        return this;
+    }
+
+    @JsonProperty("u_turn_costs")
+    public int getUTurnCosts() {
+        return uTurnCosts;
+    }
+
+    @Override
+    public String toString() {
+        return "vehicleTypes=" + vehicleTypes + ", uTurnCosts=" + uTurnCosts;
+    }
+}
diff --git a/core/src/main/java/com/graphhopper/reader/ReaderElement.java b/core/src/main/java/com/graphhopper/reader/ReaderElement.java
index 22f95bf431c..c56a6d3c6e8 100644
--- a/core/src/main/java/com/graphhopper/reader/ReaderElement.java
+++ b/core/src/main/java/com/graphhopper/reader/ReaderElement.java
@@ -162,12 +162,12 @@ public boolean hasTag(List keyList, Object value) {
     }
 
     /**
-     * Returns the first existing tag of the specified list where the order is important.
+     * Returns the first existing value of the specified list of keys where the order is important.
      *
      * @return an empty string if nothing found
      */
-    public String getFirstPriorityTag(List restrictions) {
-        for (String str : restrictions) {
+    public String getFirstValue(List searchedTags) {
+        for (String str : searchedTags) {
             Object value = properties.get(str);
             if (value != null)
                 return (String) value;
@@ -175,6 +175,19 @@ public String getFirstPriorityTag(List restrictions) {
         return "";
     }
 
+    /**
+     * @return -1 if not found
+     */
+    public int getFirstIndex(List searchedTags) {
+        for (int i = 0; i < searchedTags.size(); i++) {
+            String str = searchedTags.get(i);
+            Object value = properties.get(str);
+            if (value != null)
+                return i;
+        }
+        return -1;
+    }
+
     public void removeTag(String name) {
         properties.remove(name);
     }
diff --git a/core/src/main/java/com/graphhopper/reader/dem/AbstractSRTMElevationProvider.java b/core/src/main/java/com/graphhopper/reader/dem/AbstractSRTMElevationProvider.java
index 627e4145d45..077718cb4b4 100644
--- a/core/src/main/java/com/graphhopper/reader/dem/AbstractSRTMElevationProvider.java
+++ b/core/src/main/java/com/graphhopper/reader/dem/AbstractSRTMElevationProvider.java
@@ -156,10 +156,10 @@ private void updateHeightsFromFile(double lat, double lon, DataAccess heights) t
             heights.flush();
 
         } catch (FileNotFoundException ex) {
-            logger.warn("File not found for the coordinates for " + lat + "," + lon);
+            logger.warn("File not found " + heights + " for the coordinates " + lat + "," + lon);
             throw ex;
         } catch (Exception ex) {
-            throw new RuntimeException("There was an issue looking up the coordinates for " + lat + "," + lon, ex);
+            throw new RuntimeException("There was an issue with " + heights + " looking up the coordinates " + lat + "," + lon, ex);
         }
     }
 
diff --git a/core/src/main/java/com/graphhopper/reader/osm/OSMReader.java b/core/src/main/java/com/graphhopper/reader/osm/OSMReader.java
index c84e2ceff80..7bb81d03852 100644
--- a/core/src/main/java/com/graphhopper/reader/osm/OSMReader.java
+++ b/core/src/main/java/com/graphhopper/reader/osm/OSMReader.java
@@ -412,8 +412,8 @@ else if (Math.abs(edgeDistance - geometryDistance) > tolerance)
      * the duration tag when it is present. The latter cannot be done on a per-edge basis, because the duration tag
      * refers to the duration of the entire way.
      */
-    protected void preprocessWay(ReaderWay way, WaySegmentParser.CoordinateSupplier coordinateSupplier) {
-        // storing the road name does not yet depend on the flagEncoder so manage it directly
+    protected void preprocessWay(ReaderWay way, WaySegmentParser.CoordinateSupplier coordinateSupplier,
+                                 WaySegmentParser.NodeTagSupplier nodeTagSupplier) {
         List list = new ArrayList<>();
         if (config.isParseWayNames()) {
             // http://wiki.openstreetmap.org/wiki/Key:name
@@ -446,7 +446,36 @@ protected void preprocessWay(ReaderWay way, WaySegmentParser.CoordinateSupplier
                 if (way.hasTag("destination:backward"))
                     list.add(new KVStorage.KeyValue(STREET_DESTINATION, fixWayName(way.getTag("destination:backward")), false, true));
             }
+
+            // copy node name of motorway_junction
+            LongArrayList nodes = way.getNodes();
+            if (!nodes.isEmpty() && (way.hasTag("highway", "motorway") || way.hasTag("highway", "motorway_link"))) {
+                // index 0 assumes oneway=yes
+                Map nodeTags = nodeTagSupplier.getTags(nodes.get(0));
+                String nodeName = (String) nodeTags.getOrDefault("name", "");
+                if (!nodeName.isEmpty() && "motorway_junction".equals(nodeTags.getOrDefault("highway", "")))
+                    list.add(new KVStorage.KeyValue(MOTORWAY_JUNCTION, nodeName));
+            }
         }
+
+        if (way.getTags().size() > 1) // at least highway tag
+            for (Map.Entry entry : way.getTags().entrySet()) {
+                if (entry.getKey().endsWith(":conditional") && entry.getValue() instanceof String &&
+                        // for now reduce index size a bit and focus on access tags
+                        !entry.getKey().startsWith("maxspeed") && !entry.getKey().startsWith("maxweight")) {
+                    // remove spaces as they unnecessarily increase the unique number of values:
+                    String value = KVStorage.cutString(((String) entry.getValue()).
+                            replace(" ", "").replace("bicycle", "bike"));
+                    String key = entry.getKey().replace(':', '_').replace("bicycle", "bike");
+                    boolean fwd = key.contains("forward");
+                    boolean bwd = key.contains("backward");
+                    if (!fwd && !bwd)
+                        list.add(new KVStorage.KeyValue(key, value, true, true));
+                    else
+                        list.add(new KVStorage.KeyValue(key, value, fwd, bwd));
+                }
+            }
+
         way.setTag("key_values", list);
 
         if (!isCalculateWayDistance(way))
@@ -581,7 +610,7 @@ protected void addRestrictionsToGraph() {
         // The restriction type depends on the vehicle, or at least not all restrictions affect every vehicle type.
         // We handle the restrictions for one vehicle after another.
         for (RestrictionTagParser restrictionTagParser : osmParsers.getRestrictionTagParsers()) {
-            LongSet viaWaysUsedByOnlyRestrictions = new LongHashSet();
+            LongSet directedViaWaysUsedByRestrictions = new LongHashSet();
             List> restrictionsWithType = new ArrayList<>(restrictions.size());
             for (Triple r : restrictions) {
                 if (r.second == null)
@@ -593,12 +622,15 @@ protected void addRestrictionsToGraph() {
                         // this relation is ignored by the current restriction tag parser
                         continue;
                     RestrictionConverter.checkIfCompatibleWithRestriction(r.second, res.getRestriction());
-                    // we ignore 'only' via-way restrictions that share the same via way, because these would require adding
-                    // multiple artificial edges, see here: https://github.com/graphhopper/graphhopper/pull/2689#issuecomment-1306769694
-                    if (r.second.isViaWayRestriction() && res.getRestrictionType() == RestrictionType.ONLY)
-                        for (LongCursor viaWay : r.third.getViaWays())
-                            if (!viaWaysUsedByOnlyRestrictions.add(viaWay.value))
-                                throw new OSMRestrictionException("has a member with role 'via' that is also used as 'via' member by another 'only' restriction. GraphHopper cannot handle this.");
+                    // we ignore via-way restrictions that share the same via-way in the same direction, because these would require adding
+                    // multiple artificial edges, see here: https://github.com/graphhopper/graphhopper/pull/2689#issuecomment-1306769694 and #2907
+                    if (r.second.isViaWayRestriction())
+                        for (LongCursor viaWay : r.third.getViaWays()) {
+                            // We simply use the first and last via-node to determine the direction of the way, but for multiple via-ways maybe we need to reconsider this!
+                            long directedViaWay = viaWay.value * (r.second.getViaNodes().get(0) < r.second.getViaNodes().get(r.second.getViaNodes().size() - 1) ? +1 : -1);
+                            if (!directedViaWaysUsedByRestrictions.add(directedViaWay))
+                                throw new OSMRestrictionException("has a member with role 'via' (" + viaWay.value + ") that is also used as 'via' member by another restriction in the same direction. GraphHopper cannot handle this.");
+                        }
                     restrictionsWithType.add(new Pair<>(r.second, res.getRestrictionType()));
                 } catch (OSMRestrictionException e) {
                     warnOfRestriction(r.first, e);
diff --git a/core/src/main/java/com/graphhopper/reader/osm/WaySegmentParser.java b/core/src/main/java/com/graphhopper/reader/osm/WaySegmentParser.java
index dc78de2a9a5..1fe4c696ea9 100644
--- a/core/src/main/java/com/graphhopper/reader/osm/WaySegmentParser.java
+++ b/core/src/main/java/com/graphhopper/reader/osm/WaySegmentParser.java
@@ -69,7 +69,7 @@ public class WaySegmentParser {
     private ElevationProvider elevationProvider = ElevationProvider.NOOP;
     private Predicate wayFilter = way -> true;
     private Predicate splitNodeFilter = node -> false;
-    private WayPreprocessor wayPreprocessor = (way, supplier) -> {
+    private WayPreprocessor wayPreprocessor = (way, coordinateSupplier, nodeTagSupplier) -> {
     };
     private Consumer relationPreprocessor = relation -> {
     };
@@ -263,7 +263,7 @@ public void handleWay(ReaderWay way) {
             List segment = new ArrayList<>(way.getNodes().size());
             for (LongCursor node : way.getNodes())
                 segment.add(new SegmentNode(node.value, nodeData.getId(node.value), nodeData.getTags(node.value)));
-            wayPreprocessor.preprocessWay(way, osmNodeId -> nodeData.getCoordinates(nodeData.getId(osmNodeId)));
+            wayPreprocessor.preprocessWay(way, osmNodeId -> nodeData.getCoordinates(nodeData.getId(osmNodeId)), osmNodeId -> nodeData.getTags(osmNodeId));
             splitWayAtJunctionsAndEmptySections(segment, way);
         }
 
@@ -573,10 +573,14 @@ public interface WayPreprocessor {
          *                           of this node. If elevation is disabled it will be NaN. Returns null if no such OSM
          *                           node exists.
          */
-        void preprocessWay(ReaderWay way, CoordinateSupplier coordinateSupplier);
+        void preprocessWay(ReaderWay way, CoordinateSupplier coordinateSupplier, NodeTagSupplier nodeTagSupplier);
     }
 
     public interface CoordinateSupplier {
         GHPoint3D getCoordinate(long osmNodeId);
     }
+
+    public interface NodeTagSupplier {
+        Map getTags(long osmNodeId);
+    }
 }
diff --git a/core/src/main/java/com/graphhopper/reader/osm/conditional/ConditionalOSMTagInspector.java b/core/src/main/java/com/graphhopper/reader/osm/conditional/ConditionalOSMTagInspector.java
deleted file mode 100644
index eec6ac9ed42..00000000000
--- a/core/src/main/java/com/graphhopper/reader/osm/conditional/ConditionalOSMTagInspector.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- *  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.reader.osm.conditional;
-
-import com.graphhopper.reader.ReaderWay;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.*;
-
-/**
- * Inspects the conditional tags of an OSMWay according to the given conditional tags.
- * 

- * - * @author Robin Boldt - */ -public class ConditionalOSMTagInspector implements ConditionalTagInspector { - private final Logger logger = LoggerFactory.getLogger(getClass()); - private final List tagsToCheck; - private final ConditionalParser permitParser, restrictiveParser; - // enabling by default makes noise but could improve OSM data - private boolean enabledLogs; - - public ConditionalOSMTagInspector(Calendar value, List tagsToCheck, - Set restrictiveValues, Set permittedValues) { - this(Arrays.asList(new DateRangeParser(value)), tagsToCheck, restrictiveValues, permittedValues, false); - } - - public ConditionalOSMTagInspector(List valueParsers, List tagsToCheck, - Set restrictiveValues, Set permittedValues, boolean enabledLogs) { - this.tagsToCheck = new ArrayList<>(tagsToCheck.size()); - for (String tagToCheck : tagsToCheck) { - this.tagsToCheck.add(tagToCheck + ":conditional"); - } - - this.enabledLogs = enabledLogs; - - // enable for debugging purposes only as this is too much - boolean logUnsupportedFeatures = false; - this.permitParser = new ConditionalParser(permittedValues, logUnsupportedFeatures); - this.restrictiveParser = new ConditionalParser(restrictiveValues, logUnsupportedFeatures); - for (ConditionalValueParser cvp : valueParsers) { - permitParser.addConditionalValueParser(cvp); - restrictiveParser.addConditionalValueParser(cvp); - } - } - - public void addValueParser(ConditionalValueParser vp) { - permitParser.addConditionalValueParser(vp); - restrictiveParser.addConditionalValueParser(vp); - } - - @Override - public boolean isRestrictedWayConditionallyPermitted(ReaderWay way) { - return applies(way, true); - } - - @Override - public boolean isPermittedWayConditionallyRestricted(ReaderWay way) { - return applies(way, false); - } - - protected boolean applies(ReaderWay way, boolean checkPermissiveValues) { - for (int index = 0; index < tagsToCheck.size(); index++) { - String tagToCheck = tagsToCheck.get(index); - String val = way.getTag(tagToCheck); - if (val == null || val.isEmpty()) - continue; - - try { - if (checkPermissiveValues) { - if (permitParser.checkCondition(val)) - return true; - } else { - if (restrictiveParser.checkCondition(val)) - return true; - } - - } catch (Exception e) { - if (enabledLogs) { - // log only if no date ala 21:00 as currently date and numbers do not support time precise restrictions - if (!val.contains(":")) - logger.warn("for way " + way.getId() + " could not parse the conditional value '" + val + "' of tag '" + tagToCheck + "'. Exception:" + e.getMessage()); - } - } - } - return false; - } -} diff --git a/core/src/main/java/com/graphhopper/reader/osm/conditional/ConditionalParser.java b/core/src/main/java/com/graphhopper/reader/osm/conditional/ConditionalParser.java deleted file mode 100644 index 1b67a29e08e..00000000000 --- a/core/src/main/java/com/graphhopper/reader/osm/conditional/ConditionalParser.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * 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.reader.osm.conditional; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.List; -import java.util.Set; - -/** - * Parses a conditional tag according to - * http://wiki.openstreetmap.org/wiki/Conditional_restrictions. - *

- * - * @author Robin Boldt - */ -public class ConditionalParser { - private final Logger logger = LoggerFactory.getLogger(getClass()); - private final Set restrictedTags; - private final List valueParsers = new ArrayList<>(5); - private final boolean enabledLogs; - - public ConditionalParser(Set restrictedTags) { - this(restrictedTags, false); - } - - public ConditionalParser(Set restrictedTags, boolean enabledLogs) { - // use map => key & type (date vs. double) - this.restrictedTags = restrictedTags; - this.enabledLogs = enabledLogs; - } - - public static ConditionalValueParser createNumberParser(final String assertKey, final Number obj) { - return new ConditionalValueParser() { - @Override - public ConditionState checkCondition(String conditionalValue) throws ParseException { - int indexLT = conditionalValue.indexOf("<"); - if (indexLT > 0 && conditionalValue.length() > 2) { - final String key = conditionalValue.substring(0, indexLT).trim(); - if (!assertKey.equals(key)) - return ConditionState.INVALID; - - if (conditionalValue.charAt(indexLT + 1) == '=') - indexLT++; - final double value = parseNumber(conditionalValue.substring(indexLT + 1)); - if (obj.doubleValue() < value) - return ConditionState.TRUE; - else - return ConditionState.FALSE; - } - - int indexGT = conditionalValue.indexOf(">"); - if (indexGT > 0 && conditionalValue.length() > 2) { - final String key = conditionalValue.substring(0, indexGT).trim(); - if (!assertKey.equals(key)) - return ConditionState.INVALID; - - // for now just ignore equals sign - if (conditionalValue.charAt(indexGT + 1) == '=') - indexGT++; - - final double value = parseNumber(conditionalValue.substring(indexGT + 1)); - if (obj.doubleValue() > value) - return ConditionState.TRUE; - else - return ConditionState.FALSE; - } - - return ConditionState.INVALID; - } - }; - } - - /** - * This method adds a new value parser. The one added last has a higher priority. - */ - public ConditionalParser addConditionalValueParser(ConditionalValueParser vp) { - valueParsers.add(0, vp); - return this; - } - - public ConditionalParser setConditionalValueParser(ConditionalValueParser vp) { - valueParsers.clear(); - valueParsers.add(vp); - return this; - } - - public boolean checkCondition(String conditionalTag) throws ParseException { - if (conditionalTag == null || conditionalTag.isEmpty() || !conditionalTag.contains("@")) - return false; - - if (conditionalTag.contains(";")) { - if (enabledLogs) - logger.warn("We do not support multiple conditions yet: " + conditionalTag); - return false; - } - - String[] conditionalArr = conditionalTag.split("@"); - - if (conditionalArr.length != 2) - throw new IllegalStateException("could not split this condition: " + conditionalTag); - - String restrictiveValue = conditionalArr[0].trim(); - if (!restrictedTags.contains(restrictiveValue)) - return false; - - String conditionalValue = conditionalArr[1]; - conditionalValue = conditionalValue.replace('(', ' '); - conditionalValue = conditionalValue.replace(')', ' '); - conditionalValue = conditionalValue.trim(); - - for (ConditionalValueParser valueParser : valueParsers) { - ConditionalValueParser.ConditionState c = valueParser.checkCondition(conditionalValue); - if (c.isValid()) - return c.isCheckPassed(); - } - return false; - } - - protected static double parseNumber(String str) { - int untilIndex = str.length() - 1; - for (; untilIndex >= 0; untilIndex--) { - if (Character.isDigit(str.charAt(untilIndex))) - break; - } - return Double.parseDouble(str.substring(0, untilIndex + 1)); - } -} diff --git a/core/src/main/java/com/graphhopper/reader/osm/conditional/ConditionalTagInspector.java b/core/src/main/java/com/graphhopper/reader/osm/conditional/ConditionalTagInspector.java deleted file mode 100644 index 7b5e54eaeb8..00000000000 --- a/core/src/main/java/com/graphhopper/reader/osm/conditional/ConditionalTagInspector.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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.reader.osm.conditional; - -import com.graphhopper.reader.ReaderWay; - -/** - * @author Peter Karich - */ -public interface ConditionalTagInspector { - boolean isRestrictedWayConditionallyPermitted(ReaderWay way); - - boolean isPermittedWayConditionallyRestricted(ReaderWay way); -} diff --git a/core/src/main/java/com/graphhopper/reader/osm/conditional/DateRangeParser.java b/core/src/main/java/com/graphhopper/reader/osm/conditional/DateRangeParser.java index dc2d865dab7..fc80d22caae 100644 --- a/core/src/main/java/com/graphhopper/reader/osm/conditional/DateRangeParser.java +++ b/core/src/main/java/com/graphhopper/reader/osm/conditional/DateRangeParser.java @@ -48,7 +48,7 @@ public class DateRangeParser implements ConditionalValueParser { private Calendar date; - public DateRangeParser() { + DateRangeParser() { this(createCalendar()); } @@ -104,9 +104,9 @@ static ParsedCalendar parseDateString(String dateString) throws ParseException { return parsedCalendar; } - public DateRange getRange(String dateRangeString) throws ParseException { + DateRange getRange(String dateRangeString) throws ParseException { if (dateRangeString == null || dateRangeString.isEmpty()) - throw new IllegalArgumentException("Passing empty Strings is not allowed"); + return null; String[] dateArr = dateRangeString.split("-"); if (dateArr.length > 2 || dateArr.length < 1) @@ -122,7 +122,11 @@ public DateRange getRange(String dateRangeString) throws ParseException { // to = new ParsedCalendar(from.parseType, (Calendar) from.parsedCalendar.clone()); to = parseDateString(dateArr[0]); - return new DateRange(from, to); + try { + return new DateRange(from, to); + } catch (IllegalArgumentException ex) { + return null; + } } @Override diff --git a/core/src/main/java/com/graphhopper/routing/AlternativeRoute.java b/core/src/main/java/com/graphhopper/routing/AlternativeRoute.java index 605fbc44ef1..dbac364d8ea 100644 --- a/core/src/main/java/com/graphhopper/routing/AlternativeRoute.java +++ b/core/src/main/java/com/graphhopper/routing/AlternativeRoute.java @@ -262,7 +262,8 @@ public boolean apply(final int traversalId, final SPTEntry fromSPTEntry) { return true; // (1) skip too long paths - final double weight = fromSPTEntry.getWeightOfVisitedPath() + toSPTEntry.getWeightOfVisitedPath(); + final double weight = fromSPTEntry.getWeightOfVisitedPath() + toSPTEntry.getWeightOfVisitedPath() + + weighting.calcTurnWeight(fromSPTEntry.edge, fromSPTEntry.adjNode, toSPTEntry.edge); if (weight > maxWeight) return true; @@ -341,7 +342,7 @@ public boolean apply(final int traversalId, final SPTEntry fromSPTEntry) { Collections.sort(alternatives, ALT_COMPARATOR); if (alternatives.get(0) != bestAlt) - throw new IllegalStateException("best path should be always first entry"); + throw new IllegalStateException("best path should be always first entry " + bestAlt.path.getWeight() + " vs " + alternatives.get(0).path.getWeight()); if (alternatives.size() > maxPaths) alternatives.subList(maxPaths, alternatives.size()).clear(); diff --git a/core/src/main/java/com/graphhopper/routing/AlternativeRouteCH.java b/core/src/main/java/com/graphhopper/routing/AlternativeRouteCH.java index 7b32b8f941e..fd199d57ae9 100644 --- a/core/src/main/java/com/graphhopper/routing/AlternativeRouteCH.java +++ b/core/src/main/java/com/graphhopper/routing/AlternativeRouteCH.java @@ -114,10 +114,12 @@ List calcAlternatives(final int s, final int t) { // Okay, now we want the s -> v -> t shortest via-path, so we route s -> v and v -> t // and glue them together. DijkstraBidirectionCH svRouter = new DijkstraBidirectionCH(graph); + svRouter.setPathExtractorSupplier(this::createPathExtractor); final Path svPath = svRouter.calcPath(s, v); extraVisitedNodes += svRouter.getVisitedNodes(); DijkstraBidirectionCH vtRouter = new DijkstraBidirectionCH(graph); + vtRouter.setPathExtractorSupplier(this::createPathExtractor); final Path vtPath = vtRouter.calcPath(v, t); Path path = concat(graph.getBaseGraph(), svPath, vtPath); extraVisitedNodes += vtRouter.getVisitedNodes(); @@ -192,6 +194,7 @@ private boolean tTest(Path path, int vIndex) { int fromNode = getPreviousNodeTMetersAway(path, vIndex, T); int toNode = getNextNodeTMetersAway(path, vIndex, T); DijkstraBidirectionCH tRouter = new DijkstraBidirectionCH(graph); + tRouter.setPathExtractorSupplier(this::createPathExtractor); Path tPath = tRouter.calcPath(fromNode, toNode); extraVisitedNodes += tRouter.getVisitedNodes(); IntIndexedContainer tNodes = tPath.calcNodes(); diff --git a/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java b/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java index accbf5653d8..a1b6b5c5ac4 100644 --- a/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java +++ b/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java @@ -19,9 +19,9 @@ package com.graphhopper.routing; import com.graphhopper.config.Profile; -import com.graphhopper.routing.ev.*; +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.TurnRestriction; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.VehicleEncodedValues; import com.graphhopper.routing.weighting.DefaultTurnCostProvider; import com.graphhopper.routing.weighting.TurnCostProvider; import com.graphhopper.routing.weighting.Weighting; @@ -33,7 +33,6 @@ import com.graphhopper.util.Parameters; import static com.graphhopper.routing.weighting.TurnCostProvider.NO_TURN_COST_PROVIDER; -import static com.graphhopper.routing.weighting.Weighting.INFINITE_U_TURN_COSTS; import static com.graphhopper.util.Helper.toLowerCase; public class DefaultWeightingFactory implements WeightingFactory { @@ -55,13 +54,12 @@ public Weighting createWeighting(Profile profile, PMap requestHints, boolean dis hints.putAll(profile.getHints()); hints.putAll(requestHints); - final String vehicle = profile.getVehicle(); TurnCostProvider turnCostProvider; - if (profile.isTurnCosts() && !disableTurnCosts) { - BooleanEncodedValue turnRestrictionEnc = encodingManager.getTurnBooleanEncodedValue(TurnRestriction.key(vehicle)); + if (profile.hasTurnCosts() && !disableTurnCosts) { + BooleanEncodedValue turnRestrictionEnc = encodingManager.getTurnBooleanEncodedValue(TurnRestriction.key(profile.getName())); if (turnRestrictionEnc == null) - throw new IllegalArgumentException("Vehicle " + vehicle + " does not support turn costs"); - int uTurnCosts = hints.getInt(Parameters.Routing.U_TURN_COSTS, INFINITE_U_TURN_COSTS); + throw new IllegalArgumentException("Cannot find turn restriction encoded value for " + profile.getName()); + int uTurnCosts = hints.getInt(Parameters.Routing.U_TURN_COSTS, profile.getTurnCostsConfig().getUTurnCosts()); turnCostProvider = new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage(), uTurnCosts); } else { turnCostProvider = NO_TURN_COST_PROVIDER; @@ -72,18 +70,13 @@ public Weighting createWeighting(Profile profile, PMap requestHints, boolean dis throw new IllegalArgumentException("You have to specify a weighting"); Weighting weighting = null; - BooleanEncodedValue accessEnc = encodingManager.getBooleanEncodedValue(VehicleAccess.key(vehicle)); - DecimalEncodedValue speedEnc = encodingManager.getDecimalEncodedValue(VehicleSpeed.key(vehicle)); - DecimalEncodedValue priorityEnc = encodingManager.hasEncodedValue(VehiclePriority.key(vehicle)) - ? encodingManager.getDecimalEncodedValue(VehiclePriority.key(vehicle)) - : null; if (CustomWeighting.NAME.equalsIgnoreCase(weightingStr)) { final CustomModel queryCustomModel = requestHints.getObject(CustomModel.KEY, null); final CustomModel mergedCustomModel = CustomModel.merge(profile.getCustomModel(), queryCustomModel); if (requestHints.has(Parameters.Routing.HEADING_PENALTY)) mergedCustomModel.setHeadingPenalty(requestHints.getDouble(Parameters.Routing.HEADING_PENALTY, Parameters.Routing.DEFAULT_HEADING_PENALTY)); - weighting = CustomModelParser.createWeighting(accessEnc, speedEnc, - priorityEnc, encodingManager, turnCostProvider, mergedCustomModel); + weighting = CustomModelParser.createWeighting(encodingManager, turnCostProvider, mergedCustomModel); + } else if ("shortest".equalsIgnoreCase(weightingStr)) { throw new IllegalArgumentException("Instead of weighting=shortest use weighting=custom with a high distance_influence"); } else if ("fastest".equalsIgnoreCase(weightingStr)) { @@ -101,7 +94,4 @@ public Weighting createWeighting(Profile profile, PMap requestHints, boolean dis return weighting; } - public boolean isOutdoorVehicle(String name) { - return VehicleEncodedValues.OUTDOOR_VEHICLES.contains(name); - } } diff --git a/core/src/main/java/com/graphhopper/routing/DijkstraOneToMany.java b/core/src/main/java/com/graphhopper/routing/DijkstraOneToMany.java index 7de2ab6626e..65d7c8ab4b9 100644 --- a/core/src/main/java/com/graphhopper/routing/DijkstraOneToMany.java +++ b/core/src/main/java/com/graphhopper/routing/DijkstraOneToMany.java @@ -67,6 +67,7 @@ public DijkstraOneToMany(Graph graph, Weighting weighting, TraversalMode tMode) @Override public Path calcPath(int from, int to) { + setupFinishTime(); fromNode = from; endNode = findEndNode(from, to); if (endNode < 0 || isWeightLimitExceeded()) { diff --git a/core/src/main/java/com/graphhopper/routing/InstructionsFromEdges.java b/core/src/main/java/com/graphhopper/routing/InstructionsFromEdges.java index b63f89c7cf9..e70109348e2 100644 --- a/core/src/main/java/com/graphhopper/routing/InstructionsFromEdges.java +++ b/core/src/main/java/com/graphhopper/routing/InstructionsFromEdges.java @@ -44,6 +44,7 @@ public class InstructionsFromEdges implements Path.EdgeVisitor { private final BooleanEncodedValue roundaboutEnc; private final BooleanEncodedValue roadClassLinkEnc; private final EnumEncodedValue roadClassEnc; + private final IntEncodedValue lanesEnc; private final DecimalEncodedValue maxSpeedEnc; /* @@ -88,6 +89,7 @@ public InstructionsFromEdges(Graph graph, Weighting weighting, EncodedValueLooku this.roadClassEnc = evLookup.getEnumEncodedValue(RoadClass.KEY, RoadClass.class); this.roadClassLinkEnc = evLookup.getBooleanEncodedValue(RoadClassLink.KEY); this.maxSpeedEnc = evLookup.getDecimalEncodedValue(MaxSpeed.KEY); + this.lanesEnc = evLookup.hasEncodedValue(Lanes.KEY) ? evLookup.getIntEncodedValue(Lanes.KEY) : null; this.nodeAccess = graph.getNodeAccess(); this.ways = ways; prevNode = -1; @@ -144,6 +146,7 @@ public void next(EdgeIteratorState edge, int index, int prevEdgeId) { final String ref = (String) edge.getValue(STREET_REF); final String destination = (String) edge.getValue(STREET_DESTINATION); // getValue is fast if it does not exist in edge final String destinationRef = (String) edge.getValue(STREET_DESTINATION_REF); + final String motorwayJunction = (String) edge.getValue(MOTORWAY_JUNCTION); if ((prevInstruction == null) && (!isRoundabout)) // very first instruction (if not in Roundabout) { int sign = Instruction.CONTINUE_ON_STREET; @@ -151,6 +154,7 @@ public void next(EdgeIteratorState edge, int index, int prevEdgeId) { prevInstruction.setExtraInfo(STREET_REF, ref); prevInstruction.setExtraInfo(STREET_DESTINATION, destination); prevInstruction.setExtraInfo(STREET_DESTINATION_REF, destinationRef); + prevInstruction.setExtraInfo(MOTORWAY_JUNCTION, motorwayJunction); double startLat = nodeAccess.getLat(baseNode); double startLon = nodeAccess.getLon(baseNode); double heading = AngleCalc.ANGLE_CALC.calcAzimuth(startLat, startLon, latitude, longitude); @@ -213,6 +217,7 @@ public void next(EdgeIteratorState edge, int index, int prevEdgeId) { prevInstruction.setExtraInfo(STREET_REF, ref); prevInstruction.setExtraInfo(STREET_DESTINATION, destination); prevInstruction.setExtraInfo(STREET_DESTINATION_REF, destinationRef); + prevInstruction.setExtraInfo(MOTORWAY_JUNCTION, motorwayJunction); // calc angle between roundabout entrance and exit double orientation = AngleCalc.ANGLE_CALC.calcOrientation(prevLat, prevLon, latitude, longitude); @@ -257,7 +262,7 @@ public void next(EdgeIteratorState edge, int index, int prevEdgeId) { && (Math.abs(sign) == Instruction.TURN_SLIGHT_RIGHT || Math.abs(sign) == Instruction.TURN_RIGHT || Math.abs(sign) == Instruction.TURN_SHARP_RIGHT) && (Math.abs(prevInstruction.getSign()) == Instruction.TURN_SLIGHT_RIGHT || Math.abs(prevInstruction.getSign()) == Instruction.TURN_RIGHT || Math.abs(prevInstruction.getSign()) == Instruction.TURN_SHARP_RIGHT) && Double.isFinite(weighting.calcEdgeWeight(edge, false)) != Double.isFinite(weighting.calcEdgeWeight(edge, true)) - && InstructionsHelper.isNameSimilar(prevInstructionName, name)) { + && InstructionsHelper.isSameName(prevInstructionName, name)) { // Chances are good that this is a u-turn, we only need to check if the orientation matches GHPoint point = InstructionsHelper.getPointForOrientationCalculation(edge, nodeAccess); double lat = point.getLat(); @@ -273,7 +278,6 @@ public void next(EdgeIteratorState edge, int index, int prevEdgeId) { uTurnType = Instruction.U_TURN_RIGHT; } } - } if (isUTurn) { @@ -289,6 +293,7 @@ public void next(EdgeIteratorState edge, int index, int prevEdgeId) { prevInstruction.setExtraInfo(STREET_REF, ref); prevInstruction.setExtraInfo(STREET_DESTINATION, destination); prevInstruction.setExtraInfo(STREET_DESTINATION_REF, destinationRef); + prevInstruction.setExtraInfo(MOTORWAY_JUNCTION, motorwayJunction); } // Update the prevName, since we don't always create an instruction on name changes the previous // name can be an old name. This leads to incorrect turn instructions due to name changes @@ -347,7 +352,7 @@ private int getTurn(EdgeIteratorState edge, int baseNode, int prevNode, int adjN // there is no other turn possible if (nrOfPossibleTurns <= 1) { - if (Math.abs(sign) > 1 && outgoingEdges.getVisibleTurns() > 1) { + if (Math.abs(sign) > 1 && outgoingEdges.getVisibleTurns() > 1 && !outgoingEdges.mergedOrSplitWay(lanesEnc)) { // This is an actual turn because |sign| > 1 // There could be some confusion, if we would not create a turn instruction, even though it is the only // possible turn, also see #1048 @@ -361,7 +366,8 @@ private int getTurn(EdgeIteratorState edge, int baseNode, int prevNode, int adjN if (Math.abs(sign) > 1) { // Don't show an instruction if the user is following a street, even though the street is // bending. We should only do this, if following the street is the obvious choice. - if (InstructionsHelper.isNameSimilar(name, prevName) && outgoingEdges.outgoingEdgesAreSlowerByFactor(2)) { + if (InstructionsHelper.isSameName(name, prevName) && outgoingEdges.outgoingEdgesAreSlowerByFactor(2) + || outgoingEdges.mergedOrSplitWay(lanesEnc)) { return Instruction.IGNORE; } @@ -394,9 +400,9 @@ private int getTurn(EdgeIteratorState edge, int baseNode, int prevNode, int adjN // For _links, comparing flags works quite good, as links usually have different speeds => different flags if (otherContinue != null) { // We are at a fork - if (!InstructionsHelper.isNameSimilar(name, prevName) - || !InstructionsHelper.isNameSimilar(destinationAndRef, prevDestinationAndRef) - || InstructionsHelper.isNameSimilar(otherContinue.getName(), prevName) + if (!InstructionsHelper.isSameName(name, prevName) + || !InstructionsHelper.isSameName(destinationAndRef, prevDestinationAndRef) + || InstructionsHelper.isSameName(otherContinue.getName(), prevName) || !outgoingEdgesAreSlower) { final RoadClass roadClass = edge.get(roadClassEnc); @@ -417,7 +423,7 @@ private int getTurn(EdgeIteratorState edge, int baseNode, int prevNode, int adjN double otherDelta = InstructionsHelper.calculateOrientationDelta(prevLat, prevLon, tmpPoint.getLat(), tmpPoint.getLon(), prevOrientation); // This is required to avoid keep left/right on the motorway at off-ramps/motorway_links - if (Math.abs(delta) < .1 && Math.abs(otherDelta) > .15 && InstructionsHelper.isNameSimilar(name, prevName)) { + if (Math.abs(delta) < .1 && Math.abs(otherDelta) > .15 && InstructionsHelper.isSameName(name, prevName)) { return Instruction.CONTINUE_ON_STREET; } @@ -429,7 +435,9 @@ private int getTurn(EdgeIteratorState edge, int baseNode, int prevNode, int adjN } } - if (!outgoingEdgesAreSlower && (Math.abs(delta) > .6 || outgoingEdges.isLeavingCurrentStreet(prevName, name))) { + if (!outgoingEdgesAreSlower + && !outgoingEdges.mergedOrSplitWay(lanesEnc) + && (Math.abs(delta) > .6 || outgoingEdges.isLeavingCurrentStreet(prevName, name))) { // Leave the current road -> create instruction return sign; } diff --git a/core/src/main/java/com/graphhopper/routing/InstructionsHelper.java b/core/src/main/java/com/graphhopper/routing/InstructionsHelper.java index 68186ff175d..5d6ff85858e 100644 --- a/core/src/main/java/com/graphhopper/routing/InstructionsHelper.java +++ b/core/src/main/java/com/graphhopper/routing/InstructionsHelper.java @@ -60,7 +60,7 @@ static int calculateSign(double prevLatitude, double prevLongitude, double latit return Instruction.TURN_SHARP_RIGHT; } - static boolean isNameSimilar(String name1, String name2) { + static boolean isSameName(String name1, String name2) { // We don't want two empty names to be similar (they usually don't have names if they are random tracks) if (name1 == null || name2 == null || name1.isEmpty() || name2.isEmpty()) return false; @@ -80,4 +80,4 @@ static GHPoint getPointForOrientationCalculation(EdgeIteratorState edgeIteratorS } return new GHPoint(tmpLat, tmpLon); } -} \ No newline at end of file +} diff --git a/core/src/main/java/com/graphhopper/routing/InstructionsOutgoingEdges.java b/core/src/main/java/com/graphhopper/routing/InstructionsOutgoingEdges.java index ac2be1e2ba8..b27137e2561 100644 --- a/core/src/main/java/com/graphhopper/routing/InstructionsOutgoingEdges.java +++ b/core/src/main/java/com/graphhopper/routing/InstructionsOutgoingEdges.java @@ -17,10 +17,7 @@ */ package com.graphhopper.routing; -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.EnumEncodedValue; -import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.storage.NodeAccess; import com.graphhopper.util.EdgeExplorer; @@ -58,15 +55,17 @@ class InstructionsOutgoingEdges { private final EdgeIteratorState prevEdge; private final EdgeIteratorState currentEdge; - // Outgoing edges that we would be allowed to turn on + // edges that one can turn onto private final List allowedAlternativeTurns; - // All outgoing edges, including oneways in the wrong direction + // edges, including oneways in the wrong direction private final List visibleAlternativeTurns; private final DecimalEncodedValue maxSpeedEnc; private final EnumEncodedValue roadClassEnc; private final BooleanEncodedValue roadClassLinkEnc; private final NodeAccess nodeAccess; private final Weighting weighting; + private final int baseNode; + private final EdgeExplorer allExplorer; public InstructionsOutgoingEdges(EdgeIteratorState prevEdge, EdgeIteratorState currentEdge, @@ -86,6 +85,8 @@ public InstructionsOutgoingEdges(EdgeIteratorState prevEdge, this.roadClassEnc = roadClassEnc; this.roadClassLinkEnc = roadClassLinkEnc; this.nodeAccess = nodeAccess; + this.baseNode = baseNode; + this.allExplorer = allExplorer; visibleAlternativeTurns = new ArrayList<>(); allowedAlternativeTurns = new ArrayList<>(); @@ -179,7 +180,7 @@ public EdgeIteratorState getOtherContinue(double prevLat, double prevLon, double * If either of these properties is true, we can be quite certain that a turn instruction should be provided. */ public boolean isLeavingCurrentStreet(String prevName, String name) { - if (InstructionsHelper.isNameSimilar(name, prevName)) { + if (InstructionsHelper.isSameName(name, prevName)) { return false; } @@ -187,11 +188,11 @@ public boolean isLeavingCurrentStreet(String prevName, String name) { for (EdgeIteratorState edge : allowedAlternativeTurns) { String edgeName = edge.getName(); // leave the current street - if (InstructionsHelper.isNameSimilar(prevName, edgeName) || (roadClassOrLinkChange && isTheSameRoadClassAndLink(prevEdge, edge))) { + if (InstructionsHelper.isSameName(prevName, edgeName) || (roadClassOrLinkChange && isTheSameRoadClassAndLink(prevEdge, edge))) { return true; } // enter a different street - if (InstructionsHelper.isNameSimilar(name, edgeName) || (roadClassOrLinkChange && isTheSameRoadClassAndLink(currentEdge, edge))) { + if (InstructionsHelper.isSameName(name, edgeName) || (roadClassOrLinkChange && isTheSameRoadClassAndLink(currentEdge, edge))) { return true; } } @@ -202,4 +203,54 @@ private boolean isTheSameRoadClassAndLink(EdgeIteratorState edge1, EdgeIteratorS return edge1.get(roadClassEnc) == edge2.get(roadClassEnc) && edge1.get(roadClassLinkEnc) == edge2.get(roadClassLinkEnc); } + // for cases like in #2946 we should not create instructions as they are only "tagging artifacts" + public boolean mergedOrSplitWay(IntEncodedValue lanesEnc) { + if (lanesEnc == null) return false; + + String name = currentEdge.getName(); + RoadClass roadClass = currentEdge.get(roadClassEnc); + if (!InstructionsHelper.isSameName(name, prevEdge.getName()) || roadClass != prevEdge.get(roadClassEnc)) + return false; + + EdgeIterator edgeIter = allExplorer.setBaseNode(baseNode); + EdgeIteratorState otherEdge = null; + while (edgeIter.next()) { + if (currentEdge.getEdge() != edgeIter.getEdge() + && prevEdge.getEdge() != edgeIter.getEdge() + && roadClass == edgeIter.get(roadClassEnc) + && InstructionsHelper.isSameName(name, edgeIter.getName()) + && (Double.isFinite(weighting.calcEdgeWeight(edgeIter, false)) + || Double.isFinite(weighting.calcEdgeWeight(edgeIter, true)))) { + if (otherEdge != null) return false; // too many possible other edges + otherEdge = edgeIter.detach(false); + } + } + if (otherEdge == null) return false; + + if (Double.isFinite(weighting.calcEdgeWeight(currentEdge, true))) { + // assume two ways are merged into one way + // -> prev -> + // <- edge -> + // -> other -> + if (Double.isFinite(weighting.calcEdgeWeight(prevEdge, true))) return false; + // otherEdge has direction from junction outwards + if (!Double.isFinite(weighting.calcEdgeWeight(otherEdge, false))) return false; + if (Double.isFinite(weighting.calcEdgeWeight(otherEdge, true))) return false; + + int delta = Math.abs(prevEdge.get(lanesEnc) + otherEdge.get(lanesEnc) - currentEdge.get(lanesEnc)); + return delta <= 1; + } + + // assume one way is split into two ways + // -> edge -> + // <- prev -> + // -> other -> + if (!Double.isFinite(weighting.calcEdgeWeight(prevEdge, true))) return false; + // otherEdge has direction from junction outwards + if (Double.isFinite(weighting.calcEdgeWeight(otherEdge, false))) return false; + if (!Double.isFinite(weighting.calcEdgeWeight(otherEdge, true))) return false; + + int delta = prevEdge.get(lanesEnc) - (currentEdge.get(lanesEnc) + otherEdge.get(lanesEnc)); + return delta <= 1; + } } diff --git a/core/src/main/java/com/graphhopper/routing/OSMReaderConfig.java b/core/src/main/java/com/graphhopper/routing/OSMReaderConfig.java index a1be72f6221..7440bfefc13 100644 --- a/core/src/main/java/com/graphhopper/routing/OSMReaderConfig.java +++ b/core/src/main/java/com/graphhopper/routing/OSMReaderConfig.java @@ -25,7 +25,7 @@ public class OSMReaderConfig { private List ignoredHighways = new ArrayList<>(); private boolean parseWayNames = true; private String preferredLanguage = ""; - private double maxWayPointDistance = 1; + private double maxWayPointDistance = 0.5; private double elevationMaxWayPointDistance = Double.MAX_VALUE; private String smoothElevation = ""; diff --git a/core/src/main/java/com/graphhopper/routing/Router.java b/core/src/main/java/com/graphhopper/routing/Router.java index 595dcd76da9..80938828592 100644 --- a/core/src/main/java/com/graphhopper/routing/Router.java +++ b/core/src/main/java/com/graphhopper/routing/Router.java @@ -49,7 +49,7 @@ import java.util.*; -import static com.graphhopper.routing.weighting.Weighting.INFINITE_U_TURN_COSTS; +import static com.graphhopper.config.TurnCostsConfig.INFINITE_U_TURN_COSTS; import static com.graphhopper.util.DistanceCalcEarth.DIST_EARTH; import static com.graphhopper.util.Parameters.Algorithms.ALT_ROUTE; import static com.graphhopper.util.Parameters.Algorithms.ROUND_TRIP; @@ -296,7 +296,7 @@ protected GHResponse routeVia(GHRequest request, Solver solver) { private PathMerger createPathMerger(GHRequest request, Weighting weighting, Graph graph) { boolean enableInstructions = request.getHints().getBool(Parameters.Routing.INSTRUCTIONS, routerConfig.isInstructionsEnabled()); boolean calcPoints = request.getHints().getBool(Parameters.Routing.CALC_POINTS, routerConfig.isCalcPoints()); - double wayPointMaxDistance = request.getHints().getDouble(Parameters.Routing.WAY_POINT_MAX_DISTANCE, 1d); + double wayPointMaxDistance = request.getHints().getDouble(Parameters.Routing.WAY_POINT_MAX_DISTANCE, 0.5); double elevationWayPointMaxDistance = request.getHints().getDouble(ELEVATION_WAY_POINT_MAX_DISTANCE, routerConfig.getElevationWayPointMaxDistance()); RamerDouglasPeucker peucker = new RamerDouglasPeucker(). @@ -387,14 +387,14 @@ protected Profile getProfile() { } protected void checkProfileCompatibility() { - if (!profile.isTurnCosts() && !request.getCurbsides().isEmpty()) + if (!profile.hasTurnCosts() && !request.getCurbsides().isEmpty()) throw new IllegalArgumentException("To make use of the " + CURBSIDE + " parameter you need to use a profile that supports turn costs" + "\nThe following profiles do support turn costs: " + getTurnCostProfiles()); if (request.getCustomModel() != null && !CustomWeighting.NAME.equals(profile.getWeighting())) throw new IllegalArgumentException("The requested profile '" + request.getProfile() + "' cannot be used with `custom_model`, because it has weighting=" + profile.getWeighting()); final int uTurnCostsInt = request.getHints().getInt(Parameters.Routing.U_TURN_COSTS, INFINITE_U_TURN_COSTS); - if (uTurnCostsInt != INFINITE_U_TURN_COSTS && !profile.isTurnCosts()) { + if (uTurnCostsInt != INFINITE_U_TURN_COSTS && !profile.hasTurnCosts()) { throw new IllegalArgumentException("Finite u-turn costs can only be used for edge-based routing, you need to use a profile that" + " supports turn costs. Currently the following profiles that support turn costs are available: " + getTurnCostProfiles()); } @@ -416,7 +416,7 @@ public DirectedEdgeFilter createDirectedEdgeFilter() { private List getTurnCostProfiles() { List turnCostProfiles = new ArrayList<>(); for (Profile p : profilesByName.values()) { - if (p.isTurnCosts()) { + if (p.hasTurnCosts()) { turnCostProfiles.add(p.getName()); } } @@ -525,7 +525,7 @@ public FlexiblePathCalculator createPathCalculator(QueryGraph queryGraph) { protected AlgorithmOptions getAlgoOpts() { AlgorithmOptions algoOpts = new AlgorithmOptions(). setAlgorithm(request.getAlgorithm()). - setTraversalMode(profile.isTurnCosts() ? TraversalMode.EDGE_BASED : TraversalMode.NODE_BASED). + setTraversalMode(profile.hasTurnCosts() ? TraversalMode.EDGE_BASED : TraversalMode.NODE_BASED). setMaxVisitedNodes(getMaxVisitedNodes(request.getHints())). setTimeoutMillis(getTimeoutMillis(request.getHints())). setHints(request.getHints()); diff --git a/core/src/main/java/com/graphhopper/routing/TestProfiles.java b/core/src/main/java/com/graphhopper/routing/TestProfiles.java new file mode 100644 index 00000000000..7fa8b7a1958 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/TestProfiles.java @@ -0,0 +1,69 @@ +/* + * 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.routing; + +import com.graphhopper.config.Profile; +import com.graphhopper.json.Statement; +import com.graphhopper.util.CustomModel; + +import static com.graphhopper.json.Statement.Else; +import static com.graphhopper.json.Statement.If; +import static com.graphhopper.json.Statement.Op.LIMIT; +import static com.graphhopper.json.Statement.Op.MULTIPLY; + +public class TestProfiles { + public static Profile constantSpeed(String name) { + return constantSpeed(name, 60); + } + + public static Profile constantSpeed(String name, double speed) { + Profile profile = new Profile(name); + CustomModel customModel = new CustomModel(); + customModel.addToSpeed(Statement.If("true", Statement.Op.LIMIT, String.valueOf(speed))); + profile.setCustomModel(customModel); + return profile; + } + + public static Profile accessAndSpeed(String vehicle) { + return accessAndSpeed(vehicle, vehicle); + } + + public static Profile accessAndSpeed(String name, String vehicle) { + Profile profile = new Profile(name); + CustomModel customModel = new CustomModel(). + addToPriority(If("!" + vehicle + "_access", MULTIPLY, "0")). + addToSpeed(If("true", LIMIT, vehicle + "_average_speed")); + profile.setCustomModel(customModel); + return profile; + } + + public static Profile accessSpeedAndPriority(String vehicle) { + return accessSpeedAndPriority(vehicle, vehicle); + } + + public static Profile accessSpeedAndPriority(String name, String vehicle) { + Profile profile = new Profile(name); + CustomModel customModel = new CustomModel(). + addToPriority(If(vehicle + "_access", MULTIPLY, vehicle + "_priority")). + addToPriority(Else(MULTIPLY, "0")). + addToSpeed(If("true", LIMIT, vehicle + "_average_speed")); + profile.setCustomModel(customModel); + return profile; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/ch/CHPreparationGraph.java b/core/src/main/java/com/graphhopper/routing/ch/CHPreparationGraph.java index a5107e327d3..bd3868f8432 100644 --- a/core/src/main/java/com/graphhopper/routing/ch/CHPreparationGraph.java +++ b/core/src/main/java/com/graphhopper/routing/ch/CHPreparationGraph.java @@ -110,8 +110,7 @@ public static void buildFromGraph(CHPreparationGraph prepareGraph, Graph graph, public static TurnCostFunction buildTurnCostFunctionFromTurnCostStorage(Graph graph, Weighting weighting) { // At some point we used an optimized version where we copied the turn costs to sorted arrays // temporarily. This seemed to be around 25% faster according to measurements on the Bavaria - // map, but later this turned out to be no real improvement for large maps (planet, Europe, a - // and even Germany). See also #2084 + // map, but for bigger maps the improvement is less, around 10% for planet. See also #2084 return weighting::calcTurnWeight; } diff --git a/core/src/main/java/com/graphhopper/routing/ev/BikeTemporalAccess.java b/core/src/main/java/com/graphhopper/routing/ev/BikeTemporalAccess.java new file mode 100644 index 00000000000..948abbb9985 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/ev/BikeTemporalAccess.java @@ -0,0 +1,47 @@ +/* + * 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.routing.ev; + +import com.graphhopper.util.Helper; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; + +/** + * Stores temporary so-called conditional restrictions from access:conditional and other conditional + * tags affecting bikes. See OSMRoadAccessConditionalParser. + */ +public enum BikeTemporalAccess { + + MISSING, YES, NO; + + public static final Collection CONDITIONALS = new HashSet<>(Arrays.asList("access:conditional", + "vehicle:conditional", "bicycle:conditional")); + public static final String KEY = "bike_temporal_access"; + + public static EnumEncodedValue create() { + return new EnumEncodedValue<>(KEY, BikeTemporalAccess.class); + } + + @Override + public String toString() { + return Helper.toLowerCase(super.toString()); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/ev/BusAccess.java b/core/src/main/java/com/graphhopper/routing/ev/BusAccess.java new file mode 100644 index 00000000000..10f3e9d2f86 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/ev/BusAccess.java @@ -0,0 +1,10 @@ +package com.graphhopper.routing.ev; + +public class BusAccess { + public final static String KEY = "bus_access"; + + public static BooleanEncodedValue create() { + return new SimpleBooleanEncodedValue(KEY, true); + } + +} diff --git a/core/src/main/java/com/graphhopper/routing/ev/CarTemporalAccess.java b/core/src/main/java/com/graphhopper/routing/ev/CarTemporalAccess.java new file mode 100644 index 00000000000..05e813a79a9 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/ev/CarTemporalAccess.java @@ -0,0 +1,47 @@ +/* + * 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.routing.ev; + +import com.graphhopper.util.Helper; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; + +/** + * Stores temporary so-called conditional restrictions from access:conditional and other conditional + * tags affecting cars. See OSMRoadAccessConditionalParser. + */ +public enum CarTemporalAccess { + + MISSING, YES, NO; + + public static final Collection CONDITIONALS = new HashSet<>(Arrays.asList("access:conditional", + "vehicle:conditional", "motor_vehicle:conditional", "motorcar:conditional")); + public static final String KEY = "car_temporal_access"; + + public static EnumEncodedValue create() { + return new EnumEncodedValue<>(KEY, CarTemporalAccess.class); + } + + @Override + public String toString() { + return Helper.toLowerCase(super.toString()); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/ev/Crossing.java b/core/src/main/java/com/graphhopper/routing/ev/Crossing.java index b3909b49c86..8602ca7d641 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/Crossing.java +++ b/core/src/main/java/com/graphhopper/routing/ev/Crossing.java @@ -13,6 +13,10 @@ public enum Crossing { NO; // crossing is impossible or illegal public static final String KEY = "crossing"; + public static EnumEncodedValue create() { + return new EnumEncodedValue<>(Crossing.KEY, Crossing.class); + } + @Override public String toString() { return Helper.toLowerCase(super.toString()); diff --git a/core/src/main/java/com/graphhopper/routing/ev/DefaultEncodedValueFactory.java b/core/src/main/java/com/graphhopper/routing/ev/DefaultEncodedValueFactory.java deleted file mode 100644 index 67b03984802..00000000000 --- a/core/src/main/java/com/graphhopper/routing/ev/DefaultEncodedValueFactory.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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.routing.ev; - -import com.graphhopper.util.PMap; - -public class DefaultEncodedValueFactory implements EncodedValueFactory { - - @Override - public EncodedValue create(String name, PMap properties) { - if (Roundabout.KEY.equals(name)) { - return Roundabout.create(); - } else if (GetOffBike.KEY.equals(name)) { - return GetOffBike.create(); - } else if (RoadClass.KEY.equals(name)) { - return RoadClass.create(); - } else if (RoadClassLink.KEY.equals(name)) { - return RoadClassLink.create(); - } else if (RoadEnvironment.KEY.equals(name)) { - return RoadEnvironment.create(); - } else if (RoadAccess.KEY.equals(name)) { - return RoadAccess.create(); - } else if (MaxSpeed.KEY.equals(name)) { - return MaxSpeed.create(); - } else if (MaxSpeedEstimated.KEY.equals(name)) { - return MaxSpeedEstimated.create(); - } else if (MaxWeight.KEY.equals(name)) { - return MaxWeight.create(); - } else if (MaxWeightExcept.KEY.equals(name)) { - return MaxWeightExcept.create(); - } else if (MaxHeight.KEY.equals(name)) { - return MaxHeight.create(); - } else if (MaxWidth.KEY.equals(name)) { - return MaxWidth.create(); - } else if (MaxAxleLoad.KEY.equals(name)) { - return MaxAxleLoad.create(); - } else if (MaxLength.KEY.equals(name)) { - return MaxLength.create(); - } else if (Hgv.KEY.equals(name)) { - return Hgv.create(); - } else if (Surface.KEY.equals(name)) { - return Surface.create(); - } else if (Smoothness.KEY.equals(name)) { - return Smoothness.create(); - } else if (Toll.KEY.equals(name)) { - return Toll.create(); - } else if (TrackType.KEY.equals(name)) { - return TrackType.create(); - } else if (BikeNetwork.KEY.equals(name) || FootNetwork.KEY.equals(name)) { - return RouteNetwork.create(name); - } else if (Hazmat.KEY.equals(name)) { - return Hazmat.create(); - } else if (HazmatTunnel.KEY.equals(name)) { - return HazmatTunnel.create(); - } else if (HazmatWater.KEY.equals(name)) { - return HazmatWater.create(); - } else if (Lanes.KEY.equals(name)) { - return Lanes.create(); - } else if (Footway.KEY.equals(name)) { - return Footway.create(); - } else if (OSMWayID.KEY.equals(name)) { - return OSMWayID.create(); - } else if (MtbRating.KEY.equals(name)) { - return MtbRating.create(); - } else if (HikeRating.KEY.equals(name)) { - return HikeRating.create(); - } else if (HorseRating.KEY.equals(name)) { - return HorseRating.create(); - } else if (Country.KEY.equals(name)) { - return Country.create(); - } else if (State.KEY.equals(name)) { - return State.create(); - } else if (name.endsWith(Subnetwork.key(""))) { - return Subnetwork.create(name); - } else if (MaxSlope.KEY.equals(name)) { - return MaxSlope.create(); - } else if (AverageSlope.KEY.equals(name)) { - return AverageSlope.create(); - } else if (Curvature.KEY.equals(name)) { - return Curvature.create(); - } else if (Crossing.KEY.equals(name)) { - return new EnumEncodedValue<>(Crossing.KEY, Crossing.class); - } else if (FerrySpeed.KEY.equals(name)) { - return FerrySpeed.create(); - } else { - throw new IllegalArgumentException("DefaultEncodedValueFactory cannot find EncodedValue " + name); - } - } -} diff --git a/core/src/main/java/com/graphhopper/routing/ev/DefaultImportRegistry.java b/core/src/main/java/com/graphhopper/routing/ev/DefaultImportRegistry.java new file mode 100644 index 00000000000..06af888796d --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/ev/DefaultImportRegistry.java @@ -0,0 +1,330 @@ +/* + * 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.routing.ev; + +import com.graphhopper.routing.util.*; +import com.graphhopper.routing.util.parsers.*; + +import java.util.Arrays; +import java.util.stream.Collectors; + +public class DefaultImportRegistry implements ImportRegistry { + @Override + public ImportUnit createImportUnit(String name) { + if (Roundabout.KEY.equals(name)) + return ImportUnit.create(name, props -> Roundabout.create(), + (lookup, props) -> new OSMRoundaboutParser( + lookup.getBooleanEncodedValue(Roundabout.KEY)) + ); + else if (GetOffBike.KEY.equals(name)) + return ImportUnit.create(name, props -> GetOffBike.create(), + (lookup, pros) -> new OSMGetOffBikeParser( + lookup.getBooleanEncodedValue(GetOffBike.KEY), + lookup.getBooleanEncodedValue("bike_access") + ), "bike_access"); + else if (RoadClass.KEY.equals(name)) + return ImportUnit.create(name, props -> RoadClass.create(), + (lookup, props) -> new OSMRoadClassParser( + lookup.getEnumEncodedValue(RoadClass.KEY, RoadClass.class)) + ); + else if (RoadClassLink.KEY.equals(name)) + return ImportUnit.create(name, props -> RoadClassLink.create(), + (lookup, props) -> new OSMRoadClassLinkParser( + lookup.getBooleanEncodedValue(RoadClassLink.KEY)) + ); + else if (RoadEnvironment.KEY.equals(name)) + return ImportUnit.create(name, props -> RoadEnvironment.create(), + (lookup, props) -> new OSMRoadEnvironmentParser( + lookup.getEnumEncodedValue(RoadEnvironment.KEY, RoadEnvironment.class)) + ); + else if (RoadAccess.KEY.equals(name)) + return ImportUnit.create(name, props -> RoadAccess.create(), + (lookup, props) -> new OSMRoadAccessParser( + lookup.getEnumEncodedValue(RoadAccess.KEY, RoadAccess.class), + OSMRoadAccessParser.toOSMRestrictions(TransportationMode.CAR)) + ); + else if (MaxSpeed.KEY.equals(name)) + return ImportUnit.create(name, props -> MaxSpeed.create(), + (lookup, props) -> new OSMMaxSpeedParser( + lookup.getDecimalEncodedValue(MaxSpeed.KEY)) + ); + else if (MaxSpeedEstimated.KEY.equals(name)) + return ImportUnit.create(name, props -> MaxSpeedEstimated.create(), + null, Country.KEY, UrbanDensity.KEY); + else if (UrbanDensity.KEY.equals(name)) + return ImportUnit.create(name, props -> UrbanDensity.create(), + null); + else if (MaxWeight.KEY.equals(name)) + return ImportUnit.create(name, props -> MaxWeight.create(), + (lookup, props) -> new OSMMaxWeightParser( + lookup.getDecimalEncodedValue(MaxWeight.KEY)) + ); + else if (MaxWeightExcept.KEY.equals(name)) + return ImportUnit.create(name, props -> MaxWeightExcept.create(), + (lookup, props) -> new MaxWeightExceptParser( + lookup.getEnumEncodedValue(MaxWeightExcept.KEY, MaxWeightExcept.class)) + ); + else if (MaxHeight.KEY.equals(name)) + return ImportUnit.create(name, props -> MaxHeight.create(), + (lookup, props) -> new OSMMaxHeightParser( + lookup.getDecimalEncodedValue(MaxHeight.KEY)) + ); + else if (MaxWidth.KEY.equals(name)) + return ImportUnit.create(name, props -> MaxWidth.create(), + (lookup, props) -> new OSMMaxWidthParser( + lookup.getDecimalEncodedValue(MaxWidth.KEY)) + ); + else if (MaxAxleLoad.KEY.equals(name)) + return ImportUnit.create(name, props -> MaxAxleLoad.create(), + (lookup, props) -> new OSMMaxAxleLoadParser( + lookup.getDecimalEncodedValue(MaxAxleLoad.KEY)) + ); + else if (MaxLength.KEY.equals(name)) + return ImportUnit.create(name, props -> MaxLength.create(), + (lookup, props) -> new OSMMaxLengthParser( + lookup.getDecimalEncodedValue(MaxLength.KEY)) + ); + else if (Surface.KEY.equals(name)) + return ImportUnit.create(name, props -> Surface.create(), + (lookup, props) -> new OSMSurfaceParser( + lookup.getEnumEncodedValue(Surface.KEY, Surface.class)) + ); + else if (Smoothness.KEY.equals(name)) + return ImportUnit.create(name, props -> Smoothness.create(), + (lookup, props) -> new OSMSmoothnessParser( + lookup.getEnumEncodedValue(Smoothness.KEY, Smoothness.class)) + ); + else if (Hgv.KEY.equals(name)) + return ImportUnit.create(name, props -> Hgv.create(), + (lookup, props) -> new OSMHgvParser( + lookup.getEnumEncodedValue(Hgv.KEY, Hgv.class) + )); + else if (Toll.KEY.equals(name)) + return ImportUnit.create(name, props -> Toll.create(), + (lookup, props) -> new OSMTollParser( + lookup.getEnumEncodedValue(Toll.KEY, Toll.class)) + ); + else if (TrackType.KEY.equals(name)) + return ImportUnit.create(name, props -> TrackType.create(), + (lookup, props) -> new OSMTrackTypeParser( + lookup.getEnumEncodedValue(TrackType.KEY, TrackType.class)) + ); + else if (Hazmat.KEY.equals(name)) + return ImportUnit.create(name, props -> Hazmat.create(), + (lookup, props) -> new OSMHazmatParser( + lookup.getEnumEncodedValue(Hazmat.KEY, Hazmat.class)) + ); + else if (HazmatTunnel.KEY.equals(name)) + return ImportUnit.create(name, props -> HazmatTunnel.create(), + (lookup, props) -> new OSMHazmatTunnelParser( + lookup.getEnumEncodedValue(HazmatTunnel.KEY, HazmatTunnel.class)) + ); + else if (HazmatWater.KEY.equals(name)) + return ImportUnit.create(name, props -> HazmatWater.create(), + (lookup, props) -> new OSMHazmatWaterParser( + lookup.getEnumEncodedValue(HazmatWater.KEY, HazmatWater.class)) + ); + else if (Lanes.KEY.equals(name)) + return ImportUnit.create(name, props -> Lanes.create(), + (lookup, props) -> new OSMLanesParser( + lookup.getIntEncodedValue(Lanes.KEY)) + ); + else if (Footway.KEY.equals(name)) + return ImportUnit.create(name, props -> Footway.create(), + (lookup, props) -> new OSMFootwayParser( + lookup.getEnumEncodedValue(Footway.KEY, Footway.class)) + ); + else if (OSMWayID.KEY.equals(name)) + return ImportUnit.create(name, props -> OSMWayID.create(), + (lookup, props) -> new OSMWayIDParser( + lookup.getIntEncodedValue(OSMWayID.KEY)) + ); + else if (MtbRating.KEY.equals(name)) + return ImportUnit.create(name, props -> MtbRating.create(), + (lookup, props) -> new OSMMtbRatingParser( + lookup.getIntEncodedValue(MtbRating.KEY)) + ); + else if (HikeRating.KEY.equals(name)) + return ImportUnit.create(name, props -> HikeRating.create(), + (lookup, props) -> new OSMHikeRatingParser( + lookup.getIntEncodedValue(HikeRating.KEY)) + ); + else if (HorseRating.KEY.equals(name)) + return ImportUnit.create(name, props -> HorseRating.create(), + (lookup, props) -> new OSMHorseRatingParser( + lookup.getIntEncodedValue(HorseRating.KEY)) + ); + else if (Country.KEY.equals(name)) + return ImportUnit.create(name, props -> Country.create(), + (lookup, props) -> new CountryParser( + lookup.getEnumEncodedValue(Country.KEY, Country.class)) + ); + else if (State.KEY.equals(name)) + return ImportUnit.create(name, props -> State.create(), + (lookup, props) -> new StateParser( + lookup.getEnumEncodedValue(State.KEY, State.class)) + ); + else if (Crossing.KEY.equals(name)) + return ImportUnit.create(name, props -> Crossing.create(), + (lookup, props) -> new OSMCrossingParser( + lookup.getEnumEncodedValue(Crossing.KEY, Crossing.class)) + ); + else if (FerrySpeed.KEY.equals(name)) + return ImportUnit.create(name, props -> FerrySpeed.create(), + (lookup, props) -> new FerrySpeedCalculator( + lookup.getDecimalEncodedValue(FerrySpeed.KEY))); + else if (Curvature.KEY.equals(name)) + return ImportUnit.create(name, props -> Curvature.create(), + (lookup, props) -> new CurvatureCalculator( + lookup.getDecimalEncodedValue(Curvature.KEY)) + ); + else if (AverageSlope.KEY.equals(name)) + return ImportUnit.create(name, props -> AverageSlope.create(), null, "slope_calculator"); + else if (MaxSlope.KEY.equals(name)) + return ImportUnit.create(name, props -> MaxSlope.create(), null, "slope_calculator"); + else if ("slope_calculator".equals(name)) + return ImportUnit.create(name, null, + (lookup, props) -> new SlopeCalculator( + lookup.hasEncodedValue(MaxSlope.KEY) ? lookup.getDecimalEncodedValue(MaxSlope.KEY) : null, + lookup.hasEncodedValue(AverageSlope.KEY) ? lookup.getDecimalEncodedValue(AverageSlope.KEY) : null + )); + else if (BikeNetwork.KEY.equals(name) || MtbNetwork.KEY.equals(name) || FootNetwork.KEY.equals(name)) + return ImportUnit.create(name, props -> RouteNetwork.create(name), null); + + else if (BusAccess.KEY.equals(name)) + return ImportUnit.create(name, props -> BusAccess.create(), + (lookup, props) -> new ModeAccessParser(TransportationMode.BUS, lookup.getBooleanEncodedValue(BusAccess.KEY), + lookup.getBooleanEncodedValue(Roundabout.KEY), Arrays.stream(props.getString("restrictions", "").split(";")).filter(s -> !s.isEmpty()).collect(Collectors.toList())), + "roundabout" + ); + + else if (HovAccess.KEY.equals(name)) + return ImportUnit.create(name, props -> HovAccess.create(), + (lookup, props) -> new ModeAccessParser(TransportationMode.HOV, lookup.getBooleanEncodedValue(HovAccess.KEY), + lookup.getBooleanEncodedValue(Roundabout.KEY), Arrays.stream(props.getString("restrictions", "").split(";")).filter(s -> !s.isEmpty()).collect(Collectors.toList())), + "roundabout" + ); + else if (FootTemporalAccess.KEY.equals(name)) + return ImportUnit.create(name, props -> FootTemporalAccess.create(), + (lookup, props) -> { + EnumEncodedValue enc = lookup.getEnumEncodedValue(FootTemporalAccess.KEY, FootTemporalAccess.class); + OSMTemporalAccessParser.Setter fct = (edgeId, edgeIntAccess, b) -> enc.setEnum(false, edgeId, edgeIntAccess, b ? FootTemporalAccess.YES : FootTemporalAccess.NO); + return new OSMTemporalAccessParser(FootTemporalAccess.CONDITIONALS, fct, props.getString("date_range_parser_day", "")); + } + ); + + else if (BikeTemporalAccess.KEY.equals(name)) + return ImportUnit.create(name, props -> BikeTemporalAccess.create(), + (lookup, props) -> { + EnumEncodedValue enc = lookup.getEnumEncodedValue(BikeTemporalAccess.KEY, BikeTemporalAccess.class); + OSMTemporalAccessParser.Setter fct = (edgeId, edgeIntAccess, b) -> enc.setEnum(false, edgeId, edgeIntAccess, b ? BikeTemporalAccess.YES : BikeTemporalAccess.NO); + return new OSMTemporalAccessParser(BikeTemporalAccess.CONDITIONALS, fct, props.getString("date_range_parser_day", "")); + } + ); + + else if (CarTemporalAccess.KEY.equals(name)) + return ImportUnit.create(name, props -> CarTemporalAccess.create(), + (lookup, props) -> { + EnumEncodedValue enc = lookup.getEnumEncodedValue(CarTemporalAccess.KEY, CarTemporalAccess.class); + OSMTemporalAccessParser.Setter fct = (edgeId, edgeIntAccess, b) -> enc.setEnum(false, edgeId, edgeIntAccess, b ? CarTemporalAccess.YES : CarTemporalAccess.NO); + return new OSMTemporalAccessParser(CarTemporalAccess.CONDITIONALS, fct, props.getString("date_range_parser_day", "")); + } + ); + + else if (VehicleAccess.key("car").equals(name)) + return ImportUnit.create(name, props -> VehicleAccess.create("car"), + CarAccessParser::new, + "roundabout" + ); + else if (VehicleAccess.key("roads").equals(name)) + throw new IllegalArgumentException("roads_access parser no longer necessary, see docs/migration/config-migration-08-09.md"); + else if (VehicleAccess.key("bike").equals(name)) + return ImportUnit.create(name, props -> VehicleAccess.create("bike"), + BikeAccessParser::new, + "roundabout" + ); + else if (VehicleAccess.key("racingbike").equals(name)) + return ImportUnit.create(name, props -> VehicleAccess.create("racingbike"), + RacingBikeAccessParser::new, + "roundabout" + ); + else if (VehicleAccess.key("mtb").equals(name)) + return ImportUnit.create(name, props -> VehicleAccess.create("mtb"), + MountainBikeAccessParser::new, + "roundabout" + ); + else if (VehicleAccess.key("foot").equals(name)) + return ImportUnit.create(name, props -> VehicleAccess.create("foot"), + FootAccessParser::new); + + else if (VehicleSpeed.key("car").equals(name)) + return ImportUnit.create(name, props -> new DecimalEncodedValueImpl( + name, props.getInt("speed_bits", 7), props.getDouble("speed_factor", 2), true), + (lookup, props) -> new CarAverageSpeedParser(lookup), + "ferry_speed" + ); + else if (VehicleSpeed.key("roads").equals(name)) + throw new IllegalArgumentException("roads_average_speed parser no longer necessary, see docs/migration/config-migration-08-09.md"); + else if (VehicleSpeed.key("bike").equals(name)) + return ImportUnit.create(name, props -> new DecimalEncodedValueImpl( + name, props.getInt("speed_bits", 4), props.getDouble("speed_factor", 2), false), + (lookup, props) -> new BikeAverageSpeedParser(lookup), + "ferry_speed", "smoothness" + ); + else if (VehicleSpeed.key("racingbike").equals(name)) + return ImportUnit.create(name, props -> new DecimalEncodedValueImpl( + name, props.getInt("speed_bits", 4), props.getDouble("speed_factor", 2), false), + (lookup, props) -> new RacingBikeAverageSpeedParser(lookup), + "ferry_speed", "smoothness" + ); + else if (VehicleSpeed.key("mtb").equals(name)) + return ImportUnit.create(name, props -> new DecimalEncodedValueImpl( + name, props.getInt("speed_bits", 4), props.getDouble("speed_factor", 2), false), + (lookup, props) -> new MountainBikeAverageSpeedParser(lookup), + "ferry_speed", "smoothness" + ); + else if (VehicleSpeed.key("foot").equals(name)) + return ImportUnit.create(name, props -> new DecimalEncodedValueImpl( + name, props.getInt("speed_bits", 4), props.getDouble("speed_factor", 1), false), + (lookup, props) -> new FootAverageSpeedParser(lookup), + "ferry_speed" + ); + else if (VehiclePriority.key("foot").equals(name)) + return ImportUnit.create(name, props -> VehiclePriority.create("foot", 4, PriorityCode.getFactor(1), false), + (lookup, props) -> new FootPriorityParser(lookup), + RouteNetwork.key("foot") + ); + else if (VehiclePriority.key("bike").equals(name)) + return ImportUnit.create(name, props -> VehiclePriority.create("bike", 4, PriorityCode.getFactor(1), false), + (lookup, props) -> new BikePriorityParser(lookup), + VehicleSpeed.key("bike"), BikeNetwork.KEY + ); + else if (VehiclePriority.key("racingbike").equals(name)) + return ImportUnit.create(name, props -> VehiclePriority.create("racingbike", 4, PriorityCode.getFactor(1), false), + (lookup, props) -> new RacingBikePriorityParser(lookup), + VehicleSpeed.key("racingbike"), BikeNetwork.KEY + ); + else if (VehiclePriority.key("mtb").equals(name)) + return ImportUnit.create(name, props -> VehiclePriority.create("mtb", 4, PriorityCode.getFactor(1), false), + (lookup, props) -> new MountainBikePriorityParser(lookup), + VehicleSpeed.key("mtb"), BikeNetwork.KEY + ); + return null; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/ev/EnumEncodedValue.java b/core/src/main/java/com/graphhopper/routing/ev/EnumEncodedValue.java index 555e1a36982..1bec91183db 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/EnumEncodedValue.java +++ b/core/src/main/java/com/graphhopper/routing/ev/EnumEncodedValue.java @@ -61,8 +61,8 @@ public EnumEncodedValue(String name, Class enumType, boolean storeTwoDirectio arr = enumType.getEnumConstants(); } - public String getEnumSimpleName() { - return enumType.getSimpleName(); + public Class getEnumType() { + return enumType; } public E[] getValues() { diff --git a/core/src/test/java/com/graphhopper/reader/osm/conditional/CalendarBasedTest.java b/core/src/main/java/com/graphhopper/routing/ev/FootTemporalAccess.java similarity index 50% rename from core/src/test/java/com/graphhopper/reader/osm/conditional/CalendarBasedTest.java rename to core/src/main/java/com/graphhopper/routing/ev/FootTemporalAccess.java index b6da0f73ee0..eac70d4da2e 100644 --- a/core/src/test/java/com/graphhopper/reader/osm/conditional/CalendarBasedTest.java +++ b/core/src/main/java/com/graphhopper/routing/ev/FootTemporalAccess.java @@ -15,22 +15,33 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.graphhopper.reader.osm.conditional; -import java.util.Calendar; +package com.graphhopper.routing.ev; + +import com.graphhopper.util.Helper; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; /** - * Base Test for calendar based tasks. - *

- * - * @author Robin Boldt + * Stores temporary so-called conditional restrictions from access:conditional and other conditional + * tags affecting foot. See OSMRoadAccessConditionalParser. */ -public abstract class CalendarBasedTest { - protected Calendar getCalendar(int year, int month, int day) { - Calendar calendar = DateRangeParser.createCalendar(); - calendar.set(Calendar.YEAR, year); - calendar.set(Calendar.MONTH, month); - calendar.set(Calendar.DAY_OF_MONTH, day); - return calendar; +public enum FootTemporalAccess { + + MISSING, YES, NO; + + public static final Collection CONDITIONALS = new HashSet<>(Arrays.asList("access:conditional", + "foot:conditional")); + public static final String KEY = "foot_temporal_access"; + + public static EnumEncodedValue create() { + return new EnumEncodedValue<>(KEY, FootTemporalAccess.class); + } + + @Override + public String toString() { + return Helper.toLowerCase(super.toString()); } } diff --git a/core/src/main/java/com/graphhopper/routing/ev/HovAccess.java b/core/src/main/java/com/graphhopper/routing/ev/HovAccess.java new file mode 100644 index 00000000000..f67b3b201a5 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/ev/HovAccess.java @@ -0,0 +1,9 @@ +package com.graphhopper.routing.ev; + +public class HovAccess { + public final static String KEY = "hov_access"; + + public static BooleanEncodedValue create() { + return new SimpleBooleanEncodedValue(KEY, true); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/ev/EncodedValueFactory.java b/core/src/main/java/com/graphhopper/routing/ev/ImportRegistry.java similarity index 86% rename from core/src/main/java/com/graphhopper/routing/ev/EncodedValueFactory.java rename to core/src/main/java/com/graphhopper/routing/ev/ImportRegistry.java index c8fcbcc993e..283321e3574 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/EncodedValueFactory.java +++ b/core/src/main/java/com/graphhopper/routing/ev/ImportRegistry.java @@ -15,10 +15,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.graphhopper.routing.ev; -import com.graphhopper.util.PMap; +package com.graphhopper.routing.ev; -public interface EncodedValueFactory { - EncodedValue create(String name, PMap properties); +public interface ImportRegistry { + ImportUnit createImportUnit(String name); } diff --git a/core/src/main/java/com/graphhopper/routing/ev/ImportUnit.java b/core/src/main/java/com/graphhopper/routing/ev/ImportUnit.java new file mode 100644 index 00000000000..04fd08a389c --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/ev/ImportUnit.java @@ -0,0 +1,61 @@ +/* + * 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.routing.ev; + +import com.graphhopper.routing.util.parsers.TagParser; +import com.graphhopper.util.PMap; + +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.Function; + +public class ImportUnit { + private final String name; + private final Function createEncodedValue; + private final BiFunction createTagParser; + private final List requiredImportUnits; + + public static ImportUnit create(String name, Function createEncodedValue, BiFunction createTagParser, String... requiredImportUnits) { + return new ImportUnit(name, createEncodedValue, createTagParser, List.of(requiredImportUnits)); + } + + private ImportUnit(String name, Function createEncodedValue, BiFunction createTagParser, List requiredImportUnits) { + this.name = name; + this.createEncodedValue = createEncodedValue; + this.createTagParser = createTagParser; + this.requiredImportUnits = requiredImportUnits; + } + + public Function getCreateEncodedValue() { + return createEncodedValue; + } + + public BiFunction getCreateTagParser() { + return createTagParser; + } + + public List getRequiredImportUnits() { + return requiredImportUnits; + } + + @Override + public String toString() { + return "ImportUnit: " + name + " (requires: " + requiredImportUnits + ")"; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/ev/ImportUnitSorter.java b/core/src/main/java/com/graphhopper/routing/ev/ImportUnitSorter.java new file mode 100644 index 00000000000..301a4c49207 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/ev/ImportUnitSorter.java @@ -0,0 +1,57 @@ +/* + * 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.routing.ev; + +import java.util.*; + +// topological sort with a depth first search +public class ImportUnitSorter { + Set permanentMarked = new HashSet<>(); + Set temporaryMarked = new HashSet<>(); + List result = new ArrayList<>(); + final Map map; + + public ImportUnitSorter(Map map) { + this.map = map; + } + + public List sort() { + for (String strN : map.keySet()) { + visit(strN); + } + return result; + } + + private void visit(String strN) { + if (permanentMarked.contains(strN)) return; + ImportUnit importUnit = map.get(strN); + if (importUnit == null) + throw new IllegalArgumentException("cannot find import unit " + strN); + if (temporaryMarked.contains(strN)) + throw new IllegalArgumentException("import units with cyclic dependencies are not allowed: " + importUnit + " " + importUnit.getRequiredImportUnits()); + + temporaryMarked.add(strN); + for (String strM : importUnit.getRequiredImportUnits()) { + visit(strM); + } + temporaryMarked.remove(strN); + permanentMarked.add(strN); + result.add(strN); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/ev/MaxSlope.java b/core/src/main/java/com/graphhopper/routing/ev/MaxSlope.java index 26282b45704..a3add461bdc 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/MaxSlope.java +++ b/core/src/main/java/com/graphhopper/routing/ev/MaxSlope.java @@ -7,6 +7,6 @@ public class MaxSlope { public static final String KEY = "max_slope"; public static DecimalEncodedValue create() { - return new DecimalEncodedValueImpl(KEY, 5, 1, false); + return new DecimalEncodedValueImpl(KEY, 5, 0, 1, true, false, false); } } diff --git a/core/src/main/java/com/graphhopper/routing/ev/MtbNetwork.java b/core/src/main/java/com/graphhopper/routing/ev/MtbNetwork.java new file mode 100644 index 00000000000..5ffde5ee345 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/ev/MtbNetwork.java @@ -0,0 +1,5 @@ +package com.graphhopper.routing.ev; + +public class MtbNetwork { + public static final String KEY = RouteNetwork.key("mtb"); +} diff --git a/core/src/main/java/com/graphhopper/routing/ev/State.java b/core/src/main/java/com/graphhopper/routing/ev/State.java index 3a78014d094..ea91fa17d15 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/State.java +++ b/core/src/main/java/com/graphhopper/routing/ev/State.java @@ -6,17 +6,17 @@ * The country subdivision is stored in this EncodedValue. E.g. US-CA is the enum US_CA. */ public enum State { - MISSING("-", Country.MISSING), + MISSING("-"), // Australia - AU_ACT("AU-ACT", AUS), - AU_NSW("AU-NSW", AUS), - AU_NT("AU-NT", AUS), - AU_QLD("AU-QLD", AUS), - AU_SA("AU-SA", AUS), - AU_TAS("AU-TAS", AUS), - AU_VIC("AU-VIC", AUS), - AU_WA("AU-WA", AUS), + AU_ACT("AU-ACT"), + AU_NSW("AU-NSW"), + AU_NT("AU-NT"), + AU_QLD("AU-QLD"), + AU_SA("AU-SA"), + AU_TAS("AU-TAS"), + AU_VIC("AU-VIC"), + AU_WA("AU-WA"), // the sub regions of Belgium have no data in countries.geojson // BE_BRU("BE-BRU", BEL), @@ -24,25 +24,25 @@ public enum State { // BE_WAL("BE-WAL", BEL), // Canada - CA_AB("CA-AB", CAN), - CA_BC("CA-BC", CAN), - CA_MB("CA-MB", CAN), - CA_NB("CA-NB", CAN), - CA_NL("CA-NL", CAN), - CA_NS("CA-NS", CAN), - CA_NT("CA-NT", CAN), - CA_NU("CA-NU", CAN), - CA_ON("CA-ON", CAN), - CA_PE("CA-PE", CAN), - CA_QC("CA-QC", CAN), - CA_SK("CA-SK", CAN), - CA_YT("CA-YT", CAN), + CA_AB("CA-AB"), + CA_BC("CA-BC"), + CA_MB("CA-MB"), + CA_NB("CA-NB"), + CA_NL("CA-NL"), + CA_NS("CA-NS"), + CA_NT("CA-NT"), + CA_NU("CA-NU"), + CA_ON("CA-ON"), + CA_PE("CA-PE"), + CA_QC("CA-QC"), + CA_SK("CA-SK"), + CA_YT("CA-YT"), // Federated States of Micronesia - FM_KSA("FM-KSA", FSM), - FM_PNI("FM-PNI", FSM), - FM_TRK("FM-TRK", FSM), - FM_YAP("FM-YAP", FSM), + FM_KSA("FM-KSA"), + FM_PNI("FM-PNI"), + FM_TRK("FM-TRK"), + FM_YAP("FM-YAP"), // United Kingdom // TODO currently it isn't supported when the states list does not cover the entire country @@ -56,69 +56,67 @@ public enum State { // NL_BQ3("NL-BQ3", NLD), // United States - US_AL("US-AL", USA), - US_AK("US-AK", USA), - US_AZ("US-AZ", USA), - US_AR("US-AR", USA), - US_CA("US-CA", USA), - US_CO("US-CO", USA), - US_CT("US-CT", USA), - US_DE("US-DE", USA), - US_DC("US-DC", USA), // is a federal district not a state - US_FL("US-FL", USA), - US_GA("US-GA", USA), - US_HI("US-HI", USA), - US_ID("US-ID", USA), - US_IL("US-IL", USA), - US_IN("US-IN", USA), - US_IA("US-IA", USA), - US_KS("US-KS", USA), - US_KY("US-KY", USA), - US_LA("US-LA", USA), - US_ME("US-ME", USA), - US_MD("US-MD", USA), - US_MA("US-MA", USA), - US_MI("US-MI", USA), - US_MN("US-MN", USA), - US_MS("US-MS", USA), - US_MO("US-MO", USA), - US_MT("US-MT", USA), - US_NE("US-NE", USA), - US_NV("US-NV", USA), - US_NH("US-NH", USA), - US_NJ("US-NJ", USA), - US_NM("US-NM", USA), - US_NY("US-NY", USA), - US_NC("US-NC", USA), - US_ND("US-ND", USA), - US_OH("US-OH", USA), - US_OK("US-OK", USA), - US_OR("US-OR", USA), - US_PA("US-PA", USA), - US_RI("US-RI", USA), - US_SC("US-SC", USA), - US_SD("US-SD", USA), - US_TN("US-TN", USA), - US_TX("US-TX", USA), - US_UT("US-UT", USA), - US_VT("US-VT", USA), - US_VA("US-VA", USA), - US_WA("US-WA", USA), - US_WV("US-WV", USA), - US_WI("US-WI", USA), - US_WY("US-WY", USA); + US_AL("US-AL"), + US_AK("US-AK"), + US_AZ("US-AZ"), + US_AR("US-AR"), + US_CA("US-CA"), + US_CO("US-CO"), + US_CT("US-CT"), + US_DE("US-DE"), + US_DC("US-DC"), // is a federal district not a state + US_FL("US-FL"), + US_GA("US-GA"), + US_HI("US-HI"), + US_ID("US-ID"), + US_IL("US-IL"), + US_IN("US-IN"), + US_IA("US-IA"), + US_KS("US-KS"), + US_KY("US-KY"), + US_LA("US-LA"), + US_ME("US-ME"), + US_MD("US-MD"), + US_MA("US-MA"), + US_MI("US-MI"), + US_MN("US-MN"), + US_MS("US-MS"), + US_MO("US-MO"), + US_MT("US-MT"), + US_NE("US-NE"), + US_NV("US-NV"), + US_NH("US-NH"), + US_NJ("US-NJ"), + US_NM("US-NM"), + US_NY("US-NY"), + US_NC("US-NC"), + US_ND("US-ND"), + US_OH("US-OH"), + US_OK("US-OK"), + US_OR("US-OR"), + US_PA("US-PA"), + US_RI("US-RI"), + US_SC("US-SC"), + US_SD("US-SD"), + US_TN("US-TN"), + US_TX("US-TX"), + US_UT("US-UT"), + US_VT("US-VT"), + US_VA("US-VA"), + US_WA("US-WA"), + US_WV("US-WV"), + US_WI("US-WI"), + US_WY("US-WY"); public static final String KEY = "state", ISO_3166_2 = "ISO3166-2"; - private final Country country; private final String stateCode; /** * @param isoCodeOfSubdivision should be ISO 3166-2 but with hyphen like US-CA */ - State(String isoCodeOfSubdivision, Country country) { + State(String isoCodeOfSubdivision) { this.stateCode = isoCodeOfSubdivision; - this.country = country; } /** @@ -139,10 +137,6 @@ public String getStateCode() { return stateCode; } - public Country getCountry() { - return country; - } - @Override public String toString() { return stateCode; diff --git a/core/src/main/java/com/graphhopper/routing/lm/LMPreparationHandler.java b/core/src/main/java/com/graphhopper/routing/lm/LMPreparationHandler.java index d43595d7ce3..20ae626741e 100644 --- a/core/src/main/java/com/graphhopper/routing/lm/LMPreparationHandler.java +++ b/core/src/main/java/com/graphhopper/routing/lm/LMPreparationHandler.java @@ -180,7 +180,7 @@ public List prepare(List lmConfigs, BaseGraph baseGr final int count = i + 1; final String name = prepare.getLMConfig().getName(); prepareRunnables.add(() -> { - LOGGER.info(count + "/" + lmConfigs.size() + " calling LM prepare.doWork for " + prepare.getLMConfig().getWeighting() + " ... (" + getMemInfo() + ")"); + LOGGER.info(count + "/" + lmConfigs.size() + " calling LM prepare.doWork for " + prepare.getLMConfig().getName() + " ... (" + getMemInfo() + ")"); Thread.currentThread().setName(name); prepare.doWork(); if (closeEarly) diff --git a/core/src/main/java/com/graphhopper/routing/querygraph/VirtualEdgeIteratorState.java b/core/src/main/java/com/graphhopper/routing/querygraph/VirtualEdgeIteratorState.java index edc8736ce3f..5ef6e8a09e9 100644 --- a/core/src/main/java/com/graphhopper/routing/querygraph/VirtualEdgeIteratorState.java +++ b/core/src/main/java/com/graphhopper/routing/querygraph/VirtualEdgeIteratorState.java @@ -99,7 +99,7 @@ public int getAdjNode() { @Override public PointList fetchWayGeometry(FetchMode mode) { - if (pointList.size() == 0) + if (pointList.isEmpty()) return PointList.EMPTY; // due to API we need to create a new instance per call! if (mode == FetchMode.TOWER_ONLY) { @@ -156,12 +156,12 @@ public boolean get(BooleanEncodedValue property) { if (property == EdgeIteratorState.UNFAVORED_EDGE) return unfavored; - return property.getBool(reverse, -1, edgeIntAccess); + return property.getBool(reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess); } @Override public EdgeIteratorState set(BooleanEncodedValue property, boolean value) { - property.setBool(reverse, -1, edgeIntAccess, value); + property.setBool(reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess, value); return this; } @@ -169,12 +169,12 @@ public EdgeIteratorState set(BooleanEncodedValue property, boolean value) { public boolean getReverse(BooleanEncodedValue property) { if (property == EdgeIteratorState.UNFAVORED_EDGE) return unfavored; - return property.getBool(!reverse, -1, edgeIntAccess); + return property.getBool(!reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess); } @Override public EdgeIteratorState setReverse(BooleanEncodedValue property, boolean value) { - property.setBool(!reverse, -1, edgeIntAccess, value); + property.setBool(!reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess, value); return this; } @@ -182,30 +182,30 @@ public EdgeIteratorState setReverse(BooleanEncodedValue property, boolean value) public EdgeIteratorState set(BooleanEncodedValue property, boolean fwd, boolean bwd) { if (!property.isStoreTwoDirections()) throw new IllegalArgumentException("EncodedValue " + property.getName() + " supports only one direction"); - property.setBool(reverse, -1, edgeIntAccess, fwd); - property.setBool(!reverse, -1, edgeIntAccess, bwd); + property.setBool(reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess, fwd); + property.setBool(!reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess, bwd); return this; } @Override public int get(IntEncodedValue property) { - return property.getInt(reverse, -1, edgeIntAccess); + return property.getInt(reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess); } @Override public EdgeIteratorState set(IntEncodedValue property, int value) { - property.setInt(reverse, -1, edgeIntAccess, value); + property.setInt(reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess, value); return this; } @Override public int getReverse(IntEncodedValue property) { - return property.getInt(!reverse, -1, edgeIntAccess); + return property.getInt(!reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess); } @Override public EdgeIteratorState setReverse(IntEncodedValue property, int value) { - property.setInt(!reverse, -1, edgeIntAccess, value); + property.setInt(!reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess, value); return this; } @@ -213,30 +213,30 @@ public EdgeIteratorState setReverse(IntEncodedValue property, int value) { public EdgeIteratorState set(IntEncodedValue property, int fwd, int bwd) { if (!property.isStoreTwoDirections()) throw new IllegalArgumentException("EncodedValue " + property.getName() + " supports only one direction"); - property.setInt(reverse, -1, edgeIntAccess, fwd); - property.setInt(!reverse, -1, edgeIntAccess, bwd); + property.setInt(reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess, fwd); + property.setInt(!reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess, bwd); return this; } @Override public double get(DecimalEncodedValue property) { - return property.getDecimal(reverse, -1, edgeIntAccess); + return property.getDecimal(reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess); } @Override public EdgeIteratorState set(DecimalEncodedValue property, double value) { - property.setDecimal(reverse, -1, edgeIntAccess, value); + property.setDecimal(reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess, value); return this; } @Override public double getReverse(DecimalEncodedValue property) { - return property.getDecimal(!reverse, -1, edgeIntAccess); + return property.getDecimal(!reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess); } @Override public EdgeIteratorState setReverse(DecimalEncodedValue property, double value) { - property.setDecimal(!reverse, -1, edgeIntAccess, value); + property.setDecimal(!reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess, value); return this; } @@ -244,30 +244,30 @@ public EdgeIteratorState setReverse(DecimalEncodedValue property, double value) public EdgeIteratorState set(DecimalEncodedValue property, double fwd, double bwd) { if (!property.isStoreTwoDirections()) throw new IllegalArgumentException("EncodedValue " + property.getName() + " supports only one direction"); - property.setDecimal(reverse, -1, edgeIntAccess, fwd); - property.setDecimal(!reverse, -1, edgeIntAccess, bwd); + property.setDecimal(reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess, fwd); + property.setDecimal(!reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess, bwd); return this; } @Override public > T get(EnumEncodedValue property) { - return property.getEnum(reverse, -1, edgeIntAccess); + return property.getEnum(reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess); } @Override public > EdgeIteratorState set(EnumEncodedValue property, T value) { - property.setEnum(reverse, -1, edgeIntAccess, value); + property.setEnum(reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess, value); return this; } @Override public > T getReverse(EnumEncodedValue property) { - return property.getEnum(!reverse, -1, edgeIntAccess); + return property.getEnum(!reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess); } @Override public > EdgeIteratorState setReverse(EnumEncodedValue property, T value) { - property.setEnum(!reverse, -1, edgeIntAccess, value); + property.setEnum(!reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess, value); return this; } @@ -275,30 +275,30 @@ public > EdgeIteratorState setReverse(EnumEncodedValue prop public > EdgeIteratorState set(EnumEncodedValue property, T fwd, T bwd) { if (!property.isStoreTwoDirections()) throw new IllegalArgumentException("EncodedValue " + property.getName() + " supports only one direction"); - property.setEnum(reverse, -1, edgeIntAccess, fwd); - property.setEnum(!reverse, -1, edgeIntAccess, bwd); + property.setEnum(reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess, fwd); + property.setEnum(!reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess, bwd); return this; } @Override public String get(StringEncodedValue property) { - return property.getString(reverse, -1, edgeIntAccess); + return property.getString(reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess); } @Override public EdgeIteratorState set(StringEncodedValue property, String value) { - property.setString(reverse, -1, edgeIntAccess, value); + property.setString(reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess, value); return this; } @Override public String getReverse(StringEncodedValue property) { - return property.getString(!reverse, -1, edgeIntAccess); + return property.getString(!reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess); } @Override public EdgeIteratorState setReverse(StringEncodedValue property, String value) { - property.setString(!reverse, -1, edgeIntAccess, value); + property.setString(!reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess, value); return this; } @@ -306,8 +306,8 @@ public EdgeIteratorState setReverse(StringEncodedValue property, String value) { public EdgeIteratorState set(StringEncodedValue property, String fwd, String bwd) { if (!property.isStoreTwoDirections()) throw new IllegalArgumentException("EncodedValue " + property.getName() + " supports only one direction"); - property.setString(reverse, -1, edgeIntAccess, fwd); - property.setString(!reverse, -1, edgeIntAccess, bwd); + property.setString(reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess, fwd); + property.setString(!reverse, GHUtility.getEdgeFromEdgeKey(originalEdgeKey), edgeIntAccess, bwd); return this; } @@ -332,7 +332,8 @@ public List getKeyValues() { @Override public Object getValue(String key) { for (KVStorage.KeyValue keyValue : keyValues) { - if (keyValue.key.equals(key)) return keyValue.value; + if (keyValue.key.equals(key) && (!reverse && keyValue.fwd || reverse && keyValue.bwd)) + return keyValue.value; } return null; } diff --git a/core/src/main/java/com/graphhopper/routing/util/DefaultVehicleEncodedValuesFactory.java b/core/src/main/java/com/graphhopper/routing/util/DefaultVehicleEncodedValuesFactory.java deleted file mode 100644 index 4ce3555607d..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/DefaultVehicleEncodedValuesFactory.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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.routing.util; - -import com.graphhopper.util.PMap; - -/** - * This class creates vehicle encoded values that are already included in the GraphHopper distribution. - * - * @author Peter Karich - */ -public class DefaultVehicleEncodedValuesFactory implements VehicleEncodedValuesFactory { - @Override - public VehicleEncodedValues createVehicleEncodedValues(String name, PMap configuration) { - if (name.equals(ROADS)) - return VehicleEncodedValues.roads(configuration); - - if (name.equals(CAR)) - return VehicleEncodedValues.car(configuration); - - if (name.equals("car4wd")) - throw new IllegalArgumentException("Instead of car4wd use custom_models/car4wd.json"); - - if (name.equals(BIKE)) - return VehicleEncodedValues.bike(configuration); - - if (name.equals("bike2")) - throw new IllegalArgumentException("Instead of bike2 use custom_models/bike.json, see #2668"); - - if (name.equals(RACINGBIKE)) - return VehicleEncodedValues.racingbike(configuration); - - if (name.equals(MOUNTAINBIKE)) - return VehicleEncodedValues.mountainbike(configuration); - - if (name.equals(FOOT)) - return VehicleEncodedValues.foot(configuration); - - if (name.equals("hike")) - throw new IllegalArgumentException("Instead of hike use custom_models/hike.json, see #2759"); - - if (name.equals("motorcycle")) - throw new IllegalArgumentException("Instead of motorcycle use custom_models/motorcycle.json, see #2781"); - - if (name.equals(WHEELCHAIR)) - return VehicleEncodedValues.wheelchair(configuration); - - throw new IllegalArgumentException("entry in vehicle list not supported: " + name); - } -} diff --git a/core/src/main/java/com/graphhopper/routing/util/DefaultVehicleTagParserFactory.java b/core/src/main/java/com/graphhopper/routing/util/DefaultVehicleTagParserFactory.java deleted file mode 100644 index f10c566a469..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/DefaultVehicleTagParserFactory.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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.routing.util; - -import com.graphhopper.routing.ev.EncodedValueLookup; -import com.graphhopper.util.PMap; - -import static com.graphhopper.routing.util.VehicleEncodedValuesFactory.*; - -public class DefaultVehicleTagParserFactory implements VehicleTagParserFactory { - public VehicleTagParsers createParsers(EncodedValueLookup lookup, String name, PMap configuration) { - if (name.equals(ROADS)) - return VehicleTagParsers.roads(lookup, configuration); - if (name.equals(CAR)) - return VehicleTagParsers.car(lookup, configuration); - if (name.equals(BIKE)) - return VehicleTagParsers.bike(lookup, configuration); - if (name.equals(RACINGBIKE)) - return VehicleTagParsers.racingbike(lookup, configuration); - if (name.equals(MOUNTAINBIKE)) - return VehicleTagParsers.mtb(lookup, configuration); - if (name.equals(FOOT)) - return VehicleTagParsers.foot(lookup, configuration); - if (name.equals(WHEELCHAIR)) - return VehicleTagParsers.wheelchair(lookup, configuration); - - throw new IllegalArgumentException("Unknown name for vehicle tag parsers: " + name); - } -} diff --git a/core/src/main/java/com/graphhopper/routing/util/EncodingManager.java b/core/src/main/java/com/graphhopper/routing/util/EncodingManager.java index ef6f02d2dd6..e859eb49ab8 100644 --- a/core/src/main/java/com/graphhopper/routing/util/EncodingManager.java +++ b/core/src/main/java/com/graphhopper/routing/util/EncodingManager.java @@ -24,10 +24,12 @@ import com.graphhopper.storage.IntsRef; import com.graphhopper.storage.StorableProperties; import com.graphhopper.util.Constants; -import com.graphhopper.util.PMap; import java.io.UncheckedIOException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; import java.util.stream.Collectors; /** @@ -126,18 +128,6 @@ public static class Builder { private final EncodedValue.InitializerConfig turnCostConfig = new EncodedValue.InitializerConfig(); private EncodingManager em = new EncodingManager(); - public Builder add(VehicleEncodedValues v) { - checkNotBuiltAlready(); - List list = new ArrayList<>(); - v.createEncodedValues(list); - list.forEach(this::add); - - list = new ArrayList<>(); - v.createTurnCostEncodedValues(list); - list.forEach(this::addTurnCostEncodedValue); - return this; - } - public Builder add(EncodedValue encodedValue) { checkNotBuiltAlready(); if (em.hasEncodedValue(encodedValue.getName())) @@ -167,9 +157,6 @@ private void checkNotBuiltAlready() { public EncodingManager build() { checkNotBuiltAlready(); - addDefaultEncodedValues(); - if (em.encodedValueMap.isEmpty()) - throw new IllegalStateException("No EncodedValues were added to the EncodingManager"); em.intsForFlags = edgeConfig.getRequiredInts(); em.intsForTurnCostFlags = turnCostConfig.getRequiredInts(); EncodingManager result = em; @@ -177,30 +164,6 @@ public EncodingManager build() { return result; } - private void addDefaultEncodedValues() { - // todo: I think ultimately these should all be removed and must be added explicitly - List keys = new ArrayList<>(Arrays.asList( - Roundabout.KEY, - RoadClass.KEY, - RoadClassLink.KEY, - RoadEnvironment.KEY, - MaxSpeed.KEY, - RoadAccess.KEY, - FerrySpeed.KEY - )); - if (em.getVehicles().stream().anyMatch(vehicle -> vehicle.contains("bike") || vehicle.contains("mtb") || vehicle.contains("racingbike"))) { - keys.add(BikeNetwork.KEY); - keys.add(GetOffBike.KEY); - keys.add(Smoothness.KEY); - } - if (em.getVehicles().stream().anyMatch(vehicle -> vehicle.contains("foot") || vehicle.contains("hike") || vehicle.contains("wheelchair"))) - keys.add(FootNetwork.KEY); - - DefaultEncodedValueFactory evFactory = new DefaultEncodedValueFactory(); - for (String key : keys) - if (!em.hasEncodedValue(key)) - add(evFactory.create(key, new PMap())); - } } public int getIntsForFlags() { @@ -215,9 +178,10 @@ public boolean hasTurnEncodedValue(String key) { return turnEncodedValueMap.get(key) != null; } + /** + * @return list of all prefixes of xy_access and xy_average_speed encoded values. + */ public List getVehicles() { - // we define the 'vehicles' as all the prefixes for which there is an access and speed EV - // any EVs that contain prefix_average_speed are accepted return getEncodedValues().stream() .filter(ev -> ev.getName().endsWith("_access")) .map(ev -> ev.getName().replaceAll("_access", "")) @@ -289,7 +253,7 @@ public T getEncodedValue(String key, Class encodedVa EncodedValue ev = encodedValueMap.get(key); // todo: why do we not just return null when EV is missing? just like java.util.Map? -> https://github.com/graphhopper/graphhopper/pull/2561#discussion_r859770067 if (ev == null) - throw new IllegalArgumentException("Cannot find EncodedValue " + key + " in collection: " + encodedValueMap.keySet()); + throw new IllegalArgumentException("Cannot find EncodedValue '" + key + "' in collection: " + encodedValueMap.keySet()); return (T) ev; } diff --git a/core/src/main/java/com/graphhopper/routing/util/MaxSpeedCalculator.java b/core/src/main/java/com/graphhopper/routing/util/MaxSpeedCalculator.java index 84fad38cf89..91fca9bf9f9 100644 --- a/core/src/main/java/com/graphhopper/routing/util/MaxSpeedCalculator.java +++ b/core/src/main/java/com/graphhopper/routing/util/MaxSpeedCalculator.java @@ -9,6 +9,7 @@ import com.graphhopper.storage.DataAccess; import com.graphhopper.storage.Directory; import com.graphhopper.storage.Graph; +import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.StopWatch; import de.westnordost.osm_legal_default_speeds.LegalDefaultSpeeds; import de.westnordost.osm_legal_default_speeds.RoadType; @@ -20,6 +21,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Function; public class MaxSpeedCalculator { @@ -129,7 +131,13 @@ public void createDataAccessForParser(Directory directory) { * the default speed library which is country-dependent. */ public void fillMaxSpeed(Graph graph, EncodingManager em) { - EnumEncodedValue urbanDensityEnc = em.getEnumEncodedValue(UrbanDensity.KEY, UrbanDensity.class); + // In DefaultMaxSpeedParser and in OSMMaxSpeedParser we don't have the rural/urban info, + // but now we have and can fill the country-dependent max_speed value where missing. + EnumEncodedValue udEnc = em.getEnumEncodedValue(UrbanDensity.KEY, UrbanDensity.class); + fillMaxSpeed(graph, em, edge -> edge.get(udEnc) != UrbanDensity.RURAL); + } + + public void fillMaxSpeed(Graph graph, EncodingManager em, Function isUrbanDensityFun) { DecimalEncodedValue maxSpeedEnc = em.getDecimalEncodedValue(MaxSpeed.KEY); BooleanEncodedValue maxSpeedEstEnc = em.getBooleanEncodedValue(MaxSpeedEstimated.KEY); @@ -143,16 +151,20 @@ public void fillMaxSpeed(Graph graph, EncodingManager em) { if (fwdMaxSpeedPureOSM != MaxSpeed.UNSET_SPEED && bwdMaxSpeedPureOSM != MaxSpeed.UNSET_SPEED) continue; - // In DefaultMaxSpeedParser and in OSMMaxSpeedParser we don't have the rural/urban info, - // but now we have and can fill the country-dependent max_speed value where missing. - double maxSpeed = iter.get(urbanDensityEnc) == UrbanDensity.RURAL - ? ruralMaxSpeedEnc.getDecimal(false, iter.getEdge(), internalMaxSpeedStorage) - : urbanMaxSpeedEnc.getDecimal(false, iter.getEdge(), internalMaxSpeedStorage); + double maxSpeed = isUrbanDensityFun.apply(iter) + ? urbanMaxSpeedEnc.getDecimal(false, iter.getEdge(), internalMaxSpeedStorage) + : ruralMaxSpeedEnc.getDecimal(false, iter.getEdge(), internalMaxSpeedStorage); if (maxSpeed != MaxSpeed.UNSET_SPEED) { - iter.set(maxSpeedEnc, - fwdMaxSpeedPureOSM == MaxSpeed.UNSET_SPEED ? maxSpeed : fwdMaxSpeedPureOSM, - bwdMaxSpeedPureOSM == MaxSpeed.UNSET_SPEED ? maxSpeed : bwdMaxSpeedPureOSM); - iter.set(maxSpeedEstEnc, true); + if (maxSpeed == 0) { + // TODO fix properly: RestrictionSetter adds artificial edges for which + // we didn't set the speed in DefaultMaxSpeedParser, #2914 + iter.set(maxSpeedEnc, MaxSpeed.UNSET_SPEED, MaxSpeed.UNSET_SPEED); + } else { + iter.set(maxSpeedEnc, + fwdMaxSpeedPureOSM == MaxSpeed.UNSET_SPEED ? maxSpeed : fwdMaxSpeedPureOSM, + bwdMaxSpeedPureOSM == MaxSpeed.UNSET_SPEED ? maxSpeed : bwdMaxSpeedPureOSM); + iter.set(maxSpeedEstEnc, true); + } } } @@ -163,6 +175,13 @@ public void close() { dataAccess.close(); } + public void checkEncodedValues(EncodingManager encodingManager) { + if (!encodingManager.hasEncodedValue(Country.KEY)) + throw new IllegalArgumentException("max_speed_calculator needs country"); + if (!encodingManager.hasEncodedValue(UrbanDensity.KEY)) + throw new IllegalArgumentException("max_speed_calculator needs urban_density"); + } + static class SpeedLimitsJson { @JsonProperty private Map meta; diff --git a/core/src/main/java/com/graphhopper/routing/util/SlopeCalculator.java b/core/src/main/java/com/graphhopper/routing/util/SlopeCalculator.java index d6e2b6ea37c..5e0fbb5ef61 100644 --- a/core/src/main/java/com/graphhopper/routing/util/SlopeCalculator.java +++ b/core/src/main/java/com/graphhopper/routing/util/SlopeCalculator.java @@ -24,15 +24,19 @@ public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way PointList pointList = way.getTag("point_list", null); if (pointList != null) { if (pointList.isEmpty() || !pointList.is3D()) { - averageSlopeEnc.setDecimal(false, edgeId, edgeIntAccess, 0); + if (maxSlopeEnc != null) + maxSlopeEnc.setDecimal(false, edgeId, edgeIntAccess, 0); + if (averageSlopeEnc != null) + averageSlopeEnc.setDecimal(false, edgeId, edgeIntAccess, 0); return; } // Calculate 2d distance, although pointList might be 3D. // This calculation is a bit expensive and edge_distance is available already, but this would be in 3D double distance2D = DistanceCalcEarth.calcDistance(pointList, false); if (distance2D < MIN_LENGTH) { - // default is minimum of average_slope is negative so we have to explicitly set it to 0 - averageSlopeEnc.setDecimal(false, edgeId, edgeIntAccess, 0); + if (averageSlopeEnc != null) + // default is minimum of average_slope is negative so we have to explicitly set it to 0 + averageSlopeEnc.setDecimal(false, edgeId, edgeIntAccess, 0); return; } @@ -40,39 +44,42 @@ public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way if (Double.isNaN(towerNodeSlope)) throw new IllegalArgumentException("average_slope was NaN for OSM way ID " + way.getId()); - if (towerNodeSlope >= 0) - averageSlopeEnc.setDecimal(false, edgeId, edgeIntAccess, Math.min(towerNodeSlope, averageSlopeEnc.getMaxStorableDecimal())); - else - averageSlopeEnc.setDecimal(true, edgeId, edgeIntAccess, Math.min(Math.abs(towerNodeSlope), averageSlopeEnc.getMaxStorableDecimal())); + if (averageSlopeEnc != null) { + if (towerNodeSlope >= 0) + averageSlopeEnc.setDecimal(false, edgeId, edgeIntAccess, Math.min(towerNodeSlope, averageSlopeEnc.getMaxStorableDecimal())); + else + averageSlopeEnc.setDecimal(true, edgeId, edgeIntAccess, Math.min(Math.abs(towerNodeSlope), averageSlopeEnc.getMaxStorableDecimal())); + } - // max_slope is more error-prone as the shorter distances increase the fluctuation - // so apply some more filtering (here we use the average elevation delta of the previous two points) - double maxSlope = 0, prevDist = 0, prevLat = pointList.getLat(0), prevLon = pointList.getLon(0); - for (int i = 1; i < pointList.size(); i++) { - double pillarDistance2D = DistanceCalcEarth.DIST_EARTH.calcDist(prevLat, prevLon, pointList.getLat(i), pointList.getLon(i)); - if (i > 1 && prevDist > MIN_LENGTH) { - double averagedPrevEle = (pointList.getEle(i - 1) + pointList.getEle(i - 2)) / 2; - double tmpSlope = calcSlope(pointList.getEle(i) - averagedPrevEle, pillarDistance2D + prevDist / 2); - maxSlope = Math.max(maxSlope, Math.abs(tmpSlope)); + if (maxSlopeEnc != null) { + // max_slope is more error-prone as the shorter distances increase the fluctuation + // so apply some more filtering (here we use the average elevation delta of the previous two points) + double maxSlope = 0, prevDist = 0, prevLat = pointList.getLat(0), prevLon = pointList.getLon(0); + for (int i = 1; i < pointList.size(); i++) { + double pillarDistance2D = DistanceCalcEarth.DIST_EARTH.calcDist(prevLat, prevLon, pointList.getLat(i), pointList.getLon(i)); + if (i > 1 && prevDist > MIN_LENGTH) { + double averagedPrevEle = (pointList.getEle(i - 1) + pointList.getEle(i - 2)) / 2; + double tmpSlope = calcSlope(pointList.getEle(i) - averagedPrevEle, pillarDistance2D + prevDist / 2); + maxSlope = Math.abs(tmpSlope) > Math.abs(maxSlope) ? tmpSlope : maxSlope; + } + prevDist = pillarDistance2D; + prevLat = pointList.getLat(i); + prevLon = pointList.getLon(i); } - prevDist = pillarDistance2D; - prevLat = pointList.getLat(i); - prevLon = pointList.getLon(i); - } - // For tunnels and bridges we cannot trust the pillar node elevation and ignore all changes. - // Probably we should somehow recalculate even the average_slope after elevation interpolation? See EdgeElevationInterpolator - if (way.hasTag("tunnel", "yes") || way.hasTag("bridge", "yes") || way.hasTag("highway", "steps")) - maxSlope = Math.abs(towerNodeSlope); - else - maxSlope = Math.max(Math.abs(towerNodeSlope), maxSlope); + // For tunnels and bridges we cannot trust the pillar node elevation and ignore all changes. + // Probably we should somehow recalculate even the average_slope after elevation interpolation? See EdgeElevationInterpolator + if (way.hasTag("tunnel", "yes") || way.hasTag("bridge", "yes") || way.hasTag("highway", "steps")) + maxSlope = towerNodeSlope; + else + maxSlope = Math.abs(towerNodeSlope) > Math.abs(maxSlope) ? towerNodeSlope : maxSlope; - if (Double.isNaN(maxSlope)) - throw new IllegalArgumentException("max_slope was NaN for OSM way ID " + way.getId()); + if (Double.isNaN(maxSlope)) + throw new IllegalArgumentException("max_slope was NaN for OSM way ID " + way.getId()); - // TODO Use two independent values for both directions to store if it is a gain or loss and not just the absolute change. - // TODO To save space then it would be nice to have an encoded value that can store two different values which are swapped when the reverse direction is used - maxSlopeEnc.setDecimal(false, edgeId, edgeIntAccess, Math.min(maxSlope, maxSlopeEnc.getMaxStorableDecimal())); + double val = Math.max(maxSlope, maxSlopeEnc.getMinStorableDecimal()); + maxSlopeEnc.setDecimal(false, edgeId, edgeIntAccess, Math.min(maxSlopeEnc.getMaxStorableDecimal(), val)); + } } } diff --git a/core/src/main/java/com/graphhopper/routing/util/TransportationMode.java b/core/src/main/java/com/graphhopper/routing/util/TransportationMode.java index 227a003ebc5..5ef212d685a 100644 --- a/core/src/main/java/com/graphhopper/routing/util/TransportationMode.java +++ b/core/src/main/java/com/graphhopper/routing/util/TransportationMode.java @@ -26,7 +26,7 @@ */ public enum TransportationMode { OTHER(false), FOOT(false), VEHICLE(false), BIKE(false), - CAR(true), MOTORCYCLE(true), HGV(true), PSV(true), BUS(true), TRAIN(false); + CAR(true), MOTORCYCLE(true), HGV(true), PSV(true), BUS(true), HOV(true), TRAIN(false); private final boolean motorVehicle; diff --git a/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValues.java b/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValues.java deleted file mode 100644 index 3b4ac26ed47..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValues.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * 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.routing.util; - -import com.graphhopper.routing.ev.*; -import com.graphhopper.util.PMap; - -import java.util.Arrays; -import java.util.List; - -import static com.graphhopper.routing.util.VehicleEncodedValuesFactory.*; - -public class VehicleEncodedValues { - public static final List OUTDOOR_VEHICLES = Arrays.asList(BIKE, RACINGBIKE, MOUNTAINBIKE, FOOT, WHEELCHAIR); - - private final String name; - private final BooleanEncodedValue accessEnc; - private final DecimalEncodedValue avgSpeedEnc; - private final DecimalEncodedValue priorityEnc; - private final BooleanEncodedValue turnRestrictionEnc; - - public static VehicleEncodedValues foot(PMap properties) { - String name = properties.getString("name", "foot"); - int speedBits = properties.getInt("speed_bits", 4); - double speedFactor = properties.getDouble("speed_factor", 1); - boolean speedTwoDirections = properties.getBool("speed_two_directions", false); - boolean turnCosts = properties.getBool("turn_costs", false); - BooleanEncodedValue accessEnc = VehicleAccess.create(name); - DecimalEncodedValue speedEnc = VehicleSpeed.create(name, speedBits, speedFactor, speedTwoDirections); - DecimalEncodedValue priorityEnc = VehiclePriority.create(name, 4, PriorityCode.getFactor(1), false); - BooleanEncodedValue turnRestrictionEnc = turnCosts ? TurnRestriction.create(name) : null; - return new VehicleEncodedValues(name, accessEnc, speedEnc, priorityEnc, turnRestrictionEnc); - } - - public static VehicleEncodedValues wheelchair(PMap properties) { - if (properties.has("speed_two_directions")) - throw new IllegalArgumentException("wheelchair always uses two directions"); - return foot(new PMap(properties) - .putObject("name", properties.getString("name", "wheelchair")) - .putObject("speed_two_directions", true) - ); - } - - public static VehicleEncodedValues bike(PMap properties) { - String name = properties.getString("name", "bike"); - int speedBits = properties.getInt("speed_bits", 4); - double speedFactor = properties.getDouble("speed_factor", 2); - boolean speedTwoDirections = properties.getBool("speed_two_directions", false); - boolean turnCosts = properties.getBool("turn_costs", false); - BooleanEncodedValue accessEnc = VehicleAccess.create(name); - DecimalEncodedValue speedEnc = VehicleSpeed.create(name, speedBits, speedFactor, speedTwoDirections); - DecimalEncodedValue priorityEnc = VehiclePriority.create(name, 4, PriorityCode.getFactor(1), false); - BooleanEncodedValue turnRestrictionEnc = turnCosts ? TurnRestriction.create(name) : null; - return new VehicleEncodedValues(name, accessEnc, speedEnc, priorityEnc, turnRestrictionEnc); - } - - public static VehicleEncodedValues racingbike(PMap properties) { - return bike(new PMap(properties).putObject("name", properties.getString("name", "racingbike"))); - } - - public static VehicleEncodedValues mountainbike(PMap properties) { - return bike(new PMap(properties).putObject("name", properties.getString("name", "mtb"))); - } - - public static VehicleEncodedValues car(PMap properties) { - String name = properties.getString("name", "car"); - int speedBits = properties.getInt("speed_bits", 7); - double speedFactor = properties.getDouble("speed_factor", 2); - boolean turnCosts = properties.getBool("turn_costs", false); - BooleanEncodedValue accessEnc = VehicleAccess.create(name); - DecimalEncodedValue speedEnc = VehicleSpeed.create(name, speedBits, speedFactor, true); - BooleanEncodedValue turnRestrictionEnc = turnCosts ? TurnRestriction.create(name) : null; - return new VehicleEncodedValues(name, accessEnc, speedEnc, null, turnRestrictionEnc); - } - - public static VehicleEncodedValues roads(PMap properties) { - String name = properties.getString("name", "roads"); - int speedBits = properties.getInt("speed_bits", 7); - double speedFactor = properties.getDouble("speed_factor", 2); - boolean speedTwoDirections = properties.getBool("speed_two_directions", true); - boolean turnCosts = properties.getBool("turn_costs", false); - BooleanEncodedValue accessEnc = VehicleAccess.create(name); - DecimalEncodedValue speedEnc = VehicleSpeed.create(name, speedBits, speedFactor, speedTwoDirections); - BooleanEncodedValue turnRestrictionEnc = turnCosts ? TurnRestriction.create(name) : null; - return new VehicleEncodedValues(name, accessEnc, speedEnc, null, turnRestrictionEnc); - } - - public VehicleEncodedValues(String name, BooleanEncodedValue accessEnc, DecimalEncodedValue avgSpeedEnc, - DecimalEncodedValue priorityEnc, BooleanEncodedValue turnRestrictionEnc) { - this.name = name; - this.accessEnc = accessEnc; - this.avgSpeedEnc = avgSpeedEnc; - this.priorityEnc = priorityEnc; - this.turnRestrictionEnc = turnRestrictionEnc; - } - - public void createEncodedValues(List registerNewEncodedValue) { - if (accessEnc != null) - registerNewEncodedValue.add(accessEnc); - if (avgSpeedEnc != null) - registerNewEncodedValue.add(avgSpeedEnc); - if (priorityEnc != null) - registerNewEncodedValue.add(priorityEnc); - } - - public void createTurnCostEncodedValues(List registerNewTurnCostEncodedValues) { - if (turnRestrictionEnc != null) - registerNewTurnCostEncodedValues.add(turnRestrictionEnc); - } - - public BooleanEncodedValue getAccessEnc() { - return accessEnc; - } - - public DecimalEncodedValue getAverageSpeedEnc() { - return avgSpeedEnc; - } - - public DecimalEncodedValue getPriorityEnc() { - return priorityEnc; - } - - public BooleanEncodedValue getTurnRestrictionEnc() { - return turnRestrictionEnc; - } - - public String getName() { - return name; - } - - @Override - public String toString() { - return getName(); - } -} diff --git a/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValuesFactory.java b/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValuesFactory.java deleted file mode 100644 index e2b05569059..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValuesFactory.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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.routing.util; - -import com.graphhopper.util.PMap; - -/** - * @author Peter Karich - */ -public interface VehicleEncodedValuesFactory { - String ROADS = "roads"; - String CAR = "car"; - String BIKE = "bike"; - String RACINGBIKE = "racingbike"; - String MOUNTAINBIKE = "mtb"; - String FOOT = "foot"; - String WHEELCHAIR = "wheelchair"; - - VehicleEncodedValues createVehicleEncodedValues(String name, PMap configuration); - -} diff --git a/core/src/main/java/com/graphhopper/routing/util/VehicleTagParserFactory.java b/core/src/main/java/com/graphhopper/routing/util/VehicleTagParserFactory.java deleted file mode 100644 index 4998a9609ba..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/VehicleTagParserFactory.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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.routing.util; - -import com.graphhopper.routing.ev.EncodedValueLookup; -import com.graphhopper.util.PMap; - -public interface VehicleTagParserFactory { - VehicleTagParsers createParsers(EncodedValueLookup lookup, String name, PMap configuration); -} diff --git a/core/src/main/java/com/graphhopper/routing/util/VehicleTagParsers.java b/core/src/main/java/com/graphhopper/routing/util/VehicleTagParsers.java deleted file mode 100644 index fa2b357e503..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/VehicleTagParsers.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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.routing.util; - -import com.graphhopper.reader.osm.conditional.DateRangeParser; -import com.graphhopper.routing.ev.EncodedValueLookup; -import com.graphhopper.routing.util.parsers.*; -import com.graphhopper.util.PMap; - -import java.util.Arrays; -import java.util.List; - -public class VehicleTagParsers { - private final TagParser accessParser; - private final TagParser speedParser; - private final TagParser priorityParser; - - public static VehicleTagParsers roads(EncodedValueLookup lookup, PMap properties) { - return new VehicleTagParsers( - new RoadsAccessParser(lookup, properties), - new RoadsAverageSpeedParser(lookup, properties), - null - ); - } - - public static VehicleTagParsers car(EncodedValueLookup lookup, PMap properties) { - return new VehicleTagParsers( - new CarAccessParser(lookup, properties).init(properties.getObject("date_range_parser", new DateRangeParser())), - new CarAverageSpeedParser(lookup, properties), - null - ); - } - - public static VehicleTagParsers bike(EncodedValueLookup lookup, PMap properties) { - return new VehicleTagParsers( - new BikeAccessParser(lookup, properties).init(properties.getObject("date_range_parser", new DateRangeParser())), - new BikeAverageSpeedParser(lookup, properties), - new BikePriorityParser(lookup, properties) - ); - } - - public static VehicleTagParsers racingbike(EncodedValueLookup lookup, PMap properties) { - return new VehicleTagParsers( - new RacingBikeAccessParser(lookup, properties).init(properties.getObject("date_range_parser", new DateRangeParser())), - new RacingBikeAverageSpeedParser(lookup, properties), - new RacingBikePriorityParser(lookup, properties) - ); - } - - public static VehicleTagParsers mtb(EncodedValueLookup lookup, PMap properties) { - return new VehicleTagParsers( - new MountainBikeAccessParser(lookup, properties).init(properties.getObject("date_range_parser", new DateRangeParser())), - new MountainBikeAverageSpeedParser(lookup, properties), - new MountainBikePriorityParser(lookup, properties) - ); - } - - public static VehicleTagParsers foot(EncodedValueLookup lookup, PMap properties) { - return new VehicleTagParsers( - new FootAccessParser(lookup, properties).init(properties.getObject("date_range_parser", new DateRangeParser())), - new FootAverageSpeedParser(lookup, properties), - new FootPriorityParser(lookup, properties) - ); - } - - public static VehicleTagParsers wheelchair(EncodedValueLookup lookup, PMap properties) { - return new VehicleTagParsers( - new WheelchairAccessParser(lookup, properties).init(properties.getObject("date_range_parser", new DateRangeParser())), - new WheelchairAverageSpeedParser(lookup, properties), - new WheelchairPriorityParser(lookup, properties) - ); - } - - public VehicleTagParsers(TagParser accessParser, TagParser speedParser, TagParser priorityParser) { - this.accessParser = accessParser; - this.speedParser = speedParser; - this.priorityParser = priorityParser; - } - - public TagParser getAccessParser() { - return accessParser; - } - - public TagParser getSpeedParser() { - return speedParser; - } - - public TagParser getPriorityParser() { - return priorityParser; - } - - public List getTagParsers() { - return priorityParser == null ? Arrays.asList(accessParser, speedParser) : Arrays.asList(accessParser, speedParser, priorityParser); - } - -} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/HungaryCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/HungaryCountryRule.java index 4955ab2aeaa..0161d3d9ea3 100644 --- a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/HungaryCountryRule.java +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/HungaryCountryRule.java @@ -18,8 +18,10 @@ package com.graphhopper.routing.util.countryrules.europe; import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.RoadAccess; import com.graphhopper.routing.ev.RoadClass; import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.TransportationMode; import com.graphhopper.routing.util.countryrules.CountryRule; /** @@ -29,6 +31,26 @@ */ public class HungaryCountryRule implements CountryRule { + @Override + public RoadAccess getAccess(ReaderWay readerWay, TransportationMode transportationMode, RoadAccess currentRoadAccess) { + // Pedestrian traffic and bicycles are not restricted + if (transportationMode == TransportationMode.FOOT || transportationMode == TransportationMode.BIKE) { + return currentRoadAccess; + } + + // Override only bogus "yes" and missing/other + if (currentRoadAccess != RoadAccess.YES && currentRoadAccess != RoadAccess.OTHER) { + return currentRoadAccess; + } + + RoadClass roadClass = RoadClass.find(readerWay.getTag("highway", "")); + if (roadClass == RoadClass.LIVING_STREET) { + return RoadAccess.DESTINATION; + } + + return currentRoadAccess; + } + @Override public Toll getToll(ReaderWay readerWay, Toll currentToll) { if (currentToll != Toll.MISSING) { diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/AbstractAccessParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/AbstractAccessParser.java index 50522b10e7e..18a698e7ffc 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/AbstractAccessParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/AbstractAccessParser.java @@ -2,9 +2,6 @@ import com.graphhopper.reader.ReaderNode; import com.graphhopper.reader.ReaderWay; -import com.graphhopper.reader.osm.conditional.ConditionalOSMTagInspector; -import com.graphhopper.reader.osm.conditional.ConditionalTagInspector; -import com.graphhopper.reader.osm.conditional.DateRangeParser; import com.graphhopper.routing.ev.BooleanEncodedValue; import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.routing.util.TransportationMode; @@ -17,7 +14,7 @@ public abstract class AbstractAccessParser implements TagParser { static final Collection INTENDED = Arrays.asList("yes", "designated", "official", "permissive"); // order is important - protected final List restrictions = new ArrayList<>(5); + protected final List restrictionKeys = new ArrayList<>(5); protected final Set restrictedValues = new HashSet<>(5); protected final Set intendedValues = new HashSet<>(INTENDED); @@ -26,7 +23,6 @@ public abstract class AbstractAccessParser implements TagParser { protected final Set barriers = new HashSet<>(5); protected final BooleanEncodedValue accessEnc; private boolean blockFords = true; - private ConditionalTagInspector conditionalTagInspector; protected AbstractAccessParser(BooleanEncodedValue accessEnc, TransportationMode transportationMode) { this.accessEnc = accessEnc; @@ -38,17 +34,7 @@ protected AbstractAccessParser(BooleanEncodedValue accessEnc, TransportationMode restrictedValues.add("private"); restrictedValues.add("permit"); - restrictions.addAll(OSMRoadAccessParser.toOSMRestrictions(transportationMode)); - } - - public AbstractAccessParser init(DateRangeParser dateRangeParser) { - setConditionalTagInspector(new ConditionalOSMTagInspector(Collections.singletonList(dateRangeParser), - restrictions, restrictedValues, intendedValues, false)); - return this; - } - - protected void setConditionalTagInspector(ConditionalTagInspector inspector) { - conditionalTagInspector = inspector; + restrictionKeys.addAll(OSMRoadAccessParser.toOSMRestrictions(transportationMode)); } public boolean isBlockFords() { @@ -70,10 +56,6 @@ protected void blockPrivate(boolean blockPrivate) { } } - public ConditionalTagInspector getConditionalTagInspector() { - return conditionalTagInspector; - } - protected void handleBarrierEdge(int edgeId, EdgeIntAccess edgeIntAccess, Map nodeTags) { // for now we just create a dummy reader node, because our encoders do not make use of the coordinates anyway ReaderNode readerNode = new ReaderNode(0, 0, 0, nodeTags); @@ -93,11 +75,11 @@ public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way public abstract void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way); /** - * @return true if the given OSM node blocks access for this vehicle, false otherwise + * @return true if the given OSM node blocks access for the specified restrictions, false otherwise */ public boolean isBarrier(ReaderNode node) { // note that this method will be only called for certain nodes as defined by OSMReader! - String firstValue = node.getFirstPriorityTag(restrictions); + String firstValue = node.getFirstValue(restrictionKeys); if (restrictedValues.contains(firstValue) || node.hasTag("locked", "yes")) return true; else if (intendedValues.contains(firstValue)) @@ -112,8 +94,8 @@ public final BooleanEncodedValue getAccessEnc() { return accessEnc; } - public final List getRestrictions() { - return restrictions; + public final List getRestrictionKeys() { + return restrictionKeys; } public final String getName() { diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/BikeAccessParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/BikeAccessParser.java index e67fbfe31e6..28ffbeb33c1 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/BikeAccessParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/BikeAccessParser.java @@ -9,7 +9,7 @@ public class BikeAccessParser extends BikeCommonAccessParser { public BikeAccessParser(EncodedValueLookup lookup, PMap properties) { - this(lookup.getBooleanEncodedValue(VehicleAccess.key(properties.getString("name", "bike"))), + this(lookup.getBooleanEncodedValue(VehicleAccess.key("bike")), lookup.getBooleanEncodedValue(Roundabout.KEY)); blockPrivate(properties.getBool("block_private", true)); blockFords(properties.getBool("block_fords", false)); diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/BikeAverageSpeedParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/BikeAverageSpeedParser.java index 53734484dd9..436c7936201 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/BikeAverageSpeedParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/BikeAverageSpeedParser.java @@ -1,12 +1,11 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.routing.ev.*; -import com.graphhopper.util.PMap; public class BikeAverageSpeedParser extends BikeCommonAverageSpeedParser { - public BikeAverageSpeedParser(EncodedValueLookup lookup, PMap properties) { - this(lookup.getDecimalEncodedValue(VehicleSpeed.key(properties.getString("name", "bike"))), + public BikeAverageSpeedParser(EncodedValueLookup lookup) { + this(lookup.getDecimalEncodedValue(VehicleSpeed.key("bike")), lookup.getEnumEncodedValue(Smoothness.KEY, Smoothness.class), lookup.getDecimalEncodedValue(FerrySpeed.KEY)); } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/BikeCommonAccessParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/BikeCommonAccessParser.java index 685f073cc85..885437ca1ce 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/BikeCommonAccessParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/BikeCommonAccessParser.java @@ -9,6 +9,8 @@ import java.util.*; +import static com.graphhopper.routing.util.parsers.OSMTemporalAccessParser.hasTemporalRestriction; + public abstract class BikeCommonAccessParser extends AbstractAccessParser implements TagParser { private static final Set OPP_LANES = new HashSet<>(Arrays.asList("opposite", "opposite_lane", "opposite_track")); @@ -57,7 +59,7 @@ public WayAccess getAccess(ReaderWay way) { access = WayAccess.WAY; if (!access.canSkip()) { - if (way.hasTag(restrictions, restrictedValues) && !getConditionalTagInspector().isRestrictedWayConditionallyPermitted(way)) + if (way.hasTag(restrictionKeys, restrictedValues)) return WayAccess.CAN_SKIP; return access; } @@ -78,15 +80,14 @@ public WayAccess getAccess(ReaderWay way) { if (way.hasTag("bicycle", "dismount") || way.hasTag("highway", "cycleway")) return WayAccess.WAY; - boolean permittedWayConditionallyRestricted = getConditionalTagInspector().isPermittedWayConditionallyRestricted(way); - boolean restrictedWayConditionallyPermitted = getConditionalTagInspector().isRestrictedWayConditionallyPermitted(way); - String firstValue = way.getFirstPriorityTag(restrictions); - if (!firstValue.isEmpty()) { + int firstIndex = way.getFirstIndex(restrictionKeys); + if (firstIndex >= 0) { + String firstValue = way.getTag(restrictionKeys.get(firstIndex), ""); String[] restrict = firstValue.split(";"); for (String value : restrict) { - if (restrictedValues.contains(value) && !restrictedWayConditionallyPermitted) + if (restrictedValues.contains(value) && !hasTemporalRestriction(way, firstIndex, restrictionKeys)) return WayAccess.CAN_SKIP; - if (intendedValues.contains(value) && !permittedWayConditionallyRestricted) + if (intendedValues.contains(value)) return WayAccess.WAY; } } @@ -101,9 +102,6 @@ public WayAccess getAccess(ReaderWay way) { if (isBlockFords() && ("ford".equals(highwayValue) || way.hasTag("ford"))) return WayAccess.CAN_SKIP; - if (permittedWayConditionallyRestricted) - return WayAccess.CAN_SKIP; - return WayAccess.WAY; } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/BikeCommonAverageSpeedParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/BikeCommonAverageSpeedParser.java index 4a86ac0950b..bde7d88676a 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/BikeCommonAverageSpeedParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/BikeCommonAverageSpeedParser.java @@ -50,13 +50,13 @@ protected BikeCommonAverageSpeedParser(DecimalEncodedValue speedEnc, EnumEncoded setSurfaceSpeed("concrete", 18); setSurfaceSpeed("concrete:lanes", 16); setSurfaceSpeed("concrete:plates", 16); - setSurfaceSpeed("paving_stones", 14); - setSurfaceSpeed("paving_stones:30", 14); + setSurfaceSpeed("paving_stones", 16); + setSurfaceSpeed("paving_stones:30", 16); setSurfaceSpeed("unpaved", 12); setSurfaceSpeed("compacted", 14); setSurfaceSpeed("dirt", 10); setSurfaceSpeed("earth", 12); - setSurfaceSpeed("fine_gravel", 18); + setSurfaceSpeed("fine_gravel", 14); // should not be faster than compacted setSurfaceSpeed("grass", 8); setSurfaceSpeed("grass_paver", 8); setSurfaceSpeed("gravel", 12); diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/BikeCommonPriorityParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/BikeCommonPriorityParser.java index add8fa9489d..d20fcd5e15b 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/BikeCommonPriorityParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/BikeCommonPriorityParser.java @@ -10,6 +10,7 @@ import com.graphhopper.storage.IntsRef; import java.util.*; +import java.util.stream.Stream; import static com.graphhopper.routing.ev.RouteNetwork.*; import static com.graphhopper.routing.util.PriorityCode.*; @@ -183,11 +184,11 @@ void collect(ReaderWay way, double wayTypeSpeed, TreeMap w } } - String cycleway = way.getFirstPriorityTag(Arrays.asList("cycleway", "cycleway:left", "cycleway:right", "cycleway:both")); - if (Arrays.asList("lane", "opposite_track", "shared_lane", "share_busway", "shoulder").contains(cycleway)) { - weightToPrioMap.put(100d, SLIGHT_PREFER); - } else if ("track".equals(cycleway)) { + List cyclewayValues = Stream.of("cycleway", "cycleway:left", "cycleway:both", "cycleway:right").map(key -> way.getTag(key, "")).toList(); + if (cyclewayValues.contains("track")) { weightToPrioMap.put(100d, PREFER); + } else if (Stream.of("lane", "opposite_track", "shared_lane", "share_busway", "shoulder").anyMatch(cyclewayValues::contains)) { + weightToPrioMap.put(100d, SLIGHT_PREFER); } if (way.hasTag("bicycle", "use_sidepath")) { diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/BikeNetworkParserHelper.java b/core/src/main/java/com/graphhopper/routing/util/parsers/BikeNetworkParserHelper.java new file mode 100644 index 00000000000..e55e180fff8 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/BikeNetworkParserHelper.java @@ -0,0 +1,19 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.routing.ev.RouteNetwork; + +public class BikeNetworkParserHelper { + static RouteNetwork determine(String tag) { + RouteNetwork newBikeNetwork = RouteNetwork.LOCAL; + if ("lcn".equals(tag)) { + newBikeNetwork = RouteNetwork.LOCAL; + } else if ("rcn".equals(tag)) { + newBikeNetwork = RouteNetwork.REGIONAL; + } else if ("ncn".equals(tag)) { + newBikeNetwork = RouteNetwork.NATIONAL; + } else if ("icn".equals(tag)) { + newBikeNetwork = RouteNetwork.INTERNATIONAL; + } + return newBikeNetwork; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/BikePriorityParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/BikePriorityParser.java index 80d6a14e492..3c8266e323e 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/BikePriorityParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/BikePriorityParser.java @@ -1,14 +1,13 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.routing.ev.*; -import com.graphhopper.util.PMap; public class BikePriorityParser extends BikeCommonPriorityParser { - public BikePriorityParser(EncodedValueLookup lookup, PMap properties) { + public BikePriorityParser(EncodedValueLookup lookup) { this( - lookup.getDecimalEncodedValue(VehiclePriority.key(properties.getString("name", "bike"))), - lookup.getDecimalEncodedValue(VehicleSpeed.key(properties.getString("name", "bike"))), + lookup.getDecimalEncodedValue(VehiclePriority.key("bike")), + lookup.getDecimalEncodedValue(VehicleSpeed.key("bike")), lookup.getEnumEncodedValue(BikeNetwork.KEY, RouteNetwork.class) ); } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/CarAccessParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/CarAccessParser.java index e4832e5b170..d508d68e2bf 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/CarAccessParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/CarAccessParser.java @@ -26,6 +26,8 @@ import java.util.*; +import static com.graphhopper.routing.util.parsers.OSMTemporalAccessParser.hasTemporalRestriction; + public class CarAccessParser extends AbstractAccessParser implements TagParser { protected final Set trackTypeValues = new HashSet<>(); @@ -34,7 +36,7 @@ public class CarAccessParser extends AbstractAccessParser implements TagParser { public CarAccessParser(EncodedValueLookup lookup, PMap properties) { this( - lookup.getBooleanEncodedValue(VehicleAccess.key(properties.getString("name", "car"))), + lookup.getBooleanEncodedValue(VehicleAccess.key("car")), lookup.getBooleanEncodedValue(Roundabout.KEY), properties, TransportationMode.CAR @@ -79,7 +81,8 @@ public CarAccessParser(BooleanEncodedValue accessEnc, public WayAccess getAccess(ReaderWay way) { // TODO: Ferries have conditionals, like opening hours or are closed during some time in the year String highwayValue = way.getTag("highway"); - String firstValue = way.getFirstPriorityTag(restrictions); + int firstIndex = way.getFirstIndex(restrictionKeys); + String firstValue = firstIndex < 0 ? "" : way.getTag(restrictionKeys.get(firstIndex), ""); if (highwayValue == null) { if (FerrySpeedCalculator.isFerry(way)) { if (restrictedValues.contains(firstValue)) @@ -87,7 +90,7 @@ public WayAccess getAccess(ReaderWay way) { if (intendedValues.contains(firstValue) || // implied default is allowed only if foot and bicycle is not specified: firstValue.isEmpty() && !way.hasTag("foot") && !way.hasTag("bicycle") || - // if hgv is allowed than smaller trucks and cars are allowed too + // if hgv is allowed then smaller trucks and cars are allowed too way.hasTag("hgv", "yes")) return WayAccess.FERRY; } @@ -107,14 +110,12 @@ public WayAccess getAccess(ReaderWay way) { return WayAccess.CAN_SKIP; // multiple restrictions needs special handling - boolean permittedWayConditionallyRestricted = getConditionalTagInspector().isPermittedWayConditionallyRestricted(way); - boolean restrictedWayConditionallyPermitted = getConditionalTagInspector().isRestrictedWayConditionallyPermitted(way); - if (!firstValue.isEmpty()) { + if (firstIndex >= 0) { String[] restrict = firstValue.split(";"); for (String value : restrict) { - if (restrictedValues.contains(value) && !restrictedWayConditionallyPermitted) + if (restrictedValues.contains(value) && !hasTemporalRestriction(way, firstIndex, restrictionKeys)) return WayAccess.CAN_SKIP; - if (intendedValues.contains(value) && !permittedWayConditionallyRestricted) + if (intendedValues.contains(value)) return WayAccess.WAY; } } @@ -122,9 +123,6 @@ public WayAccess getAccess(ReaderWay way) { if (isBlockFords() && ("ford".equals(highwayValue) || way.hasTag("ford"))) return WayAccess.CAN_SKIP; - if (permittedWayConditionallyRestricted) - return WayAccess.CAN_SKIP; - return WayAccess.WAY; } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/CarAverageSpeedParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/CarAverageSpeedParser.java index 7262d02aca9..4ef43ed30ea 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/CarAverageSpeedParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/CarAverageSpeedParser.java @@ -21,7 +21,6 @@ import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.FerrySpeedCalculator; import com.graphhopper.util.Helper; -import com.graphhopper.util.PMap; import java.util.HashMap; import java.util.HashSet; @@ -42,8 +41,8 @@ public class CarAverageSpeedParser extends AbstractAverageSpeedParser implements */ protected final Map defaultSpeedMap = new HashMap<>(); - public CarAverageSpeedParser(EncodedValueLookup lookup, PMap properties) { - this(lookup.getDecimalEncodedValue(VehicleSpeed.key(properties.getString("name", "car"))), + public CarAverageSpeedParser(EncodedValueLookup lookup) { + this(lookup.getDecimalEncodedValue(VehicleSpeed.key("car")), lookup.getDecimalEncodedValue(FerrySpeed.KEY)); } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/DefaultMaxSpeedParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/DefaultMaxSpeedParser.java index cdc84c83e82..a143b758cd8 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/DefaultMaxSpeedParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/DefaultMaxSpeedParser.java @@ -84,6 +84,7 @@ private Map filter(Map tags) { String key = entry.getKey(); if (speeds.isRelevantTagKey(key) || key.equals("country") + || key.equals("country_state") // the :conditional tags are not yet necessary for us and expensive in the speeds library // see https://github.com/westnordost/osm-legal-default-speeds/issues/7 || key.startsWith("maxspeed:") && !key.endsWith(":conditional")) diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/DefaultTagParserFactory.java b/core/src/main/java/com/graphhopper/routing/util/parsers/DefaultTagParserFactory.java deleted file mode 100644 index bf5d48cb128..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/DefaultTagParserFactory.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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.routing.util.parsers; - -import com.graphhopper.routing.ev.*; -import com.graphhopper.routing.util.FerrySpeedCalculator; -import com.graphhopper.routing.util.TransportationMode; -import com.graphhopper.util.PMap; - -public class DefaultTagParserFactory implements TagParserFactory { - - @Override - public TagParser create(EncodedValueLookup lookup, String name, PMap properties) { - if (Roundabout.KEY.equals(name)) - return new OSMRoundaboutParser(lookup.getBooleanEncodedValue(Roundabout.KEY)); - else if (name.equals(RoadClass.KEY)) - return new OSMRoadClassParser(lookup.getEnumEncodedValue(RoadClass.KEY, RoadClass.class)); - else if (name.equals(RoadClassLink.KEY)) - return new OSMRoadClassLinkParser(lookup.getBooleanEncodedValue(RoadClassLink.KEY)); - else if (name.equals(RoadEnvironment.KEY)) - return new OSMRoadEnvironmentParser(lookup.getEnumEncodedValue(RoadEnvironment.KEY, RoadEnvironment.class)); - else if (name.equals(RoadAccess.KEY)) - return new OSMRoadAccessParser(lookup.getEnumEncodedValue(RoadAccess.KEY, RoadAccess.class), OSMRoadAccessParser.toOSMRestrictions(TransportationMode.CAR)); - else if (name.equals(MaxSpeed.KEY)) - return new OSMMaxSpeedParser(lookup.getDecimalEncodedValue(MaxSpeed.KEY)); - else if (name.equals(MaxWeight.KEY)) - return new OSMMaxWeightParser(lookup.getDecimalEncodedValue(MaxWeight.KEY)); - else if (name.equals(MaxWeightExcept.KEY)) - return new MaxWeightExceptParser(lookup.getEnumEncodedValue(MaxWeightExcept.KEY, MaxWeightExcept.class)); - else if (name.equals(MaxHeight.KEY)) - return new OSMMaxHeightParser(lookup.getDecimalEncodedValue(MaxHeight.KEY)); - else if (name.equals(MaxWidth.KEY)) - return new OSMMaxWidthParser(lookup.getDecimalEncodedValue(MaxWidth.KEY)); - else if (name.equals(MaxAxleLoad.KEY)) - return new OSMMaxAxleLoadParser(lookup.getDecimalEncodedValue(MaxAxleLoad.KEY)); - else if (name.equals(MaxLength.KEY)) - return new OSMMaxLengthParser(lookup.getDecimalEncodedValue(MaxLength.KEY)); - else if (name.equals(Surface.KEY)) - return new OSMSurfaceParser(lookup.getEnumEncodedValue(Surface.KEY, Surface.class)); - else if (name.equals(Smoothness.KEY)) - return new OSMSmoothnessParser(lookup.getEnumEncodedValue(Smoothness.KEY, Smoothness.class)); - else if (name.equals(Toll.KEY)) - return new OSMTollParser(lookup.getEnumEncodedValue(Toll.KEY, Toll.class)); - else if (name.equals(TrackType.KEY)) - return new OSMTrackTypeParser(lookup.getEnumEncodedValue(TrackType.KEY, TrackType.class)); - else if (name.equals(Hgv.KEY)) - return new OSMHgvParser(lookup.getEnumEncodedValue(Hgv.KEY, Hgv.class)); - else if (name.equals(Hazmat.KEY)) - return new OSMHazmatParser(lookup.getEnumEncodedValue(Hazmat.KEY, Hazmat.class)); - else if (name.equals(HazmatTunnel.KEY)) - return new OSMHazmatTunnelParser(lookup.getEnumEncodedValue(HazmatTunnel.KEY, HazmatTunnel.class)); - else if (name.equals(HazmatWater.KEY)) - return new OSMHazmatWaterParser(lookup.getEnumEncodedValue(HazmatWater.KEY, HazmatWater.class)); - else if (name.equals(Lanes.KEY)) - return new OSMLanesParser(lookup.getIntEncodedValue(Lanes.KEY)); - else if (name.equals(OSMWayID.KEY)) - return new OSMWayIDParser(lookup.getIntEncodedValue(OSMWayID.KEY)); - else if (name.equals(MtbRating.KEY)) - return new OSMMtbRatingParser(lookup.getIntEncodedValue(MtbRating.KEY)); - else if (name.equals(HikeRating.KEY)) - return new OSMHikeRatingParser(lookup.getIntEncodedValue(HikeRating.KEY)); - else if (name.equals(HorseRating.KEY)) - return new OSMHorseRatingParser(lookup.getIntEncodedValue(HorseRating.KEY)); - else if (name.equals(Footway.KEY)) - return new OSMFootwayParser(lookup.getEnumEncodedValue(Footway.KEY, Footway.class)); - else if (name.equals(Country.KEY)) - return new CountryParser(lookup.getEnumEncodedValue(Country.KEY, Country.class)); - else if (name.equals(State.KEY)) - return new StateParser(lookup.getEnumEncodedValue(State.KEY, State.class)); - else if (name.equals(Crossing.KEY)) - return new OSMCrossingParser(lookup.getEnumEncodedValue(Crossing.KEY, Crossing.class)); - else if (name.equals(FerrySpeed.KEY)) - return new FerrySpeedCalculator(lookup.getDecimalEncodedValue(FerrySpeed.KEY)); - return null; - } -} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/FootAccessParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/FootAccessParser.java index f52b64325b9..249846b9f38 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/FootAccessParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/FootAccessParser.java @@ -28,16 +28,16 @@ import static com.graphhopper.routing.ev.RouteNetwork.*; import static com.graphhopper.routing.util.PriorityCode.UNCHANGED; +import static com.graphhopper.routing.util.parsers.OSMTemporalAccessParser.hasTemporalRestriction; public class FootAccessParser extends AbstractAccessParser implements TagParser { final Set allowedHighwayTags = new HashSet<>(); - final Set allowedSacScale = new HashSet<>(); protected HashSet sidewalkValues = new HashSet<>(5); protected Map routeMap = new HashMap<>(); public FootAccessParser(EncodedValueLookup lookup, PMap properties) { - this(lookup.getBooleanEncodedValue(VehicleAccess.key(properties.getString("name", "foot")))); + this(lookup.getBooleanEncodedValue(VehicleAccess.key("foot"))); blockPrivate(properties.getBool("block_private", true)); blockFords(properties.getBool("block_fords", false)); } @@ -84,10 +84,6 @@ protected FootAccessParser(BooleanEncodedValue accessEnc) { routeMap.put(NATIONAL, UNCHANGED.getValue()); routeMap.put(REGIONAL, UNCHANGED.getValue()); routeMap.put(LOCAL, UNCHANGED.getValue()); - - allowedSacScale.add("hiking"); - allowedSacScale.add("mountain_hiking"); - allowedSacScale.add("demanding_mountain_hiking"); } /** @@ -112,7 +108,7 @@ public WayAccess getAccess(ReaderWay way) { acceptPotentially = WayAccess.WAY; if (!acceptPotentially.canSkip()) { - if (way.hasTag(restrictions, restrictedValues) && !getConditionalTagInspector().isRestrictedWayConditionallyPermitted(way)) + if (way.hasTag(restrictionKeys, restrictedValues)) return WayAccess.CAN_SKIP; return acceptPotentially; } @@ -120,19 +116,18 @@ public WayAccess getAccess(ReaderWay way) { return WayAccess.CAN_SKIP; } - // other scales are too dangerous, see http://wiki.openstreetmap.org/wiki/Key:sac_scale - if (way.getTag("sac_scale") != null && !way.hasTag("sac_scale", allowedSacScale)) + // via_ferrata is too dangerous, see #1326 + if ("via_ferrata".equals(highwayValue)) return WayAccess.CAN_SKIP; - boolean permittedWayConditionallyRestricted = getConditionalTagInspector().isPermittedWayConditionallyRestricted(way); - boolean restrictedWayConditionallyPermitted = getConditionalTagInspector().isRestrictedWayConditionallyPermitted(way); - String firstValue = way.getFirstPriorityTag(restrictions); - if (!firstValue.isEmpty()) { + int firstIndex = way.getFirstIndex(restrictionKeys); + if (firstIndex >= 0) { + String firstValue = way.getTag(restrictionKeys.get(firstIndex), ""); String[] restrict = firstValue.split(";"); for (String value : restrict) { - if (restrictedValues.contains(value) && !restrictedWayConditionallyPermitted) + if (restrictedValues.contains(value) && !hasTemporalRestriction(way, firstIndex, restrictionKeys)) return WayAccess.CAN_SKIP; - if (intendedValues.contains(value) && !permittedWayConditionallyRestricted) + if (intendedValues.contains(value)) return WayAccess.WAY; } } @@ -149,9 +144,6 @@ public WayAccess getAccess(ReaderWay way) { if (isBlockFords() && ("ford".equals(highwayValue) || way.hasTag("ford"))) return WayAccess.CAN_SKIP; - if (permittedWayConditionallyRestricted) - return WayAccess.CAN_SKIP; - return WayAccess.WAY; } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/FootAverageSpeedParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/FootAverageSpeedParser.java index 9dd68d0e23d..55fccf28973 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/FootAverageSpeedParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/FootAverageSpeedParser.java @@ -3,7 +3,6 @@ import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.FerrySpeedCalculator; -import com.graphhopper.util.PMap; import java.util.HashMap; import java.util.Map; @@ -16,12 +15,12 @@ public class FootAverageSpeedParser extends AbstractAverageSpeedParser implement static final int MEAN_SPEED = 5; protected Map routeMap = new HashMap<>(); - public FootAverageSpeedParser(EncodedValueLookup lookup, PMap properties) { - this(lookup.getDecimalEncodedValue(VehicleSpeed.key(properties.getString("name", "foot"))), + public FootAverageSpeedParser(EncodedValueLookup lookup) { + this(lookup.getDecimalEncodedValue(VehicleSpeed.key("foot")), lookup.getDecimalEncodedValue(FerrySpeed.KEY)); } - protected FootAverageSpeedParser(DecimalEncodedValue speedEnc, DecimalEncodedValue ferrySpeedEnc) { + public FootAverageSpeedParser(DecimalEncodedValue speedEnc, DecimalEncodedValue ferrySpeedEnc) { super(speedEnc, ferrySpeedEnc); routeMap.put(INTERNATIONAL, UNCHANGED.getValue()); diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/FootPriorityParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/FootPriorityParser.java index fe7cd54bed2..f722dc6872d 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/FootPriorityParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/FootPriorityParser.java @@ -5,7 +5,6 @@ import com.graphhopper.routing.util.FerrySpeedCalculator; import com.graphhopper.routing.util.PriorityCode; import com.graphhopper.storage.IntsRef; -import com.graphhopper.util.PMap; import java.util.*; @@ -18,15 +17,15 @@ public class FootPriorityParser implements TagParser { final Set intendedValues = new HashSet<>(INTENDED); final Set safeHighwayTags = new HashSet<>(); - final Set avoidHighwayTags = new HashSet<>(); + final Map avoidHighwayTags = new HashMap<>(); protected HashSet sidewalkValues = new HashSet<>(5); protected HashSet sidewalksNoValues = new HashSet<>(5); protected final DecimalEncodedValue priorityWayEncoder; protected EnumEncodedValue footRouteEnc; protected Map routeMap = new HashMap<>(); - public FootPriorityParser(EncodedValueLookup lookup, PMap properties) { - this(lookup.getDecimalEncodedValue(VehiclePriority.key(properties.getString("name", "foot"))), + public FootPriorityParser(EncodedValueLookup lookup) { + this(lookup.getDecimalEncodedValue(VehiclePriority.key("foot")), lookup.getEnumEncodedValue(FootNetwork.KEY, RouteNetwork.class) ); } @@ -55,14 +54,16 @@ protected FootPriorityParser(DecimalEncodedValue priorityEnc, EnumEncodedValue weightToPrioMap = new TreeMap<>(); + TreeMap weightToPrioMap = new TreeMap<>(); if (priorityFromRelation == null) - weightToPrioMap.put(0d, UNCHANGED.getValue()); + weightToPrioMap.put(0d, UNCHANGED); else - weightToPrioMap.put(110d, priorityFromRelation); + weightToPrioMap.put(110d, PriorityCode.valueOf(priorityFromRelation)); collect(way, weightToPrioMap); - // pick priority with biggest order value - return weightToPrioMap.lastEntry().getValue(); + // pick priority with the biggest order value + return weightToPrioMap.lastEntry().getValue().getValue(); } /** * @param weightToPrioMap associate a weight with every priority. This sorted map allows * subclasses to 'insert' more important priorities as well as overwrite determined priorities. */ - void collect(ReaderWay way, TreeMap weightToPrioMap) { + void collect(ReaderWay way, TreeMap weightToPrioMap) { String highway = way.getTag("highway"); if (way.hasTag("foot", "designated")) - weightToPrioMap.put(100d, PREFER.getValue()); + weightToPrioMap.put(100d, PREFER); double maxSpeed = Math.max(getMaxSpeed(way, false), getMaxSpeed(way, true)); if (safeHighwayTags.contains(highway) || (isValidSpeed(maxSpeed) && maxSpeed <= 20)) { - weightToPrioMap.put(40d, PREFER.getValue()); + weightToPrioMap.put(40d, PREFER); if (way.hasTag("tunnel", intendedValues)) { if (way.hasTag("sidewalk", sidewalksNoValues)) - weightToPrioMap.put(40d, AVOID.getValue()); + weightToPrioMap.put(40d, AVOID); else - weightToPrioMap.put(40d, UNCHANGED.getValue()); + weightToPrioMap.put(40d, UNCHANGED); } - } else if ((isValidSpeed(maxSpeed) && maxSpeed > 50) || avoidHighwayTags.contains(highway)) { + } else if ((isValidSpeed(maxSpeed) && maxSpeed > 50) || avoidHighwayTags.containsKey(highway)) { + PriorityCode priorityCode = avoidHighwayTags.get(highway); if (way.hasTag("sidewalk", sidewalksNoValues)) - weightToPrioMap.put(40d, VERY_BAD.getValue()); - else if (!way.hasTag("sidewalk", sidewalkValues)) - weightToPrioMap.put(40d, AVOID.getValue()); + weightToPrioMap.put(40d, priorityCode == null ? BAD : priorityCode); else - weightToPrioMap.put(40d, SLIGHT_AVOID.getValue()); + weightToPrioMap.put(40d, priorityCode == null ? AVOID : priorityCode.better().better()); } else if (way.hasTag("sidewalk", sidewalksNoValues)) - weightToPrioMap.put(40d, AVOID.getValue()); + weightToPrioMap.put(40d, AVOID); if (way.hasTag("bicycle", "official") || way.hasTag("bicycle", "designated")) - weightToPrioMap.put(44d, SLIGHT_AVOID.getValue()); + weightToPrioMap.put(44d, SLIGHT_AVOID); } } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/ModeAccessParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/ModeAccessParser.java new file mode 100644 index 00000000000..72214cacc00 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/ModeAccessParser.java @@ -0,0 +1,85 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; +import com.graphhopper.routing.util.FerrySpeedCalculator; +import com.graphhopper.routing.util.TransportationMode; +import com.graphhopper.storage.IntsRef; + +import java.util.*; + +import static com.graphhopper.routing.util.parsers.OSMTemporalAccessParser.hasTemporalRestriction; + +public class ModeAccessParser implements TagParser { + + private final static Set onewaysForward = new HashSet<>(Arrays.asList("yes", "true", "1")); + private final Set restrictedValues; + private final List restrictionKeys; + private final List vehicleForward; + private final List vehicleBackward; + private final List ignoreOnewayKeys; + private final BooleanEncodedValue accessEnc; + private final BooleanEncodedValue roundaboutEnc; + + public ModeAccessParser(TransportationMode mode, BooleanEncodedValue accessEnc, BooleanEncodedValue roundaboutEnc, List restrictions) { + this.accessEnc = accessEnc; + this.roundaboutEnc = roundaboutEnc; + restrictionKeys = OSMRoadAccessParser.toOSMRestrictions(mode); + vehicleForward = restrictionKeys.stream().map(r -> r + ":forward").toList(); + vehicleBackward = restrictionKeys.stream().map(r -> r + ":backward").toList(); + ignoreOnewayKeys = restrictionKeys.stream().map(r -> "oneway:" + r).toList(); + restrictedValues = new HashSet<>(restrictions.isEmpty() ? Arrays.asList("no", "restricted", "military", "emergency") : restrictions); + if (restrictedValues.contains("")) + throw new IllegalArgumentException("restriction values cannot contain empty string"); + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { + int firstIndex = way.getFirstIndex(restrictionKeys); + String firstValue = firstIndex < 0 ? "" : way.getTag(restrictionKeys.get(firstIndex), ""); + if (restrictedValues.contains(firstValue) && !hasTemporalRestriction(way, firstIndex, restrictionKeys)) + return; + + if (way.hasTag("gh:barrier_edge") && way.hasTag("node_tags")) { + List> nodeTags = way.getTag("node_tags", null); + // a barrier edge has the restriction in both nodes and the tags are the same -> get(0) + firstValue = getFirstPriorityNodeTag(nodeTags.get(0), restrictionKeys); + if (restrictedValues.contains(firstValue)) + return; + } + + if (FerrySpeedCalculator.isFerry(way)) { + accessEnc.setBool(false, edgeId, edgeIntAccess, true); + accessEnc.setBool(true, edgeId, edgeIntAccess, true); + } else { + boolean isRoundabout = roundaboutEnc.getBool(false, edgeId, edgeIntAccess); + boolean ignoreOneway = "no".equals(way.getFirstValue(ignoreOnewayKeys)); + boolean isBwd = isBackwardOneway(way); + if (!ignoreOneway && (isBwd || isRoundabout || isForwardOneway(way))) { + accessEnc.setBool(isBwd, edgeId, edgeIntAccess, true); + } else { + accessEnc.setBool(false, edgeId, edgeIntAccess, true); + accessEnc.setBool(true, edgeId, edgeIntAccess, true); + } + } + } + + private static String getFirstPriorityNodeTag(Map nodeTags, List restrictionKeys) { + for (String key : restrictionKeys) { + String val = (String) nodeTags.get(key); + if (val != null) return val; + } + return ""; + } + + protected boolean isBackwardOneway(ReaderWay way) { + // vehicle:forward=no is like oneway=-1 + return way.hasTag("oneway", "-1") || "no".equals(way.getFirstValue(vehicleForward)); + } + + protected boolean isForwardOneway(ReaderWay way) { + // vehicle:backward=no is like oneway=yes + return way.hasTag("oneway", onewaysForward) || "no".equals(way.getFirstValue(vehicleBackward)); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/MountainBikeAccessParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/MountainBikeAccessParser.java index ffa0672c005..64e855ec308 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/MountainBikeAccessParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/MountainBikeAccessParser.java @@ -9,7 +9,7 @@ public class MountainBikeAccessParser extends BikeCommonAccessParser { public MountainBikeAccessParser(EncodedValueLookup lookup, PMap properties) { - this(lookup.getBooleanEncodedValue(VehicleAccess.key(properties.getString("name", "mtb"))), + this(lookup.getBooleanEncodedValue(VehicleAccess.key("mtb")), lookup.getBooleanEncodedValue(Roundabout.KEY)); blockPrivate(properties.getBool("block_private", true)); blockFords(properties.getBool("block_fords", false)); diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/MountainBikeAverageSpeedParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/MountainBikeAverageSpeedParser.java index 4e060a729da..0c73a84b4c1 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/MountainBikeAverageSpeedParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/MountainBikeAverageSpeedParser.java @@ -1,12 +1,11 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.routing.ev.*; -import com.graphhopper.util.PMap; public class MountainBikeAverageSpeedParser extends BikeCommonAverageSpeedParser { - public MountainBikeAverageSpeedParser(EncodedValueLookup lookup, PMap properties) { - this(lookup.getDecimalEncodedValue(VehicleSpeed.key(properties.getString("name", "mtb"))), + public MountainBikeAverageSpeedParser(EncodedValueLookup lookup) { + this(lookup.getDecimalEncodedValue(VehicleSpeed.key("mtb")), lookup.getEnumEncodedValue(Smoothness.KEY, Smoothness.class), lookup.getDecimalEncodedValue(FerrySpeed.KEY)); } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/MountainBikePriorityParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/MountainBikePriorityParser.java index d9687113480..f124c7d4d3c 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/MountainBikePriorityParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/MountainBikePriorityParser.java @@ -3,7 +3,6 @@ import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.PriorityCode; -import com.graphhopper.util.PMap; import java.util.TreeMap; @@ -12,9 +11,9 @@ public class MountainBikePriorityParser extends BikeCommonPriorityParser { - public MountainBikePriorityParser(EncodedValueLookup lookup, PMap properties) { - this(lookup.getDecimalEncodedValue(VehicleSpeed.key(properties.getString("name", "mtb"))), - lookup.getDecimalEncodedValue(VehiclePriority.key(properties.getString("name", "mtb"))), + public MountainBikePriorityParser(EncodedValueLookup lookup) { + this(lookup.getDecimalEncodedValue(VehicleSpeed.key("mtb")), + lookup.getDecimalEncodedValue(VehiclePriority.key("mtb")), lookup.getEnumEncodedValue(BikeNetwork.KEY, RouteNetwork.class)); } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMBikeNetworkTagParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMBikeNetworkTagParser.java index 0788d464004..dd9aebe1051 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMBikeNetworkTagParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMBikeNetworkTagParser.java @@ -41,16 +41,7 @@ public void handleRelationTags(IntsRef relFlags, ReaderRelation relation) { RouteNetwork oldBikeNetwork = transformerRouteRelEnc.getEnum(false, -1, relIntAccess); if (relation.hasTag("route", "bicycle")) { String tag = Helper.toLowerCase(relation.getTag("network", "")); - RouteNetwork newBikeNetwork = RouteNetwork.LOCAL; - if ("lcn".equals(tag)) { - newBikeNetwork = RouteNetwork.LOCAL; - } else if ("rcn".equals(tag)) { - newBikeNetwork = RouteNetwork.REGIONAL; - } else if ("ncn".equals(tag)) { - newBikeNetwork = RouteNetwork.NATIONAL; - } else if ("icn".equals(tag)) { - newBikeNetwork = RouteNetwork.INTERNATIONAL; - } + RouteNetwork newBikeNetwork = BikeNetworkParserHelper.determine(tag); if (oldBikeNetwork == RouteNetwork.MISSING || oldBikeNetwork.ordinal() > newBikeNetwork.ordinal()) transformerRouteRelEnc.setEnum(false, -1, relIntAccess, newBikeNetwork); } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMtbNetworkTagParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMtbNetworkTagParser.java new file mode 100644 index 00000000000..2de8d4bdc6d --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMtbNetworkTagParser.java @@ -0,0 +1,61 @@ +/* + * 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.routing.util.parsers; + +import com.graphhopper.reader.ReaderRelation; +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.*; +import com.graphhopper.storage.IntsRef; +import com.graphhopper.util.Helper; + +import static com.graphhopper.routing.util.EncodingManager.getKey; + +public class OSMMtbNetworkTagParser implements RelationTagParser { + private final EnumEncodedValue bikeRouteEnc; + // used only for internal transformation from relations into edge flags + private final EnumEncodedValue transformerRouteRelEnc = new EnumEncodedValue<>(getKey("mtb", "route_relation"), RouteNetwork.class); + + public OSMMtbNetworkTagParser(EnumEncodedValue bikeRouteEnc, EncodedValue.InitializerConfig relConfig) { + this.bikeRouteEnc = bikeRouteEnc; + this.transformerRouteRelEnc.init(relConfig); + } + + @Override + public void handleRelationTags(IntsRef relFlags, ReaderRelation relation) { + IntsRefEdgeIntAccess relIntAccess = new IntsRefEdgeIntAccess(relFlags); + RouteNetwork oldBikeNetwork = transformerRouteRelEnc.getEnum(false, -1, relIntAccess); + if (relation.hasTag("route", "mtb")) { + String tag = Helper.toLowerCase(relation.getTag("network", "")); + RouteNetwork newBikeNetwork = BikeNetworkParserHelper.determine(tag); + if (oldBikeNetwork == RouteNetwork.MISSING || oldBikeNetwork.ordinal() > newBikeNetwork.ordinal()) + transformerRouteRelEnc.setEnum(false, -1, relIntAccess, newBikeNetwork); + } + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { + // just copy value into different bit range + IntsRefEdgeIntAccess relIntAccess = new IntsRefEdgeIntAccess(relationFlags); + RouteNetwork routeNetwork = transformerRouteRelEnc.getEnum(false, -1, relIntAccess); + bikeRouteEnc.setEnum(false, edgeId, edgeIntAccess, routeNetwork); + } + + public EnumEncodedValue getTransformerRouteRelEnc() { + return transformerRouteRelEnc; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMRoadAccessParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMRoadAccessParser.java index cb61d77f009..c9b54cb59e6 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMRoadAccessParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMRoadAccessParser.java @@ -99,6 +99,8 @@ public static List toOSMRestrictions(TransportationMode mode) { return Arrays.asList("access"); case BUS: return Arrays.asList("bus", "psv", "motor_vehicle", "vehicle", "access"); + case HOV: + return Arrays.asList("hov", "motor_vehicle", "vehicle", "access"); default: throw new IllegalArgumentException("Cannot convert TransportationMode " + mode + " to list of restrictions"); } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMTemporalAccessParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMTemporalAccessParser.java new file mode 100644 index 00000000000..4330f8edf5e --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMTemporalAccessParser.java @@ -0,0 +1,125 @@ +/* + * 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.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.reader.osm.conditional.ConditionalValueParser; +import com.graphhopper.reader.osm.conditional.DateRangeParser; +import com.graphhopper.routing.ev.EdgeIntAccess; +import com.graphhopper.storage.IntsRef; +import com.graphhopper.util.Helper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.text.ParseException; +import java.util.*; + +/** + * This parser fills the different XYTemporalAccess enums from the OSM conditional + * restrictions based on the specified dateRangeParserDate. 'Temporal' means that both, temporary + * and seasonal restrictions will be considered. Node tags will be ignored for now. + */ +public class OSMTemporalAccessParser implements TagParser { + + private static final Logger logger = LoggerFactory.getLogger(OSMTemporalAccessParser.class); + private final Collection conditionals; + private final Setter restrictionSetter; + private final DateRangeParser parser; + + @FunctionalInterface + public interface Setter { + void setBoolean(int edgeId, EdgeIntAccess edgeIntAccess, boolean b); + } + + public OSMTemporalAccessParser(Collection conditionals, Setter restrictionSetter, String dateRangeParserDate) { + this.conditionals = conditionals; + this.restrictionSetter = restrictionSetter; + if (dateRangeParserDate.isEmpty()) + dateRangeParserDate = Helper.createFormatter("yyyy-MM-dd").format(new Date().getTime()); + + this.parser = DateRangeParser.createInstance(dateRangeParserDate); + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { + // TODO for now the node tag overhead is not worth the effort due to very few data points + // List> nodeTags = way.getTag("node_tags", null); + + Boolean b = getConditional(way.getTags()); + if (b != null) + restrictionSetter.setBoolean(edgeId, edgeIntAccess, b); + } + + Boolean getConditional(Map tags) { + for (Map.Entry entry : tags.entrySet()) { + if (!conditionals.contains(entry.getKey())) continue; + + String value = (String) entry.getValue(); + String[] strs = value.split("@"); + if (strs.length == 2) { + Boolean inRange = isInRange(parser, strs[1].trim()); + if (inRange != null) { + if (strs[0].trim().equals("no")) return !inRange; + if (strs[0].trim().equals("yes")) return inRange; + } + } + } + return null; + } + + private static Boolean isInRange(final DateRangeParser parser, final String value) { + if (value.isEmpty()) + return null; + + if (value.contains(";")) + return null; + + String conditionalValue = value.replace('(', ' ').replace(')', ' ').trim(); + try { + ConditionalValueParser.ConditionState res = parser.checkCondition(conditionalValue); + if (res.isValid()) + return res.isCheckPassed(); + } catch (ParseException ex) { + } + return null; + } + + // We do not care about the date. We just want to know if ConditionState is valid and the value before @ is accepted + private static final DateRangeParser GENERIC_PARSER = DateRangeParser.createInstance("1970-01-01"); + private static final Set GENERIC_ACCEPTED_VALUES = Set.of("yes", "no"); + + public static boolean hasTemporalRestriction(ReaderWay way, int firstIndex, List restrictions) { + for (int i = firstIndex; i >= 0; i--) { + String value = way.getTag(restrictions.get(i) + ":conditional"); + if (OSMTemporalAccessParser.hasTemporalRestriction(value)) return true; + } + return false; + } + + private static boolean hasTemporalRestriction(String value) { + if (value == null) return false; + String[] strs = value.split("@"); + if (strs.length == 2) { + Boolean inRange = isInRange(GENERIC_PARSER, strs[1].trim()); + if (inRange != null && GENERIC_ACCEPTED_VALUES.contains(strs[0].trim())) + return true; + } + return false; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/RacingBikeAccessParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/RacingBikeAccessParser.java index 5843c167e9d..6d0c34325b3 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/RacingBikeAccessParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/RacingBikeAccessParser.java @@ -9,7 +9,7 @@ public class RacingBikeAccessParser extends BikeCommonAccessParser { public RacingBikeAccessParser(EncodedValueLookup lookup, PMap properties) { - this(lookup.getBooleanEncodedValue(VehicleAccess.key(properties.getString("name", "racingbike"))), + this(lookup.getBooleanEncodedValue(VehicleAccess.key("racingbike")), lookup.getBooleanEncodedValue(Roundabout.KEY)); blockPrivate(properties.getBool("block_private", true)); blockFords(properties.getBool("block_fords", false)); diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/RacingBikeAverageSpeedParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/RacingBikeAverageSpeedParser.java index 8ac660b6027..dabc0f82aba 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/RacingBikeAverageSpeedParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/RacingBikeAverageSpeedParser.java @@ -1,12 +1,11 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.routing.ev.*; -import com.graphhopper.util.PMap; public class RacingBikeAverageSpeedParser extends BikeCommonAverageSpeedParser { - public RacingBikeAverageSpeedParser(EncodedValueLookup lookup, PMap properties) { - this(lookup.getDecimalEncodedValue(VehicleSpeed.key(properties.getString("name", "racingbike"))), + public RacingBikeAverageSpeedParser(EncodedValueLookup lookup) { + this(lookup.getDecimalEncodedValue(VehicleSpeed.key("racingbike")), lookup.getEnumEncodedValue(Smoothness.KEY, Smoothness.class), lookup.getDecimalEncodedValue(FerrySpeed.KEY)); } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/RacingBikePriorityParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/RacingBikePriorityParser.java index 015bb712df4..9ead7ff7ea3 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/RacingBikePriorityParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/RacingBikePriorityParser.java @@ -3,7 +3,6 @@ import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.PriorityCode; -import com.graphhopper.util.PMap; import java.util.TreeMap; @@ -12,9 +11,9 @@ public class RacingBikePriorityParser extends BikeCommonPriorityParser { - public RacingBikePriorityParser(EncodedValueLookup lookup, PMap properties) { - this(lookup.getDecimalEncodedValue(VehiclePriority.key(properties.getString("name", "racingbike"))), - lookup.getDecimalEncodedValue(VehicleSpeed.key(properties.getString("name", "racingbike"))), + public RacingBikePriorityParser(EncodedValueLookup lookup) { + this(lookup.getDecimalEncodedValue(VehiclePriority.key("racingbike")), + lookup.getDecimalEncodedValue(VehicleSpeed.key("racingbike")), lookup.getEnumEncodedValue(BikeNetwork.KEY, RouteNetwork.class)); } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/RestrictionSetter.java b/core/src/main/java/com/graphhopper/routing/util/parsers/RestrictionSetter.java index 37b00dda727..17fffd77e99 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/RestrictionSetter.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/RestrictionSetter.java @@ -86,7 +86,7 @@ private void addArtificialEdges(List> re } private void addViaWayRestrictions(List> restrictions, BooleanEncodedValue turnRestrictionEnc) { - IntSet viaEdgesUsedByOnlyRestrictions = new IntHashSet(); + IntSet directedViaEdgesUsedByRestrictions = new IntHashSet(); for (Pair p : restrictions) { if (!p.first.isViaWayRestriction()) continue; if (ignoreViaWayRestriction(p)) continue; @@ -96,8 +96,19 @@ private void addViaWayRestrictions(List> final int artificialVia = artificialEdgesByEdges.getOrDefault(viaEdge, viaEdge); if (artificialVia == viaEdge) throw new IllegalArgumentException("There should be an artificial edge for every via edge of a way restriction"); - if (p.second == ONLY && !viaEdgesUsedByOnlyRestrictions.add(viaEdge)) - throw new IllegalStateException("We cannot deal with 'only' via-way restrictions that use the same via edges"); + + // With a single artificial edge per original edge there can only be one restriction + // that uses this edge as via-member **per direction**, see #2907. + // We can use the two via node ids to determine the via-edge direction in the restriction: + if (p.first.getViaEdges().size() != 1) + throw new IllegalStateException("At this point we assumed we were dealing with single via-way restrictions only"); + if (viaEdge == 0 && directedViaEdgesUsedByRestrictions.contains(viaEdge)) + // This approach does not work for edge 0... + throw new IllegalStateException("We cannot deal with multiple via-way restrictions if the via-edge is edge 0"); + final int directedViaEdge = viaEdge * (p.first.getViaNodes().get(0) < p.first.getViaNodes().get(1) ? +1 : -1); + if (!directedViaEdgesUsedByRestrictions.add(directedViaEdge)) + throw new IllegalStateException("We cannot deal with multiple via-way restrictions that use the same via edge in the same direction"); + final int artificialFrom = artificialEdgesByEdges.getOrDefault(fromEdge, fromEdge); final int artificialTo = artificialEdgesByEdges.getOrDefault(toEdge, toEdge); final int fromToViaNode = p.first.getViaNodes().get(0); diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/RoadsAccessParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/RoadsAccessParser.java deleted file mode 100644 index b73176e9472..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/RoadsAccessParser.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.graphhopper.routing.util.parsers; - -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.EdgeIntAccess; -import com.graphhopper.routing.ev.EncodedValueLookup; -import com.graphhopper.routing.ev.VehicleAccess; -import com.graphhopper.storage.IntsRef; -import com.graphhopper.util.PMap; - -/** - * Access parser (boolean) for the 'roads' vehicle. Not to be confused with OSMRoadAccessParser that fills road_access - * enum (for car). - */ -public class RoadsAccessParser implements TagParser { - private final BooleanEncodedValue accessEnc; - - public RoadsAccessParser(EncodedValueLookup lookup, PMap properties) { - this(lookup.getBooleanEncodedValue(VehicleAccess.key(properties.getString("name", "roads")))); - } - - public RoadsAccessParser(BooleanEncodedValue accessEnc) { - this.accessEnc = accessEnc; - } - - @Override - public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { - accessEnc.setBool(true, edgeId, edgeIntAccess, true); - accessEnc.setBool(false, edgeId, edgeIntAccess, true); - } - - @Override - public String toString() { - return accessEnc.getName(); - } -} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/RoadsAverageSpeedParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/RoadsAverageSpeedParser.java deleted file mode 100644 index 9756c73f772..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/RoadsAverageSpeedParser.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.graphhopper.routing.util.parsers; - -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.EncodedValueLookup; -import com.graphhopper.routing.ev.EdgeIntAccess; -import com.graphhopper.routing.ev.VehicleSpeed; -import com.graphhopper.storage.IntsRef; -import com.graphhopper.util.PMap; - -public class RoadsAverageSpeedParser implements TagParser { - private final DecimalEncodedValue avgSpeedEnc; - private final double maxPossibleSpeed; - - public RoadsAverageSpeedParser(EncodedValueLookup lookup, PMap properties) { - this(lookup.getDecimalEncodedValue(VehicleSpeed.key(properties.getString("name", "roads")))); - } - - public RoadsAverageSpeedParser(DecimalEncodedValue avgSpeedEnc) { - this.avgSpeedEnc = avgSpeedEnc; - this.maxPossibleSpeed = this.avgSpeedEnc.getMaxStorableDecimal(); - } - - @Override - public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { - // let's make it high and let it be reduced in the custom model - avgSpeedEnc.setDecimal(false, edgeId, edgeIntAccess, maxPossibleSpeed); - if (avgSpeedEnc.isStoreTwoDirections()) - avgSpeedEnc.setDecimal(true, edgeId, edgeIntAccess, maxPossibleSpeed); - } -} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/TagParserFactory.java b/core/src/main/java/com/graphhopper/routing/util/parsers/TagParserFactory.java deleted file mode 100644 index 2a0a676cd3c..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/TagParserFactory.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.graphhopper.routing.util.parsers; - -import com.graphhopper.routing.ev.EncodedValueLookup; -import com.graphhopper.util.PMap; - -public interface TagParserFactory { - TagParser create(EncodedValueLookup lookup, String name, PMap properties); -} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/WheelchairAccessParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/WheelchairAccessParser.java deleted file mode 100644 index a0836292904..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/WheelchairAccessParser.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.graphhopper.routing.util.parsers; - -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.EncodedValueLookup; -import com.graphhopper.routing.ev.EdgeIntAccess; -import com.graphhopper.routing.ev.VehicleAccess; -import com.graphhopper.routing.util.WayAccess; -import com.graphhopper.util.PMap; - -import java.util.HashSet; -import java.util.Set; - -public class WheelchairAccessParser extends FootAccessParser { - private final Set excludeSurfaces = new HashSet<>(); - private final Set excludeSmoothness = new HashSet<>(); - private final int maxInclinePercent = 6; - - public WheelchairAccessParser(EncodedValueLookup lookup, PMap properties) { - this(lookup.getBooleanEncodedValue(properties.getString("name", VehicleAccess.key("wheelchair")))); - blockPrivate(properties.getBool("block_private", true)); - blockFords(properties.getBool("block_fords", false)); - } - - protected WheelchairAccessParser(BooleanEncodedValue accessEnc) { - super(accessEnc); - - restrictions.add("wheelchair"); - - barriers.add("handrail"); - barriers.add("wall"); - barriers.add("turnstile"); - barriers.add("kissing_gate"); - barriers.add("stile"); - - allowedHighwayTags.remove("steps"); - allowedHighwayTags.remove("track"); - - excludeSurfaces.add("cobblestone"); - excludeSurfaces.add("gravel"); - excludeSurfaces.add("sand"); - - excludeSmoothness.add("bad"); - excludeSmoothness.add("very_bad"); - excludeSmoothness.add("horrible"); - excludeSmoothness.add("very_horrible"); - excludeSmoothness.add("impassable"); - - allowedSacScale.clear(); - } - - /** - * Avoid some more ways than for pedestrian like hiking trails. - */ - @Override - public WayAccess getAccess(ReaderWay way) { - if (way.hasTag("surface", excludeSurfaces)) { - if (!way.hasTag("sidewalk", sidewalkValues)) { - return WayAccess.CAN_SKIP; - } else { - String sidewalk = way.getTag("sidewalk"); - if (way.hasTag("sidewalk:" + sidewalk + ":surface", excludeSurfaces)) { - return WayAccess.CAN_SKIP; - } - } - } - - if (way.hasTag("smoothness", excludeSmoothness)) { - if (!way.hasTag("sidewalk", sidewalkValues)) { - return WayAccess.CAN_SKIP; - } else { - String sidewalk = way.getTag("sidewalk"); - if (way.hasTag("sidewalk:" + sidewalk + ":smoothness", excludeSmoothness)) { - return WayAccess.CAN_SKIP; - } - } - } - - if (way.hasTag("incline")) { - String tagValue = way.getTag("incline"); - if (tagValue.endsWith("%") || tagValue.endsWith("°")) { - try { - double incline = Double.parseDouble(tagValue.substring(0, tagValue.length() - 1)); - if (tagValue.endsWith("°")) { - incline = Math.tan(incline * Math.PI / 180) * 100; - } - - if (-maxInclinePercent > incline || incline > maxInclinePercent) { - return WayAccess.CAN_SKIP; - } - } catch (NumberFormatException ex) { - } - } - } - - if (way.hasTag("kerb", "raised")) - return WayAccess.CAN_SKIP; - - if (way.hasTag("kerb")) { - String tagValue = way.getTag("kerb"); - if (tagValue.endsWith("cm") || tagValue.endsWith("mm")) { - try { - float kerbHeight = Float.parseFloat(tagValue.substring(0, tagValue.length() - 2)); - if (tagValue.endsWith("mm")) { - kerbHeight /= 100; - } - - int maxKerbHeightCm = 3; - if (kerbHeight > maxKerbHeightCm) { - return WayAccess.CAN_SKIP; - } - } catch (NumberFormatException ex) { - } - } - } - - return super.getAccess(way); - } - - @Override - public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way) { - WayAccess access = getAccess(way); - if (access.canSkip()) - return; - - accessEnc.setBool(false, edgeId, edgeIntAccess, true); - accessEnc.setBool(true, edgeId, edgeIntAccess, true); - } -} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/WheelchairAverageSpeedParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/WheelchairAverageSpeedParser.java deleted file mode 100644 index 702fb54f790..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/WheelchairAverageSpeedParser.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.graphhopper.routing.util.parsers; - -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.*; -import com.graphhopper.routing.util.FerrySpeedCalculator; -import com.graphhopper.util.PMap; -import com.graphhopper.util.PointList; - -public class WheelchairAverageSpeedParser extends FootAverageSpeedParser { - - public WheelchairAverageSpeedParser(EncodedValueLookup lookup, PMap properties) { - this(lookup.getDecimalEncodedValue(properties.getString("name", VehicleSpeed.key("wheelchair"))), - lookup.getDecimalEncodedValue(FerrySpeed.KEY)); - } - - protected WheelchairAverageSpeedParser(DecimalEncodedValue speedEnc, DecimalEncodedValue ferrySpeedEnc) { - super(speedEnc, ferrySpeedEnc); - } - - @Override - public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way) { - String highwayValue = way.getTag("highway"); - if (highwayValue == null) { - if (FerrySpeedCalculator.isFerry(way)) { - double ferrySpeed = FerrySpeedCalculator.minmax(ferrySpeedEnc.getDecimal(false, edgeId, edgeIntAccess), avgSpeedEnc); - setSpeed(false, edgeId, edgeIntAccess, ferrySpeed); - setSpeed(true, edgeId, edgeIntAccess, ferrySpeed); - } - if (!way.hasTag("railway", "platform") && !way.hasTag("man_made", "pier")) - return; - } - - setSpeed(false, edgeId, edgeIntAccess, MEAN_SPEED); - setSpeed(true, edgeId, edgeIntAccess, MEAN_SPEED); - applyWayTags(way, edgeId, edgeIntAccess); - } - - /** - * Calculate slopes from elevation data and set speed according to that. In-/declines between smallInclinePercent - * and maxInclinePercent will reduce speed to SLOW_SPEED. In-/declines above maxInclinePercent will result in zero - * speed. - */ - public void applyWayTags(ReaderWay way, int edgeId, EdgeIntAccess edgeIntAccess) { - PointList pl = way.getTag("point_list", null); - if (pl == null) - throw new IllegalArgumentException("The artificial point_list tag is missing"); - if (!way.hasTag("edge_distance")) - throw new IllegalArgumentException("The artificial edge_distance tag is missing"); - double fullDist2D = way.getTag("edge_distance", 0d); - if (Double.isInfinite(fullDist2D)) - throw new IllegalStateException("Infinite distance should not happen due to #435. way ID=" + way.getId()); - - // skip elevation data adjustment for too short segments, TODO use custom model for elevation handling - if (fullDist2D < 20 || !pl.is3D()) - return; - - double prevEle = pl.getEle(0); - double eleDelta = pl.getEle(pl.size() - 1) - prevEle; - double elePercent = eleDelta / fullDist2D * 100; - int smallInclinePercent = 3; - double fwdSpeed = 0, bwdSpeed = 0; - final int maxInclinePercent = 6; - if (elePercent > smallInclinePercent && elePercent < maxInclinePercent) { - fwdSpeed = SLOW_SPEED; - bwdSpeed = MEAN_SPEED; - } else if (elePercent < -smallInclinePercent && elePercent > -maxInclinePercent) { - fwdSpeed = MEAN_SPEED; - bwdSpeed = SLOW_SPEED; - } else if (elePercent > maxInclinePercent || elePercent < -maxInclinePercent) { - // it can be problematic to exclude roads due to potential bad elevation data (e.g.delta for narrow nodes could be too high) - // so exclude only when we are certain - if (fullDist2D > 50) { - avgSpeedEnc.setDecimal(false, edgeId, edgeIntAccess, 0); - avgSpeedEnc.setDecimal(true, edgeId, edgeIntAccess, 0); - return; - } - - fwdSpeed = SLOW_SPEED; - bwdSpeed = SLOW_SPEED; - } - - if (fwdSpeed > 0) setSpeed(false, edgeId, edgeIntAccess, fwdSpeed); - if (bwdSpeed > 0) setSpeed(true, edgeId, edgeIntAccess, bwdSpeed); - } -} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/WheelchairPriorityParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/WheelchairPriorityParser.java deleted file mode 100644 index a632b91fc2d..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/WheelchairPriorityParser.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.graphhopper.routing.util.parsers; - -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.*; -import com.graphhopper.routing.util.PriorityCode; -import com.graphhopper.storage.IntsRef; -import com.graphhopper.util.PMap; - -import java.util.TreeMap; - -import static com.graphhopper.routing.util.PriorityCode.AVOID; -import static com.graphhopper.routing.util.PriorityCode.VERY_NICE; - -public class WheelchairPriorityParser extends FootPriorityParser { - - public WheelchairPriorityParser(EncodedValueLookup lookup, PMap properties) { - this(lookup.getDecimalEncodedValue(properties.getString("name", VehiclePriority.key("wheelchair"))), - lookup.getEnumEncodedValue(FootNetwork.KEY, RouteNetwork.class)); - } - - protected WheelchairPriorityParser(DecimalEncodedValue priorityEnc, EnumEncodedValue footRouteEnc) { - super(priorityEnc, footRouteEnc); - - safeHighwayTags.add("footway"); - safeHighwayTags.add("pedestrian"); - safeHighwayTags.add("living_street"); - safeHighwayTags.add("residential"); - safeHighwayTags.add("service"); - safeHighwayTags.add("platform"); - - safeHighwayTags.remove("steps"); - safeHighwayTags.remove("track"); - } - - @Override - public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { - Integer priorityFromRelation = routeMap.get(footRouteEnc.getEnum(false, edgeId, edgeIntAccess)); - priorityWayEncoder.setDecimal(false, edgeId, edgeIntAccess, PriorityCode.getValue(handlePriority(way, priorityFromRelation))); - } - - /** - * First get priority from {@link FootPriorityParser#handlePriority(ReaderWay, Integer)} then evaluate wheelchair specific - * tags. - * - * @return a priority for the given way - */ - @Override - public int handlePriority(ReaderWay way, Integer priorityFromRelation) { - TreeMap weightToPrioMap = new TreeMap<>(); - - weightToPrioMap.put(100d, super.handlePriority(way, priorityFromRelation)); - - if (way.hasTag("wheelchair", "designated")) { - weightToPrioMap.put(102d, VERY_NICE.getValue()); - } else if (way.hasTag("wheelchair", "limited")) { - weightToPrioMap.put(102d, AVOID.getValue()); - } - - return weightToPrioMap.lastEntry().getValue(); - } -} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/helpers/OSMValueExtractor.java b/core/src/main/java/com/graphhopper/routing/util/parsers/helpers/OSMValueExtractor.java index 04b8eaf9f97..22d6a4d3996 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/helpers/OSMValueExtractor.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/helpers/OSMValueExtractor.java @@ -30,7 +30,7 @@ private OSMValueExtractor() { } public static void extractTons(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, DecimalEncodedValue valueEncoder, List keys) { - final String rawValue = way.getFirstPriorityTag(keys); + final String rawValue = way.getFirstValue(keys); double value = stringToTons(rawValue); if (Double.isNaN(value)) value = Double.POSITIVE_INFINITY; @@ -93,7 +93,7 @@ public static double stringToTons(String value) { } public static void extractMeter(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, DecimalEncodedValue valueEncoder, List keys) { - final String rawValue = way.getFirstPriorityTag(keys); + final String rawValue = way.getFirstValue(keys); double value = stringToMeter(rawValue); if (Double.isNaN(value)) value = Double.POSITIVE_INFINITY; diff --git a/core/src/main/java/com/graphhopper/routing/weighting/AbstractAdjustedWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/AbstractAdjustedWeighting.java index 9a87702f10f..c46cf30c0d9 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/AbstractAdjustedWeighting.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/AbstractAdjustedWeighting.java @@ -34,8 +34,8 @@ public AbstractAdjustedWeighting(Weighting superWeighting) { } @Override - public double getMinWeight(double distance) { - return superWeighting.getMinWeight(distance); + public double calcMinWeightPerDistance() { + return superWeighting.calcMinWeightPerDistance(); } @Override diff --git a/core/src/main/java/com/graphhopper/routing/weighting/AbstractWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/AbstractWeighting.java index d62b9fd03bb..4c7eade1dbd 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/AbstractWeighting.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/AbstractWeighting.java @@ -33,7 +33,7 @@ public abstract class AbstractWeighting implements Weighting { private final TurnCostProvider turnCostProvider; protected AbstractWeighting(BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc, TurnCostProvider turnCostProvider) { - if (!isValidName(getName())) + if (!Weighting.isValidName(getName())) throw new IllegalStateException("Not a valid name for a Weighting: " + getName()); this.accessEnc = accessEnc; this.speedEnc = speedEnc; @@ -72,17 +72,6 @@ public boolean hasTurnCosts() { return turnCostProvider != NO_TURN_COST_PROVIDER; } - public TurnCostProvider getTurnCostProvider() { - return turnCostProvider; - } - - static boolean isValidName(String name) { - if (name == null || name.isEmpty()) - return false; - - return name.matches("[\\|_a-z]+"); - } - @Override public String toString() { return getName() + "|" + speedEnc.getName(); diff --git a/core/src/main/java/com/graphhopper/routing/weighting/BeelineWeightApproximator.java b/core/src/main/java/com/graphhopper/routing/weighting/BeelineWeightApproximator.java index 2ef330c30ac..289c4c7ed0c 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/BeelineWeightApproximator.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/BeelineWeightApproximator.java @@ -30,6 +30,7 @@ public class BeelineWeightApproximator implements WeightApproximator { private final NodeAccess nodeAccess; private final Weighting weighting; + private final double minWeightPerDistance; private DistanceCalc distanceCalc = DistanceCalcEarth.DIST_EARTH; private double toLat, toLon; private double epsilon = 1; @@ -37,6 +38,7 @@ public class BeelineWeightApproximator implements WeightApproximator { public BeelineWeightApproximator(NodeAccess nodeAccess, Weighting weighting) { this.nodeAccess = nodeAccess; this.weighting = weighting; + this.minWeightPerDistance = weighting.calcMinWeightPerDistance(); } @Override @@ -65,7 +67,7 @@ public double approximate(int fromNode) { double fromLat = nodeAccess.getLat(fromNode); double fromLon = nodeAccess.getLon(fromNode); double dist2goal = distanceCalc.calcDist(toLat, toLon, fromLat, fromLon); - double weight2goal = weighting.getMinWeight(dist2goal); + double weight2goal = minWeightPerDistance * dist2goal; return weight2goal * epsilon; } diff --git a/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java b/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java index 299dd04a658..bd9b7515277 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java @@ -18,11 +18,12 @@ package com.graphhopper.routing.weighting; +import com.graphhopper.config.TurnCostsConfig; import com.graphhopper.routing.ev.BooleanEncodedValue; import com.graphhopper.storage.TurnCostStorage; import com.graphhopper.util.EdgeIterator; -import static com.graphhopper.routing.weighting.Weighting.INFINITE_U_TURN_COSTS; +import static com.graphhopper.config.TurnCostsConfig.INFINITE_U_TURN_COSTS; public class DefaultTurnCostProvider implements TurnCostProvider { private final BooleanEncodedValue turnRestrictionEnc; @@ -31,11 +32,11 @@ public class DefaultTurnCostProvider implements TurnCostProvider { private final double uTurnCosts; public DefaultTurnCostProvider(BooleanEncodedValue turnRestrictionEnc, TurnCostStorage turnCostStorage) { - this(turnRestrictionEnc, turnCostStorage, Weighting.INFINITE_U_TURN_COSTS); + this(turnRestrictionEnc, turnCostStorage, TurnCostsConfig.INFINITE_U_TURN_COSTS); } /** - * @param uTurnCosts the costs of a u-turn in seconds, for {@link Weighting#INFINITE_U_TURN_COSTS} the u-turn costs + * @param uTurnCosts the costs of a u-turn in seconds, for {@link TurnCostsConfig#INFINITE_U_TURN_COSTS} the u-turn costs * will be infinite */ public DefaultTurnCostProvider(BooleanEncodedValue turnRestrictionEnc, TurnCostStorage turnCostStorage, int uTurnCosts) { diff --git a/core/src/main/java/com/graphhopper/routing/weighting/FastestWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/FastestWeighting.java deleted file mode 100644 index 3990ae51c87..00000000000 --- a/core/src/main/java/com/graphhopper/routing/weighting/FastestWeighting.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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.routing.weighting; - -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.EnumEncodedValue; -import com.graphhopper.routing.ev.RoadAccess; -import com.graphhopper.util.EdgeIteratorState; -import com.graphhopper.util.PMap; -import com.graphhopper.util.Parameters.Routing; - -import static com.graphhopper.routing.weighting.TurnCostProvider.NO_TURN_COST_PROVIDER; - -/** - * Calculates the fastest route with the specified vehicle (VehicleEncoder). Calculates the weight - * in seconds. - *

- * - * @author Peter Karich - */ -public class FastestWeighting extends AbstractWeighting { - public static String DESTINATION_FACTOR = "road_access_destination_factor"; - public static String PRIVATE_FACTOR = "road_access_private_factor"; - /** - * Converting to seconds is not necessary but makes adding other penalties easier (e.g. turn - * costs or traffic light costs etc) - */ - protected final static double SPEED_CONV = 3.6; - private final double headingPenalty; - private final double maxSpeed; - private final EnumEncodedValue roadAccessEnc; - // this factor puts a penalty on roads with a "destination"-only or private access, see #733 and #1936 - private final double destinationPenalty, privatePenalty; - - public FastestWeighting(BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { - this(accessEnc, speedEnc, NO_TURN_COST_PROVIDER); - } - - public FastestWeighting(BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc, TurnCostProvider turnCostProvider) { - this(accessEnc, speedEnc, null, new PMap(0), turnCostProvider); - } - - public FastestWeighting(BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc, EnumEncodedValue roadAccessEnc, PMap map, TurnCostProvider turnCostProvider) { - super(accessEnc, speedEnc, turnCostProvider); - headingPenalty = map.getDouble(Routing.HEADING_PENALTY, Routing.DEFAULT_HEADING_PENALTY); - maxSpeed = speedEnc.getMaxOrMaxStorableDecimal() / SPEED_CONV; - - destinationPenalty = map.getDouble(DESTINATION_FACTOR, 1); - privatePenalty = map.getDouble(PRIVATE_FACTOR, 1); - // ensure that we do not need to change getMinWeight, i.e. both factors need to be >= 1 - checkBounds(DESTINATION_FACTOR, destinationPenalty, 1, 10); - checkBounds(PRIVATE_FACTOR, privatePenalty, 1, 10); - if (destinationPenalty > 1 || privatePenalty > 1) { - if (roadAccessEnc == null) - throw new IllegalArgumentException("road_access must not be null when destination or private penalties are > 1"); - this.roadAccessEnc = roadAccessEnc; - } else - this.roadAccessEnc = null; - } - - @Override - public double getMinWeight(double distance) { - return distance / maxSpeed; - } - - @Override - public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) { - if (reverse ? !edgeState.getReverse(accessEnc) : !edgeState.get(accessEnc)) - return Double.POSITIVE_INFINITY; - double speed = reverse ? edgeState.getReverse(speedEnc) : edgeState.get(speedEnc); - if (speed == 0) - return Double.POSITIVE_INFINITY; - - double time = edgeState.getDistance() / speed * SPEED_CONV; - if (roadAccessEnc != null) { - RoadAccess access = edgeState.get(roadAccessEnc); - if (access == RoadAccess.DESTINATION) - time *= destinationPenalty; - else if (access == RoadAccess.PRIVATE) - time *= privatePenalty; - } - // add direction penalties at start/stop/via points - boolean unfavoredEdge = edgeState.get(EdgeIteratorState.UNFAVORED_EDGE); - if (unfavoredEdge) - time += headingPenalty; - - return time; - } - - static double checkBounds(String key, double val, double from, double to) { - if (val < from || val > to) - throw new IllegalArgumentException(key + " has invalid range should be within [" + from + ", " + to + "]"); - - return val; - } - - @Override - public String getName() { - return "fastest"; - } -} diff --git a/core/src/main/java/com/graphhopper/routing/weighting/QueryGraphWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/QueryGraphWeighting.java index 1390680872b..e8db6064533 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/QueryGraphWeighting.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/QueryGraphWeighting.java @@ -42,8 +42,8 @@ public QueryGraphWeighting(Weighting weighting, int firstVirtualNodeId, int firs } @Override - public double getMinWeight(double distance) { - return weighting.getMinWeight(distance); + public double calcMinWeightPerDistance() { + return weighting.calcMinWeightPerDistance(); } @Override diff --git a/core/src/main/java/com/graphhopper/routing/weighting/ShortestWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/ShortestWeighting.java deleted file mode 100644 index 16feb05535c..00000000000 --- a/core/src/main/java/com/graphhopper/routing/weighting/ShortestWeighting.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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.routing.weighting; - -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.util.EdgeIteratorState; - -/** - * Calculates the shortest route - independent of a vehicle as the calculation is based on the - * distance only. - * - * @author Peter Karich - */ -public class ShortestWeighting implements Weighting { - - final BooleanEncodedValue accessEnc; - final DecimalEncodedValue speedEnc; - final TurnCostProvider tcProvider; - - public ShortestWeighting(BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { - this(accessEnc, speedEnc, TurnCostProvider.NO_TURN_COST_PROVIDER); - } - - public ShortestWeighting(BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc, TurnCostProvider tcProvider) { - this.accessEnc = accessEnc; - this.speedEnc = speedEnc; - this.tcProvider = tcProvider; - } - - @Override - public double getMinWeight(double distance) { - return distance; - } - - @Override - public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) { - if (reverse ? !edgeState.getReverse(accessEnc) : !edgeState.get(accessEnc)) - return Double.POSITIVE_INFINITY; - return edgeState.getDistance(); - } - - @Override - public long calcEdgeMillis(EdgeIteratorState edgeState, boolean reverse) { - double speed = reverse ? edgeState.getReverse(speedEnc) : edgeState.get(speedEnc); - return Math.round(edgeState.getDistance() / speed * 3.6 * 1000); - } - - @Override - public double calcTurnWeight(int inEdge, int viaNode, int outEdge) { - return tcProvider.calcTurnWeight(inEdge, viaNode, outEdge); - } - - @Override - public long calcTurnMillis(int inEdge, int viaNode, int outEdge) { - return tcProvider.calcTurnMillis(inEdge, viaNode, outEdge); - } - - @Override - public boolean hasTurnCosts() { - return tcProvider != TurnCostProvider.NO_TURN_COST_PROVIDER; - } - - @Override - public String getName() { - return "shortest"; - } -} diff --git a/core/src/main/java/com/graphhopper/routing/weighting/SpeedWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/SpeedWeighting.java index ae12375e9dd..c8e793e2d63 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/SpeedWeighting.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/SpeedWeighting.java @@ -60,8 +60,8 @@ public SpeedWeighting(DecimalEncodedValue speedEnc, TurnCostProvider turnCostPro } @Override - public double getMinWeight(double distance) { - return distance / speedEnc.getMaxStorableDecimal(); + public double calcMinWeightPerDistance() { + return 1 / speedEnc.getMaxStorableDecimal(); } @Override diff --git a/core/src/main/java/com/graphhopper/routing/weighting/Weighting.java b/core/src/main/java/com/graphhopper/routing/weighting/Weighting.java index cd475ee4950..3f0b680563a 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/Weighting.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/Weighting.java @@ -25,15 +25,14 @@ * @author Peter Karich */ public interface Weighting { - int INFINITE_U_TURN_COSTS = -1; /** * Used only for the heuristic estimation in A* * - * @return minimal weight for the specified distance in meter. E.g. if you calculate the fastest - * way the return value is 'distance/max_velocity' + * @return minimal weight per meter. E.g. if you calculate the fastest way the return value + * is '1/max_velocity' or a shortest weighting would return 1. */ - double getMinWeight(double distance); + double calcMinWeightPerDistance(); /** * This method calculates the weight of a given {@link EdgeIteratorState}. E.g. a high value indicates that the edge @@ -67,4 +66,10 @@ public interface Weighting { String getName(); + static boolean isValidName(String name) { + if (name == null || name.isEmpty()) + return false; + + return name.matches("[\\|_a-z]+"); + } } diff --git a/core/src/main/java/com/graphhopper/routing/weighting/custom/ClassHelper.java b/core/src/main/java/com/graphhopper/routing/weighting/custom/ClassHelper.java new file mode 100644 index 00000000000..c92b442f08f --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/weighting/custom/ClassHelper.java @@ -0,0 +1,5 @@ +package com.graphhopper.routing.weighting.custom; + +public interface ClassHelper { + String getClassName(String encVal); +} diff --git a/core/src/main/java/com/graphhopper/routing/weighting/custom/ConditionalExpressionVisitor.java b/core/src/main/java/com/graphhopper/routing/weighting/custom/ConditionalExpressionVisitor.java index 7ad89132c6c..e030898a167 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/custom/ConditionalExpressionVisitor.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/custom/ConditionalExpressionVisitor.java @@ -17,7 +17,6 @@ */ package com.graphhopper.routing.weighting.custom; -import com.graphhopper.routing.ev.RouteNetwork; import com.graphhopper.util.Helper; import org.codehaus.janino.Scanner; import org.codehaus.janino.*; @@ -38,11 +37,13 @@ class ConditionalExpressionVisitor implements Visitor.AtomVisitor replacements = new TreeMap<>(); private final NameValidator variableValidator; + private final ClassHelper classHelper; private String invalidMessage; - public ConditionalExpressionVisitor(ParseResult result, NameValidator variableValidator) { + public ConditionalExpressionVisitor(ParseResult result, NameValidator variableValidator, ClassHelper classHelper) { this.result = result; this.variableValidator = variableValidator; + this.classHelper = classHelper; } // allow only methods and other identifiers (constants and encoded values) @@ -123,7 +124,7 @@ public Boolean visitRvalue(Java.Rvalue rv) throws Exception { if (variableValidator.isValid(lhVarAsString) && Helper.toUpperCase(rhValueAsString).equals(rhValueAsString)) { if (!eqOps) throw new IllegalArgumentException("Operator " + binOp.operator + " not allowed for enum"); - String value = toEncodedValueClassName(binOp.lhs.toString()); + String value = classHelper.getClassName(binOp.lhs.toString()); replacements.put(startRH, new Replacement(startRH, rhValueAsString.length(), value + "." + rhValueAsString)); } } @@ -155,7 +156,7 @@ public Boolean visitConstructorInvocation(Java.ConstructorInvocation ci) { * converted expression that includes class names for constants to avoid conflicts e.g. when doing "toll == Toll.NO" * instead of "toll == NO". */ - static ParseResult parse(String expression, NameValidator validator) { + static ParseResult parse(String expression, NameValidator validator, ClassHelper helper) { ParseResult result = new ParseResult(); try { Parser parser = new Parser(new Scanner("ignore", new StringReader(expression))); @@ -163,7 +164,7 @@ static ParseResult parse(String expression, NameValidator validator) { // after parsing the expression the input should end (otherwise it is not "simple") if (parser.peek().type == TokenType.END_OF_INPUT) { result.guessedVariables = new LinkedHashSet<>(); - ConditionalExpressionVisitor visitor = new ConditionalExpressionVisitor(result, validator); + ConditionalExpressionVisitor visitor = new ConditionalExpressionVisitor(result, validator, helper); result.ok = atom.accept(visitor); result.invalidMessage = visitor.invalidMessage; if (result.ok) { @@ -181,13 +182,6 @@ static ParseResult parse(String expression, NameValidator validator) { return result; } - static String toEncodedValueClassName(String arg) { - if (arg.isEmpty()) throw new IllegalArgumentException("Cannot be empty"); - if (arg.endsWith(RouteNetwork.key(""))) return RouteNetwork.class.getSimpleName(); - String clazz = Helper.underScoreToCamelCase(arg); - return Character.toUpperCase(clazz.charAt(0)) + clazz.substring(1); - } - static class Replacement { int start; int oldLength; diff --git a/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomModelParser.java b/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomModelParser.java index e89431c88ff..7c77d622bb0 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomModelParser.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomModelParser.java @@ -17,7 +17,6 @@ */ package com.graphhopper.routing.weighting.custom; -import com.graphhopper.json.MinMax; import com.graphhopper.json.Statement; import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; @@ -39,6 +38,8 @@ import java.util.*; import java.util.concurrent.atomic.AtomicLong; +import static com.graphhopper.json.Statement.Keyword.IF; + public class CustomModelParser { private static final AtomicLong longVal = new AtomicLong(1); static final String IN_AREA_PREFIX = "in_"; @@ -51,7 +52,7 @@ public class CustomModelParser { // Use accessOrder==true to remove oldest accessed entry, not oldest inserted. private static final int CACHE_SIZE = Integer.getInteger("graphhopper.custom_weighting.cache_size", 1000); private static final Map> CACHE = Collections.synchronizedMap( - new LinkedHashMap>(CACHE_SIZE, 0.75f, true) { + new LinkedHashMap<>(CACHE_SIZE, 0.75f, true) { protected boolean removeEldestEntry(Map.Entry eldest) { return size() > CACHE_SIZE; } @@ -67,35 +68,24 @@ private CustomModelParser() { // utility class } - public static CustomWeighting createWeighting(BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc, DecimalEncodedValue priorityEnc, - EncodedValueLookup lookup, TurnCostProvider turnCostProvider, CustomModel customModel) { + /** + * This method creates a weighting from a CustomModel that must limit the speed. Either as an + * unconditional statement { "if": "true", "limit_to": "car_average_speed" } or as + * an if-else block. + */ + public static CustomWeighting createWeighting(EncodedValueLookup lookup, TurnCostProvider turnCostProvider, CustomModel customModel) { if (customModel == null) throw new IllegalStateException("CustomModel cannot be null"); - double maxSpeed = speedEnc.getMaxOrMaxStorableDecimal(); - CustomWeighting.Parameters parameters = createWeightingParameters(customModel, lookup, speedEnc, maxSpeed, priorityEnc); - return new CustomWeighting(accessEnc, speedEnc, turnCostProvider, parameters); - } - - public static CustomWeighting createFastestWeighting(BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc, EncodingManager lookup) { - CustomModel cm = new CustomModel(); - - return createWeighting(accessEnc, speedEnc, null, lookup, TurnCostProvider.NO_TURN_COST_PROVIDER, cm); + CustomWeighting.Parameters parameters = createWeightingParameters(customModel, lookup); + return new CustomWeighting(turnCostProvider, parameters); } /** * This method compiles a new subclass of CustomWeightingHelper composed from the provided CustomModel caches this * and returns an instance. - * - * @param priorityEnc can be null */ - public static CustomWeighting.Parameters createWeightingParameters(CustomModel customModel, EncodedValueLookup lookup, - DecimalEncodedValue avgSpeedEnc, double globalMaxSpeed, - DecimalEncodedValue priorityEnc) { - - double globalMaxPriority = priorityEnc == null ? 1 : priorityEnc.getMaxStorableDecimal(); - // if the same custom model is used with a different base profile we cannot use the cached version - String key = customModel + ",speed:" + avgSpeedEnc.getName() + ",global_max_speed:" + globalMaxSpeed - + (priorityEnc == null ? "" : "prio:" + priorityEnc.getName() + ",global_max_priority:" + globalMaxPriority); + public static CustomWeighting.Parameters createWeightingParameters(CustomModel customModel, EncodedValueLookup lookup) { + String key = customModel.toString(); if (key.length() > 100_000) throw new IllegalArgumentException("Custom Model too big: " + key.length()); @@ -103,7 +93,7 @@ public static CustomWeighting.Parameters createWeightingParameters(CustomModel c if (CACHE_SIZE > 0 && clazz == null) clazz = CACHE.get(key); if (clazz == null) { - clazz = createClazz(customModel, lookup, globalMaxSpeed, globalMaxPriority); + clazz = createClazz(customModel, lookup); if (customModel.isInternal()) { INTERNAL_CACHE.put(key, clazz); if (INTERNAL_CACHE.size() > 100) { @@ -120,8 +110,10 @@ public static CustomWeighting.Parameters createWeightingParameters(CustomModel c try { // The class does not need to be thread-safe as we create an instance per request CustomWeightingHelper prio = (CustomWeightingHelper) clazz.getDeclaredConstructor().newInstance(); - prio.init(lookup, avgSpeedEnc, priorityEnc, CustomModel.getAreasAsMap(customModel.getAreas())); - return new CustomWeighting.Parameters(prio::getSpeed, prio::getPriority, prio.getMaxSpeed(), prio.getMaxPriority(), + prio.init(customModel, lookup, CustomModel.getAreasAsMap(customModel.getAreas())); + return new CustomWeighting.Parameters( + prio::getSpeed, prio::calcMaxSpeed, + prio::getPriority, prio::calcMaxPriority, customModel.getDistanceInfluence() == null ? 0 : customModel.getDistanceInfluence(), customModel.getHeadingPenalty() == null ? Parameters.Routing.DEFAULT_HEADING_PENALTY : customModel.getHeadingPenalty()); } catch (ReflectiveOperationException ex) { @@ -132,47 +124,43 @@ public static CustomWeighting.Parameters createWeightingParameters(CustomModel c /** * This method does the following: *

    - *
  • 0. optionally we already checked the right-hand side expressions before this method call in FindMinMax.checkLMConstraints - * (only the client-side custom model statements) - *
  • - *
  • 1. determine minimum and maximum values via parsing the right-hand side expression -> done in ValueExpressionVisitor. - * We need the maximum values for a simple negative check AND for the CustomWeighting.Parameters which is for - * Weighting.getMinWeight which is for A*. Note: we could make this step optional somehow for other algorithms, - * but parsing would be still required in the next step for security reasons. + *
  • + * 1. parse the value expressions (RHS) to know about additional encoded values ('findVariables') + * and check for multiplications with negative values. *
  • - *
  • 2. parse condition value of priority and speed statements -> done in ConditionalExpressionVisitor (don't parse RHS expressions again) + *
  • 2. parse conditional expression of priority and speed statements -> done in ConditionalExpressionVisitor (don't parse RHS expressions again) *
  • *
  • 3. create class template as String, inject the created statements and create the Class *
  • *
*/ - private static Class createClazz(CustomModel customModel, EncodedValueLookup lookup, - double globalMaxSpeed, double globalMaxPriority) { + private static Class createClazz(CustomModel customModel, EncodedValueLookup lookup) { try { - HashSet priorityVariables = new LinkedHashSet<>(); - // initial value of minimum has to be >0 so that multiple_by with a negative value leads to a negative value and not 0 - MinMax minMaxPriority = new MinMax(1, globalMaxPriority); - FindMinMax.findMinMax(priorityVariables, minMaxPriority, customModel.getPriority(), lookup); - if (minMaxPriority.min < 0) - throw new IllegalArgumentException("priority has to be >=0 but can be negative (" + minMaxPriority.min + ")"); - if (minMaxPriority.max < 0) - throw new IllegalArgumentException("maximum priority has to be >=0 but was " + minMaxPriority.max); + Set priorityVariables = ValueExpressionVisitor.findVariables(customModel.getPriority(), lookup); List priorityStatements = createGetPriorityStatements(priorityVariables, customModel, lookup); - HashSet speedVariables = new LinkedHashSet<>(); - MinMax minMaxSpeed = new MinMax(1, globalMaxSpeed); - FindMinMax.findMinMax(speedVariables, minMaxSpeed, customModel.getSpeed(), lookup); - if (minMaxSpeed.min < 0) - throw new IllegalArgumentException("speed has to be >=0 but can be negative (" + minMaxSpeed.min + ")"); - if (minMaxSpeed.max <= 0) - throw new IllegalArgumentException("maximum speed has to be >0 but was " + minMaxSpeed.max); + if (customModel.getSpeed().isEmpty()) + throw new IllegalArgumentException("At least one initial statement under 'speed' is required."); + + List firstBlock = splitIntoBlocks(customModel.getSpeed()).get(0); + if (firstBlock.size() > 1) { + Statement lastSt = firstBlock.get(firstBlock.size() - 1); + if (lastSt.getOperation() != Statement.Op.LIMIT || lastSt.getKeyword() != Statement.Keyword.ELSE) + throw new IllegalArgumentException("The first block needs to end with an 'else' (or contain a single unconditional 'if' statement)."); + } else { + Statement firstSt = firstBlock.get(0); + if (!"true".equals(firstSt.getCondition()) || firstSt.getOperation() != Statement.Op.LIMIT || firstSt.getKeyword() != Statement.Keyword.IF) + throw new IllegalArgumentException("The first block needs to contain a single unconditional 'if' statement (or end with an 'else')."); + } + + Set speedVariables = ValueExpressionVisitor.findVariables(customModel.getSpeed(), lookup); List speedStatements = createGetSpeedStatements(speedVariables, customModel, lookup); + // Create different class name, which is required only for debugging. // TODO does it improve performance too? I.e. it could be that the JIT is confused if different classes // have the same name and it mixes performance stats. See https://github.com/janino-compiler/janino/issues/137 long counter = longVal.incrementAndGet(); - String classTemplate = createClassTemplate(counter, priorityVariables, minMaxPriority.max, speedVariables, minMaxSpeed.max, - lookup, CustomModel.getAreasAsMap(customModel.getAreas())); + String classTemplate = createClassTemplate(counter, priorityVariables, speedVariables, lookup, CustomModel.getAreasAsMap(customModel.getAreas())); Java.CompilationUnit cu = (Java.CompilationUnit) new Parser(new Scanner("source", new StringReader(classTemplate))). parseAbstractCompilationUnit(); cu = injectStatements(priorityStatements, speedStatements, cu); @@ -184,6 +172,50 @@ private static Class createClazz(CustomModel customModel, EncodedValueLookup } } + public static List findVariablesForEncodedValuesString(CustomModel model, NameValidator nameValidator, ClassHelper classHelper) { + Set variables = new LinkedHashSet<>(); + // avoid parsing exception for backward_xy or in_xy ... + NameValidator nameValidatorIntern = s -> { + // some literals are no variables and would throw an exception (encoded value not found) + if (Character.isUpperCase(s.charAt(0)) || s.startsWith(BACKWARD_PREFIX) || s.startsWith(IN_AREA_PREFIX)) + return true; + if (nameValidator.isValid(s)) { + variables.add(s); + return true; + } + return false; + }; + findVariablesForEncodedValuesString(model.getPriority(), nameValidatorIntern, classHelper); + findVariablesForEncodedValuesString(model.getSpeed(), nameValidatorIntern, classHelper); + return new ArrayList<>(variables); + } + + private static void findVariablesForEncodedValuesString(List statements, NameValidator nameValidator, ClassHelper classHelper) { + List> blocks = CustomModelParser.splitIntoBlocks(statements); + for (List block : blocks) { + for (Statement statement : block) { + // ignore potential problems; collect only variables in this step + ConditionalExpressionVisitor.parse(statement.getCondition(), nameValidator, classHelper); + ValueExpressionVisitor.parse(statement.getValue(), nameValidator); + } + } + } + + /** + * Splits the specified list into several list of statements starting with if + */ + static List> splitIntoBlocks(List statements) { + List> result = new ArrayList<>(); + List block = null; + for (Statement st : statements) { + if (IF.equals(st.getKeyword())) result.add(block = new ArrayList<>()); + if (block == null) + throw new IllegalArgumentException("Every block must start with an if-statement"); + block.add(st); + } + return result; + } + /** * Parse the expressions from CustomModel relevant for the method getSpeed - see createClassTemplate. * @@ -193,8 +225,8 @@ private static List createGetSpeedStatements(Set sp CustomModel customModel, EncodedValueLookup lookup) throws Exception { List speedStatements = new ArrayList<>(verifyExpressions(new StringBuilder(), "speed entry", speedVariables, customModel.getSpeed(), lookup)); - String speedMethodStartBlock = "double value = super.getRawSpeed(edge, reverse);\n"; - // a bit inefficient to possibly define variables twice, but for now we have two separate methods + String speedMethodStartBlock = "double value = " + CustomWeightingHelper.GLOBAL_MAX_SPEED + ";\n"; + // potentially we fetch EncodedValues twice (one time here and one time for priority) for (String arg : speedVariables) { speedMethodStartBlock += getVariableDeclaration(lookup, arg); } @@ -212,7 +244,7 @@ private static List createGetPriorityStatements(Set CustomModel customModel, EncodedValueLookup lookup) throws Exception { List priorityStatements = new ArrayList<>(verifyExpressions(new StringBuilder(), "priority entry", priorityVariables, customModel.getPriority(), lookup)); - String priorityMethodStartBlock = "double value = super.getRawPriority(edge, reverse);\n"; + String priorityMethodStartBlock = "double value = " + CustomWeightingHelper.GLOBAL_PRIORITY + ";\n"; for (String arg : priorityVariables) { priorityMethodStartBlock += getVariableDeclaration(lookup, arg); } @@ -261,8 +293,11 @@ private static String getInterface(EncodedValue enc) { private static String getReturnType(EncodedValue encodedValue) { // order is important - if (encodedValue instanceof EnumEncodedValue) - return ((EnumEncodedValue) encodedValue).getEnumSimpleName(); + if (encodedValue instanceof EnumEncodedValue) { + Class cl = ((EnumEncodedValue) encodedValue).getEnumType(); + // use getSimpleName for inbuilt EncodedValues and more readability of generated source + return cl.getPackage().equals(EnumEncodedValue.class.getPackage()) ? cl.getSimpleName() : cl.getName(); + } if (encodedValue instanceof StringEncodedValue) return "int"; // we use indexOf if (encodedValue instanceof DecimalEncodedValue) return "double"; if (encodedValue instanceof BooleanEncodedValue) return "boolean"; @@ -277,16 +312,16 @@ private static String getReturnType(EncodedValue encodedValue) { * have to inject that parsed and safe user expressions in a later step. */ private static String createClassTemplate(long counter, - Set priorityVariables, double maxPriority, - Set speedVariables, double maxSpeed, + Set priorityVariables, Set speedVariables, EncodedValueLookup lookup, Map areas) { final StringBuilder importSourceCode = new StringBuilder("import com.graphhopper.routing.ev.*;\n"); importSourceCode.append("import java.util.Map;\n"); + importSourceCode.append("import " + CustomModel.class.getName() + ";\n"); final StringBuilder classSourceCode = new StringBuilder(100); boolean includedAreaImports = false; - final StringBuilder initSourceCode = new StringBuilder("this.avg_speed_enc = avgSpeedEnc;\n"); - initSourceCode.append("this.priority_enc = priorityEnc;\n"); + final StringBuilder initSourceCode = new StringBuilder("this.lookup = lookup;\n"); + initSourceCode.append("this.customModel = customModel;\n"); Set set = new HashSet<>(); for (String prioVar : priorityVariables) set.add(prioVar.startsWith(BACKWARD_PREFIX) ? prioVar.substring(BACKWARD_PREFIX.length()) : prioVar); @@ -340,8 +375,7 @@ private static String createClassTemplate(long counter, + "\npublic class JaninoCustomWeightingHelperSubclass" + counter + " extends " + CustomWeightingHelper.class.getSimpleName() + " {\n" + classSourceCode + " @Override\n" - + " public void init(EncodedValueLookup lookup, " + DecimalEncodedValue.class.getName() + " avgSpeedEnc, " - + DecimalEncodedValue.class.getName() + " priorityEnc, Map areas) {\n" + + " public void init(CustomModel customModel, EncodedValueLookup lookup, Map areas) {\n" + initSourceCode + " }\n\n" // we need these placeholder methods so that the hooks in DeepCopier are invoked @@ -351,15 +385,7 @@ private static String createClassTemplate(long counter, + " }\n" + " @Override\n" + " public double getSpeed(EdgeIteratorState edge, boolean reverse) {\n" - + " return getRawSpeed(edge, reverse); //will be overwritten by code injected in DeepCopier\n" - + " }\n" - + " @Override\n" - + " protected double getMaxSpeed() {\n" - + " return " + maxSpeed + ";" - + " }\n" - + " @Override\n" - + " protected double getMaxPriority() {\n" - + " return " + maxPriority + ";" + + " return 1; //will be overwritten by code injected in DeepCopier\n" + " }\n" + "}"; } @@ -378,14 +404,16 @@ private static List verifyExpressions(StringBuilder express NameValidator nameInConditionValidator = name -> lookup.hasEncodedValue(name) || name.toUpperCase(Locale.ROOT).equals(name) || name.startsWith(IN_AREA_PREFIX) || name.startsWith(BACKWARD_PREFIX) && lookup.hasEncodedValue(name.substring(BACKWARD_PREFIX.length())); + ClassHelper helper = key -> getReturnType(lookup.getEncodedValue(key, EncodedValue.class)); - parseExpressions(expressions, nameInConditionValidator, info, createObjects, list); + parseExpressions(expressions, nameInConditionValidator, info, createObjects, list, helper); return new Parser(new org.codehaus.janino.Scanner(info, new StringReader(expressions.toString()))). parseBlockStatements(); } static void parseExpressions(StringBuilder expressions, NameValidator nameInConditionValidator, - String exceptionInfo, Set createObjects, List list) { + String exceptionInfo, Set createObjects, List list, + ClassHelper classHelper) { for (Statement statement : list) { // avoid parsing the RHS value expression again as we just did it to get the maximum values in createClazz @@ -395,7 +423,7 @@ static void parseExpressions(StringBuilder expressions, NameValidator nameInCond expressions.append("else {").append(statement.getOperation().build(statement.getValue())).append("; }\n"); } else if (statement.getKeyword() == Statement.Keyword.ELSEIF || statement.getKeyword() == Statement.Keyword.IF) { - ParseResult parseResult = ConditionalExpressionVisitor.parse(statement.getCondition(), nameInConditionValidator); + ParseResult parseResult = ConditionalExpressionVisitor.parse(statement.getCondition(), nameInConditionValidator, classHelper); if (!parseResult.ok) throw new IllegalArgumentException(exceptionInfo + " invalid condition \"" + statement.getCondition() + "\"" + (parseResult.invalidMessage == null ? "" : ": " + parseResult.invalidMessage)); diff --git a/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomWeighting.java index 87db80322a2..e61744eea1d 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomWeighting.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomWeighting.java @@ -17,13 +17,13 @@ */ package com.graphhopper.routing.weighting.custom; -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.weighting.AbstractWeighting; import com.graphhopper.routing.weighting.TurnCostProvider; +import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.util.CustomModel; import com.graphhopper.util.EdgeIteratorState; +import static com.graphhopper.routing.weighting.TurnCostProvider.NO_TURN_COST_PROVIDER; + /** * The CustomWeighting allows adjusting the edge weights relative to those we'd obtain for a given base flag encoder. * For example a car flag encoder already provides speeds and access flags for every edge depending on certain edge @@ -69,7 +69,7 @@ * calculated via the speed_factor is simply overwritten. Edges that are not accessible according to the access flags of * the base vehicle always get assigned an infinite weight and this cannot be changed (yet) using this weighting. */ -public final class CustomWeighting extends AbstractWeighting { +public final class CustomWeighting implements Weighting { public static final String NAME = "custom"; /** @@ -77,20 +77,26 @@ public final class CustomWeighting extends AbstractWeighting { * costs or traffic light costs etc) */ private final static double SPEED_CONV = 3.6; - private final double maxSpeed; - private final double maxPriority; private final double distanceInfluence; private final double headingPenaltySeconds; private final EdgeToDoubleMapping edgeToSpeedMapping; private final EdgeToDoubleMapping edgeToPriorityMapping; + private final TurnCostProvider turnCostProvider; + private final MaxCalc maxPrioCalc; + private final MaxCalc maxSpeedCalc; + + public CustomWeighting(TurnCostProvider turnCostProvider, Parameters parameters) { + if (!Weighting.isValidName(getName())) + throw new IllegalStateException("Not a valid name for a Weighting: " + getName()); + this.turnCostProvider = turnCostProvider; - public CustomWeighting(BooleanEncodedValue baseAccessEnc, DecimalEncodedValue baseSpeedEnc, TurnCostProvider turnCostProvider, Parameters parameters) { - super(baseAccessEnc, baseSpeedEnc, turnCostProvider); this.edgeToSpeedMapping = parameters.getEdgeToSpeedMapping(); + this.maxSpeedCalc = parameters.getMaxSpeedCalc(); + this.edgeToPriorityMapping = parameters.getEdgeToPriorityMapping(); + this.maxPrioCalc = parameters.getMaxPrioCalc(); + this.headingPenaltySeconds = parameters.getHeadingPenaltySeconds(); - this.maxSpeed = parameters.getMaxSpeed() / SPEED_CONV; - this.maxPriority = parameters.getMaxPriority(); // given unit is s/km -> convert to s/m this.distanceInfluence = parameters.getDistanceInfluence() / 1000.0; @@ -99,12 +105,15 @@ public CustomWeighting(BooleanEncodedValue baseAccessEnc, DecimalEncodedValue ba } @Override - public double getMinWeight(double distance) { - return distance / maxSpeed / maxPriority + distance * distanceInfluence; + public double calcMinWeightPerDistance() { + return 1d / (maxSpeedCalc.calcMax() / SPEED_CONV) / maxPrioCalc.calcMax() + distanceInfluence; } @Override public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) { + double priority = edgeToPriorityMapping.get(edgeState, reverse); + if (priority == 0) return Double.POSITIVE_INFINITY; + final double distance = edgeState.getDistance(); double seconds = calcSeconds(distance, edgeState, reverse); if (Double.isInfinite(seconds)) return Double.POSITIVE_INFINITY; @@ -112,19 +121,11 @@ public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) { if (edgeState.get(EdgeIteratorState.UNFAVORED_EDGE)) seconds += headingPenaltySeconds; double distanceCosts = distance * distanceInfluence; if (Double.isInfinite(distanceCosts)) return Double.POSITIVE_INFINITY; - double priority = edgeToPriorityMapping.get(edgeState, reverse); - // special case to avoid NaN for barrier edges (where time is often 0s) - if (priority == 0 && seconds == 0) return Double.POSITIVE_INFINITY; return seconds / priority + distanceCosts; } double calcSeconds(double distance, EdgeIteratorState edgeState, boolean reverse) { - if (reverse ? !edgeState.getReverse(accessEnc) : !edgeState.get(accessEnc)) - return Double.POSITIVE_INFINITY; - double speed = edgeToSpeedMapping.get(edgeState, reverse); - if (speed > maxSpeed * SPEED_CONV) - throw new IllegalStateException("for " + getName() + " speed <= maxSpeed is violated, " + speed + " <= " + maxSpeed * SPEED_CONV); if (speed == 0) return Double.POSITIVE_INFINITY; if (speed < 0) @@ -138,6 +139,21 @@ public long calcEdgeMillis(EdgeIteratorState edgeState, boolean reverse) { return Math.round(calcSeconds(edgeState.getDistance(), edgeState, reverse) * 1000); } + @Override + public double calcTurnWeight(int inEdge, int viaNode, int outEdge) { + return turnCostProvider.calcTurnWeight(inEdge, viaNode, outEdge); + } + + @Override + public long calcTurnMillis(int inEdge, int viaNode, int outEdge) { + return turnCostProvider.calcTurnMillis(inEdge, viaNode, outEdge); + } + + @Override + public boolean hasTurnCosts() { + return turnCostProvider != NO_TURN_COST_PROVIDER; + } + @Override public String getName() { return NAME; @@ -148,20 +164,26 @@ public interface EdgeToDoubleMapping { double get(EdgeIteratorState edge, boolean reverse); } + @FunctionalInterface + public interface MaxCalc { + double calcMax(); + } + public static class Parameters { private final EdgeToDoubleMapping edgeToSpeedMapping; private final EdgeToDoubleMapping edgeToPriorityMapping; - private final double maxSpeed; - private final double maxPriority; + private final MaxCalc maxSpeedCalc; + private final MaxCalc maxPrioCalc; private final double distanceInfluence; private final double headingPenaltySeconds; - public Parameters(EdgeToDoubleMapping edgeToSpeedMapping, EdgeToDoubleMapping edgeToPriorityMapping, - double maxSpeed, double maxPriority, double distanceInfluence, double headingPenaltySeconds) { + public Parameters(EdgeToDoubleMapping edgeToSpeedMapping, MaxCalc maxSpeedCalc, + EdgeToDoubleMapping edgeToPriorityMapping, MaxCalc maxPrioCalc, + double distanceInfluence, double headingPenaltySeconds) { this.edgeToSpeedMapping = edgeToSpeedMapping; + this.maxSpeedCalc = maxSpeedCalc; this.edgeToPriorityMapping = edgeToPriorityMapping; - this.maxSpeed = maxSpeed; - this.maxPriority = maxPriority; + this.maxPrioCalc = maxPrioCalc; this.distanceInfluence = distanceInfluence; this.headingPenaltySeconds = headingPenaltySeconds; } @@ -174,8 +196,12 @@ public EdgeToDoubleMapping getEdgeToPriorityMapping() { return edgeToPriorityMapping; } - public double getMaxSpeed() { - return maxSpeed; + public MaxCalc getMaxSpeedCalc() { + return maxSpeedCalc; + } + + public MaxCalc getMaxPrioCalc() { + return maxPrioCalc; } public double getDistanceInfluence() { @@ -185,9 +211,5 @@ public double getDistanceInfluence() { public double getHeadingPenaltySeconds() { return headingPenaltySeconds; } - - public double getMaxPriority() { - return maxPriority; - } } } diff --git a/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomWeightingHelper.java b/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomWeightingHelper.java index bdd66c8cea0..4388d470546 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomWeightingHelper.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomWeightingHelper.java @@ -17,35 +17,38 @@ */ package com.graphhopper.routing.weighting.custom; +import com.graphhopper.json.MinMax; +import com.graphhopper.json.Statement; import com.graphhopper.routing.ev.DecimalEncodedValue; import com.graphhopper.routing.ev.EncodedValueLookup; -import com.graphhopper.util.EdgeIteratorState; -import com.graphhopper.util.FetchMode; -import com.graphhopper.util.GHUtility; -import com.graphhopper.util.JsonFeature; +import com.graphhopper.util.*; import com.graphhopper.util.shapes.BBox; import com.graphhopper.util.shapes.Polygon; +import java.util.List; import java.util.Map; /** - * This class is for internal usage only. It is subclassed by Janino, then special expressions are injected into init, - * getSpeed and getPriority. At the end an instance is created and used in CustomWeighting. + * This class is for internal usage only. It is subclassed by Janino, then special expressions are + * injected into init, getSpeed and getPriority. At the end an instance is created and used in CustomWeighting. */ public class CustomWeightingHelper { - protected DecimalEncodedValue avg_speed_enc; - protected DecimalEncodedValue priority_enc; + static double GLOBAL_MAX_SPEED = 999; + static double GLOBAL_PRIORITY = 1; + + protected EncodedValueLookup lookup; + protected CustomModel customModel; protected CustomWeightingHelper() { } - public void init(EncodedValueLookup lookup, DecimalEncodedValue avgSpeedEnc, DecimalEncodedValue priorityEnc, Map areas) { - this.avg_speed_enc = avgSpeedEnc; - this.priority_enc = priorityEnc; + public void init(CustomModel customModel, EncodedValueLookup lookup, Map areas) { + this.lookup = lookup; + this.customModel = customModel; } public double getPriority(EdgeIteratorState edge, boolean reverse) { - return 1; + return getRawPriority(edge, reverse); } public double getSpeed(EdgeIteratorState edge, boolean reverse) { @@ -53,26 +56,40 @@ public double getSpeed(EdgeIteratorState edge, boolean reverse) { } protected final double getRawSpeed(EdgeIteratorState edge, boolean reverse) { - double speed = reverse ? edge.getReverse(avg_speed_enc) : edge.get(avg_speed_enc); - if (Double.isInfinite(speed) || Double.isNaN(speed) || speed < 0) - throw new IllegalStateException("Invalid estimated speed " + speed); - return speed; + return 1; } protected final double getRawPriority(EdgeIteratorState edge, boolean reverse) { - if (priority_enc == null) return 1; - double priority = reverse ? edge.getReverse(priority_enc) : edge.get(priority_enc); - if (Double.isInfinite(priority) || Double.isNaN(priority) || priority < 0) - throw new IllegalStateException("Invalid priority " + priority); - return priority; + return 1; } - protected double getMaxPriority() { - return 1; + public final double calcMaxSpeed() { + MinMax minMaxSpeed = new MinMax(0, GLOBAL_MAX_SPEED); + FindMinMax.findMinMax(minMaxSpeed, customModel.getSpeed(), lookup); + if (minMaxSpeed.min < 0) + throw new IllegalArgumentException("speed has to be >=0 but can be negative (" + minMaxSpeed.min + ")"); + if (minMaxSpeed.max <= 0) + throw new IllegalArgumentException("maximum speed has to be >0 but was " + minMaxSpeed.max); + if (minMaxSpeed.max == GLOBAL_MAX_SPEED) + throw new IllegalArgumentException("The first statement for 'speed' must be unconditionally to set the speed. But it was " + customModel.getSpeed().get(0)); + + return minMaxSpeed.max; } - protected double getMaxSpeed() { - return 1; + public final double calcMaxPriority() { + MinMax minMaxPriority = new MinMax(0, GLOBAL_PRIORITY); + List statements = customModel.getPriority(); + if (!statements.isEmpty() && "true".equals(statements.get(0).getCondition())) { + String value = statements.get(0).getValue(); + if (lookup.hasEncodedValue(value)) + minMaxPriority.max = lookup.getDecimalEncodedValue(value).getMaxOrMaxStorableDecimal(); + } + FindMinMax.findMinMax(minMaxPriority, statements, lookup); + if (minMaxPriority.min < 0) + throw new IllegalArgumentException("priority has to be >=0 but can be negative (" + minMaxPriority.min + ")"); + if (minMaxPriority.max < 0) + throw new IllegalArgumentException("maximum priority has to be >=0 but was " + minMaxPriority.max); + return minMaxPriority.max; } public static boolean in(Polygon p, EdgeIteratorState edge) { diff --git a/core/src/main/java/com/graphhopper/routing/weighting/custom/FindMinMax.java b/core/src/main/java/com/graphhopper/routing/weighting/custom/FindMinMax.java index 9aa5a1c8eed..eab4791cf35 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/custom/FindMinMax.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/custom/FindMinMax.java @@ -5,10 +5,7 @@ import com.graphhopper.routing.ev.EncodedValueLookup; import com.graphhopper.util.CustomModel; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import static com.graphhopper.json.Statement.Keyword.ELSE; import static com.graphhopper.json.Statement.Keyword.IF; @@ -35,10 +32,9 @@ public static void checkLMConstraints(CustomModel baseModel, CustomModel queryMo } private static void checkMultiplyValue(List list, EncodedValueLookup lookup) { - Set createdObjects = new HashSet<>(); for (Statement statement : list) { if (statement.getOperation() == Statement.Op.MULTIPLY) { - MinMax minMax = ValueExpressionVisitor.findMinMax(createdObjects, statement.getValue(), lookup); + MinMax minMax = ValueExpressionVisitor.findMinMax(statement.getValue(), lookup); if (minMax.max > 1) throw new IllegalArgumentException("maximum of value '" + statement.getValue() + "' cannot be larger than 1, but was: " + minMax.max); else if (minMax.min < 0) @@ -51,26 +47,30 @@ else if (minMax.min < 0) * This method returns the smallest value possible in "min" and the smallest value that cannot be * exceeded by any edge in max. */ - static MinMax findMinMax(Set createdObjects, MinMax minMax, List statements, EncodedValueLookup lookup) { + static MinMax findMinMax(MinMax minMax, List statements, EncodedValueLookup lookup) { // 'blocks' of the statements are applied one after the other. A block consists of one (if) or more statements (elseif+else) - List> blocks = splitIntoBlocks(statements); - for (List block : blocks) findMinMaxForBlock(createdObjects, minMax, block, lookup); + List> blocks = CustomModelParser.splitIntoBlocks(statements); + for (List block : blocks) findMinMaxForBlock(minMax, block, lookup); return minMax; } - private static void findMinMaxForBlock(Set createdObjects, final MinMax minMax, List block, EncodedValueLookup lookup) { + private static void findMinMaxForBlock(final MinMax minMax, List block, EncodedValueLookup lookup) { if (block.isEmpty() || !IF.equals(block.get(0).getKeyword())) throw new IllegalArgumentException("Every block must start with an if-statement"); MinMax minMaxBlock; if (block.get(0).getCondition().trim().equals("true")) { - minMaxBlock = block.get(0).getOperation().apply(minMax, ValueExpressionVisitor.findMinMax(createdObjects, block.get(0).getValue(), lookup)); + minMaxBlock = block.get(0).getOperation().apply(minMax, ValueExpressionVisitor.findMinMax(block.get(0).getValue(), lookup)); + if (minMaxBlock.max < 0) + throw new IllegalArgumentException("statement resulted in negative value: " + block.get(0)); } else { minMaxBlock = new MinMax(Double.MAX_VALUE, 0); boolean foundElse = false; for (Statement s : block) { if (s.getKeyword() == ELSE) foundElse = true; - MinMax tmp = s.getOperation().apply(minMax, ValueExpressionVisitor.findMinMax(createdObjects, s.getValue(), lookup)); + MinMax tmp = s.getOperation().apply(minMax, ValueExpressionVisitor.findMinMax(s.getValue(), lookup)); + if (tmp.max < 0) + throw new IllegalArgumentException("statement resulted in negative value: " + s); minMaxBlock.min = Math.min(minMaxBlock.min, tmp.min); minMaxBlock.max = Math.max(minMaxBlock.max, tmp.max); } @@ -85,18 +85,4 @@ private static void findMinMaxForBlock(Set createdObjects, final MinMax minMax.min = minMaxBlock.min; minMax.max = minMaxBlock.max; } - - /** - * Splits the specified list into several list of statements starting with if - */ - private static List> splitIntoBlocks(List statements) { - List> result = new ArrayList<>(); - List block = null; - for (Statement st : statements) { - if (IF.equals(st.getKeyword())) result.add(block = new ArrayList<>()); - if (block == null) throw new IllegalArgumentException("Every block must start with an if-statement"); - block.add(st); - } - return result; - } } diff --git a/core/src/main/java/com/graphhopper/routing/weighting/custom/NameValidator.java b/core/src/main/java/com/graphhopper/routing/weighting/custom/NameValidator.java index 058e7ce8022..4fb4e51a575 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/custom/NameValidator.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/custom/NameValidator.java @@ -1,5 +1,5 @@ package com.graphhopper.routing.weighting.custom; -interface NameValidator { +public interface NameValidator { boolean isValid(String name); } diff --git a/core/src/main/java/com/graphhopper/routing/weighting/custom/ValueExpressionVisitor.java b/core/src/main/java/com/graphhopper/routing/weighting/custom/ValueExpressionVisitor.java index 2e626b10b68..e7a33365188 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/custom/ValueExpressionVisitor.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/custom/ValueExpressionVisitor.java @@ -1,19 +1,20 @@ package com.graphhopper.routing.weighting.custom; import com.graphhopper.json.MinMax; +import com.graphhopper.json.Statement; import com.graphhopper.routing.ev.DecimalEncodedValue; import com.graphhopper.routing.ev.EncodedValue; import com.graphhopper.routing.ev.EncodedValueLookup; import com.graphhopper.routing.ev.IntEncodedValue; import org.codehaus.commons.compiler.CompileException; import org.codehaus.janino.*; +import org.codehaus.janino.Scanner; import java.io.StringReader; import java.lang.reflect.InvocationTargetException; -import java.util.Arrays; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Set; +import java.util.*; + +import static com.graphhopper.json.Statement.Keyword.IF; /** * Expression visitor for right-hand side value of limit_to or multiply_by. @@ -135,13 +136,79 @@ static ParseResult parse(String expression, NameValidator variableValidator) { return result; } - static MinMax findMinMax(Set createdObjects, String valueExpression, EncodedValueLookup lookup) { + static Set findVariables(List statements, EncodedValueLookup lookup) { + List> blocks = CustomModelParser.splitIntoBlocks(statements); + Set variables = new LinkedHashSet<>(); + for (List block : blocks) findVariablesForBlock(variables, block, lookup); + return variables; + } + + private static void findVariablesForBlock(Set createdObjects, List block, EncodedValueLookup lookup) { + if (block.isEmpty() || !IF.equals(block.get(0).getKeyword())) + throw new IllegalArgumentException("Every block must start with an if-statement"); + + if (block.get(0).getCondition().trim().equals("true")) { + createdObjects.addAll(ValueExpressionVisitor.findVariables(block.get(0).getValue(), lookup)); + } else { + for (Statement s : block) { + createdObjects.addAll(ValueExpressionVisitor.findVariables(s.getValue(), lookup)); + } + } + } + + static Set findVariables(String valueExpression, EncodedValueLookup lookup) { + ParseResult result = parse(valueExpression, lookup::hasEncodedValue); + if (!result.ok) + throw new IllegalArgumentException(result.invalidMessage); + if (result.guessedVariables.size() > 1) + throw new IllegalArgumentException("Currently only a single EncodedValue is allowed on the right-hand side, but was " + result.guessedVariables.size() + ". Value expression: " + valueExpression); + + // TODO Nearly duplicate code as in findMinMax + double value; + try { + // Speed optimization for numbers only as its over 200x faster than ExpressionEvaluator+cook+evaluate! + // We still call the parse() method before as it is only ~3x slower and might increase security slightly. Because certain + // expressions are accepted from Double.parseDouble but parse() rejects them. With this call order we avoid unexpected security problems. + value = Double.parseDouble(valueExpression); + } catch (NumberFormatException ex) { + try { + if (result.guessedVariables.isEmpty()) { // without encoded values + ExpressionEvaluator ee = new ExpressionEvaluator(); + ee.cook(valueExpression); + value = ((Number) ee.evaluate()).doubleValue(); + } else if (lookup.hasEncodedValue(valueExpression)) { // speed up for common case that complete right-hand side is the encoded value + EncodedValue enc = lookup.getEncodedValue(valueExpression, EncodedValue.class); + value = Math.min(getMin(enc), getMax(enc)); + } else { + // single encoded value + ExpressionEvaluator ee = new ExpressionEvaluator(); + String var = result.guessedVariables.iterator().next(); + ee.setParameters(new String[]{var}, new Class[]{double.class}); + ee.cook(valueExpression); + double max = getMax(lookup.getEncodedValue(var, EncodedValue.class)); + Number val1 = (Number) ee.evaluate(max); + double min = getMin(lookup.getEncodedValue(var, EncodedValue.class)); + Number val2 = (Number) ee.evaluate(min); + value = Math.min(val1.doubleValue(), val2.doubleValue()); + } + } catch (CompileException | InvocationTargetException ex2) { + throw new IllegalArgumentException(ex2); + } + } + if (value < 0) + throw new IllegalArgumentException("illegal expression as it can result in a negative weight: " + valueExpression); + + return result.guessedVariables; + } + + static MinMax findMinMax(String valueExpression, EncodedValueLookup lookup) { ParseResult result = parse(valueExpression, lookup::hasEncodedValue); if (!result.ok) throw new IllegalArgumentException(result.invalidMessage); if (result.guessedVariables.size() > 1) throw new IllegalArgumentException("Currently only a single EncodedValue is allowed on the right-hand side, but was " + result.guessedVariables.size() + ". Value expression: " + valueExpression); + // TODO Nearly duplicate as in findVariables try { // Speed optimization for numbers only as its over 200x faster than ExpressionEvaluator+cook+evaluate! // We still call the parse() method before as it is only ~3x slower and might increase security slightly. Because certain @@ -159,7 +226,6 @@ static MinMax findMinMax(Set createdObjects, String valueExpression, Enc return new MinMax(val, val); } - createdObjects.addAll(result.guessedVariables); if (lookup.hasEncodedValue(valueExpression)) { // speed up for common case that complete right-hand side is the encoded value EncodedValue enc = lookup.getEncodedValue(valueExpression, EncodedValue.class); double min = getMin(enc), max = getMax(enc); @@ -187,8 +253,8 @@ static double getMin(EncodedValue enc) { } static double getMax(EncodedValue enc) { - if (enc instanceof DecimalEncodedValue) return ((DecimalEncodedValue) enc).getMaxStorableDecimal(); - else if (enc instanceof IntEncodedValue) return ((IntEncodedValue) enc).getMaxStorableInt(); + if (enc instanceof DecimalEncodedValue) return ((DecimalEncodedValue) enc).getMaxOrMaxStorableDecimal(); + else if (enc instanceof IntEncodedValue) return ((IntEncodedValue) enc).getMaxOrMaxStorableInt(); throw new IllegalArgumentException("Cannot use non-number data '" + enc.getName() + "' in value expression"); } } diff --git a/core/src/main/java/com/graphhopper/search/KVStorage.java b/core/src/main/java/com/graphhopper/search/KVStorage.java index a49b51b9f4b..f9859e0fa20 100644 --- a/core/src/main/java/com/graphhopper/search/KVStorage.java +++ b/core/src/main/java/com/graphhopper/search/KVStorage.java @@ -478,6 +478,7 @@ public static class KeyValue { public static final String STREET_REF = "street_ref"; public static final String STREET_DESTINATION = "street_destination"; public static final String STREET_DESTINATION_REF = "street_destination_ref"; + public static final String MOTORWAY_JUNCTION = "motorway_junction"; public String key; public Object value; diff --git a/core/src/main/java/com/graphhopper/storage/BaseGraph.java b/core/src/main/java/com/graphhopper/storage/BaseGraph.java index 3d74aead248..aae60e2783e 100644 --- a/core/src/main/java/com/graphhopper/storage/BaseGraph.java +++ b/core/src/main/java/com/graphhopper/storage/BaseGraph.java @@ -51,7 +51,6 @@ public class BaseGraph implements Graph, Closeable { final TurnCostStorage turnCostStorage; final BitUtil bitUtil; // length | nodeA | nextNode | ... | nodeB - // as we use integer index in 'edges' area => 'geometry' area is limited to 4GB (we use pos&neg values!) private final DataAccess wayGeometry; private final Directory dir; private final int segmentSize; @@ -352,7 +351,7 @@ private void setWayGeometry_(PointList pillarNodes, long edgePointer, boolean re throw new IllegalArgumentException("Cannot use pointlist which is " + pillarNodes.getDimension() + "D for graph which is " + nodeAccess.getDimension() + "D"); - long existingGeoRef = BitUtil.toUnsignedLong(store.getGeoRef(edgePointer)); + long existingGeoRef = store.getGeoRef(edgePointer); int len = pillarNodes.size(); int dim = nodeAccess.getDimension(); @@ -367,7 +366,7 @@ private void setWayGeometry_(PointList pillarNodes, long edgePointer, boolean re long nextGeoRef = nextGeoRef(len * dim); setWayGeometryAtGeoRef(pillarNodes, edgePointer, reverse, nextGeoRef); } else { - store.setGeoRef(edgePointer, 0); + store.setGeoRef(edgePointer, 0L); } } @@ -388,14 +387,11 @@ public void setInt(int edgeId, int index, int value) { } private void setWayGeometryAtGeoRef(PointList pillarNodes, long edgePointer, boolean reverse, long geoRef) { - int len = pillarNodes.size(); - int dim = nodeAccess.getDimension(); long geoRefPosition = geoRef * 4; - int totalLen = len * dim * 4 + 4; - ensureGeometry(geoRefPosition, totalLen); byte[] wayGeometryBytes = createWayGeometryBytes(pillarNodes, reverse); + wayGeometry.ensureCapacity(geoRefPosition + wayGeometryBytes.length); wayGeometry.setBytes(geoRefPosition, wayGeometryBytes, wayGeometryBytes.length); - store.setGeoRef(edgePointer, BitUtil.toSignedInt(geoRef)); + store.setGeoRef(edgePointer, geoRef); } private byte[] createWayGeometryBytes(PointList pillarNodes, boolean reverse) { @@ -432,7 +428,7 @@ private PointList fetchWayGeometry_(long edgePointer, boolean reverse, FetchMode pillarNodes.add(nodeAccess, adjNode); return pillarNodes; } - long geoRef = BitUtil.toUnsignedLong(store.getGeoRef(edgePointer)); + long geoRef = store.getGeoRef(edgePointer); int count = 0; byte[] bytes = null; if (geoRef > 0) { @@ -492,16 +488,9 @@ static int getPointListLength(int pillarNodes, FetchMode mode) { throw new IllegalArgumentException("Mode isn't handled " + mode); } - private void ensureGeometry(long bytePos, int byteLength) { - wayGeometry.ensureCapacity(bytePos + byteLength); - } - private long nextGeoRef(int arrayLength) { long tmp = maxGeoRef; maxGeoRef += arrayLength + 1L; - if (maxGeoRef > MAX_UNSIGNED_INT) - throw new IllegalStateException("Geometry too large, does not fit in 32 bits " + maxGeoRef); - return tmp; } @@ -963,13 +952,13 @@ public EdgeIteratorState setKeyValues(List entries) { @Override public List getKeyValues() { - long kvEntryRef = BitUtil.toUnsignedLong(store.getKeyValuesRef(edgePointer)); + long kvEntryRef = Integer.toUnsignedLong(store.getKeyValuesRef(edgePointer)); return baseGraph.edgeKVStorage.getAll(kvEntryRef); } @Override public Object getValue(String key) { - long kvEntryRef = BitUtil.toUnsignedLong(store.getKeyValuesRef(edgePointer)); + long kvEntryRef = Integer.toUnsignedLong(store.getKeyValuesRef(edgePointer)); return baseGraph.edgeKVStorage.get(kvEntryRef, key, reverse); } diff --git a/core/src/main/java/com/graphhopper/storage/BaseGraphNodesAndEdges.java b/core/src/main/java/com/graphhopper/storage/BaseGraphNodesAndEdges.java index 673eda58753..0e2e6c2cc2c 100644 --- a/core/src/main/java/com/graphhopper/storage/BaseGraphNodesAndEdges.java +++ b/core/src/main/java/com/graphhopper/storage/BaseGraphNodesAndEdges.java @@ -18,10 +18,7 @@ package com.graphhopper.storage; -import com.graphhopper.util.Constants; -import com.graphhopper.util.EdgeIterator; -import com.graphhopper.util.GHUtility; -import com.graphhopper.util.Helper; +import com.graphhopper.util.*; import com.graphhopper.util.shapes.BBox; import java.util.Locale; @@ -48,7 +45,7 @@ class BaseGraphNodesAndEdges { // edges private final DataAccess edges; - private final int E_NODEA, E_NODEB, E_LINKA, E_LINKB, E_FLAGS, E_DIST, E_GEO, E_KV; + private final int E_NODEA, E_NODEB, E_LINKA, E_LINKB, E_FLAGS, E_DIST, E_GEO_1, E_GEO_2, E_KV; private final int intsForFlags; private int edgeEntryBytes; private int edgeCount; @@ -85,8 +82,9 @@ public BaseGraphNodesAndEdges(Directory dir, int intsForFlags, boolean withEleva E_LINKB = 12; E_FLAGS = 16; E_DIST = E_FLAGS + intsForFlags * 4; - E_GEO = E_DIST + 4; - E_KV = E_GEO + 4; + E_GEO_1 = E_DIST + 4; + E_GEO_2 = E_GEO_1 + 4; + E_KV = E_GEO_2 + 4; edgeEntryBytes = E_KV + 4; } @@ -282,8 +280,11 @@ public void setDist(long edgePointer, double distance) { edges.setInt(edgePointer + E_DIST, distToInt(distance)); } - public void setGeoRef(long edgePointer, int geoRef) { - edges.setInt(edgePointer + E_GEO, geoRef); + public void setGeoRef(long edgePointer, long geoRef) { + int geo1 = BitUtil.LITTLE.getIntLow(geoRef); + int geo2 = BitUtil.LITTLE.getIntHigh(geoRef); + edges.setInt(edgePointer + E_GEO_1, geo1); + edges.setInt(edgePointer + E_GEO_2, geo2); } public void setKeyValuesRef(long edgePointer, int nameRef) { @@ -312,8 +313,11 @@ public double getDist(long pointer) { return val / INT_DIST_FACTOR; } - public int getGeoRef(long edgePointer) { - return edges.getInt(edgePointer + E_GEO); + public long getGeoRef(long edgePointer) { + return BitUtil.LITTLE.toLong( + edges.getInt(edgePointer + E_GEO_1), + edges.getInt(edgePointer + E_GEO_2) + ); } public int getKeyValuesRef(long edgePointer) { diff --git a/core/src/main/java/com/graphhopper/storage/DAType.java b/core/src/main/java/com/graphhopper/storage/DAType.java index da75daf6b0d..f2d26a30f6d 100644 --- a/core/src/main/java/com/graphhopper/storage/DAType.java +++ b/core/src/main/java/com/graphhopper/storage/DAType.java @@ -80,10 +80,10 @@ else if (dataAccess.contains("MMAP")) type = DAType.MMAP; else if (dataAccess.contains("UNSAFE")) throw new IllegalArgumentException("UNSAFE option is no longer supported, see #1620"); - else if (dataAccess.contains("RAM_STORE")) - type = DAType.RAM_STORE; - else + else if (dataAccess.equals("RAM")) type = DAType.RAM; + else + type = DAType.RAM_STORE; return type; } diff --git a/core/src/main/java/com/graphhopper/storage/MMapDataAccess.java b/core/src/main/java/com/graphhopper/storage/MMapDataAccess.java index 6424f88eef1..666309c08ca 100644 --- a/core/src/main/java/com/graphhopper/storage/MMapDataAccess.java +++ b/core/src/main/java/com/graphhopper/storage/MMapDataAccess.java @@ -17,26 +17,18 @@ */ package com.graphhopper.storage; -import com.graphhopper.util.Constants; import com.graphhopper.util.Helper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; -import java.security.AccessController; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.List; -import java.util.StringTokenizer; /** * A DataAccess implementation using a memory-mapped file, i.e. a facility of the @@ -56,8 +48,6 @@ */ public final class MMapDataAccess extends AbstractDataAccess { - private static final Logger LOGGER = LoggerFactory.getLogger(MMapDataAccess.class); - private final boolean allowWrites; private RandomAccessFile raFile; private final List segments = new ArrayList<>(); @@ -67,96 +57,29 @@ public final class MMapDataAccess extends AbstractDataAccess { this.allowWrites = allowWrites; } - public static boolean jreIsMinimumJava9() { - final StringTokenizer st = new StringTokenizer(System.getProperty("java.specification.version"), "."); - int JVM_MAJOR_VERSION = Integer.parseInt(st.nextToken()); - int JVM_MINOR_VERSION; - if (st.hasMoreTokens()) { - JVM_MINOR_VERSION = Integer.parseInt(st.nextToken()); - } else { - JVM_MINOR_VERSION = 0; - } - return JVM_MAJOR_VERSION > 1 || (JVM_MAJOR_VERSION == 1 && JVM_MINOR_VERSION >= 9); - } - public static void cleanMappedByteBuffer(final ByteBuffer buffer) { // TODO avoid reflection on every call try { - AccessController.doPrivileged(new PrivilegedExceptionAction() { - @Override - public Object run() throws Exception { - if (jreIsMinimumJava9()) { - // >=JDK9 class sun.misc.Unsafe { void invokeCleaner(ByteBuffer buf) } - final Class unsafeClass = Class.forName("sun.misc.Unsafe"); - // fetch the unsafe instance and bind it to the virtual MethodHandle - final Field f = unsafeClass.getDeclaredField("theUnsafe"); - f.setAccessible(true); - final Object theUnsafe = f.get(null); - final Method method = unsafeClass.getDeclaredMethod("invokeCleaner", ByteBuffer.class); - try { - method.invoke(theUnsafe, buffer); - return null; - } catch (Throwable t) { - throw new RuntimeException(t); - } - } - - if (buffer.getClass().getSimpleName().equals("MappedByteBufferAdapter")) { - if (!Constants.ANDROID) - throw new RuntimeException("MappedByteBufferAdapter only supported for Android at the moment"); - - // For Android 4.1 call ((MappedByteBufferAdapter)buffer).free() see #914 - Class directByteBufferClass = Class.forName("java.nio.MappedByteBufferAdapter"); - callBufferFree(buffer, directByteBufferClass); - } else { - // <=JDK8 class DirectByteBuffer { sun.misc.Cleaner cleaner(Buffer buf) } - // then call sun.misc.Cleaner.clean - final Class directByteBufferClass = Class.forName("java.nio.DirectByteBuffer"); - try { - final Method dbbCleanerMethod = directByteBufferClass.getMethod("cleaner"); - dbbCleanerMethod.setAccessible(true); - // call: cleaner = ((DirectByteBuffer)buffer).cleaner() - final Object cleaner = dbbCleanerMethod.invoke(buffer); - if (cleaner != null) { - final Class cleanerMethodReturnType = dbbCleanerMethod.getReturnType(); - final Method cleanMethod = cleanerMethodReturnType.getDeclaredMethod("clean"); - cleanMethod.setAccessible(true); - // call: ((sun.misc.Cleaner)cleaner).clean() - cleanMethod.invoke(cleaner); - } - } catch (NoSuchMethodException ex2) { - if (Constants.ANDROID) - // For Android 5.1.1 call ((DirectByteBuffer)buffer).free() see #933 - callBufferFree(buffer, directByteBufferClass); - else - // ignore if method cleaner or clean is not available - LOGGER.warn("NoSuchMethodException | " + System.getProperty("java.version"), ex2); - } - } - - return null; - } - }); - } catch (PrivilegedActionException e) { - throw new RuntimeException("Unable to unmap the mapped buffer", e); - } - } - - private static void callBufferFree(ByteBuffer buffer, Class directByteBufferClass) - throws InvocationTargetException, IllegalAccessException { - try { - final Method dbbFreeMethod = directByteBufferClass.getMethod("free"); - dbbFreeMethod.setAccessible(true); - dbbFreeMethod.invoke(buffer); - } catch (NoSuchMethodException ex2) { - LOGGER.warn("NoSuchMethodException | " + System.getProperty("java.version"), ex2); + // >=JDK9 class sun.misc.Unsafe { void invokeCleaner(ByteBuffer buf) } + final Class unsafeClass = Class.forName("sun.misc.Unsafe"); + // fetch the unsafe instance and bind it to the virtual MethodHandle + final Field f = unsafeClass.getDeclaredField("theUnsafe"); + f.setAccessible(true); + final Object theUnsafe = f.get(null); + final Method method = unsafeClass.getDeclaredMethod("invokeCleaner", ByteBuffer.class); + try { + method.invoke(theUnsafe, buffer); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } catch (Exception ex) { + throw new RuntimeException("Unable to unmap the mapped buffer", ex); } } private void initRandomAccessFile() { - if (raFile != null) { + if (raFile != null) return; - } try { // raFile necessary for loadExisting and create @@ -328,65 +251,50 @@ public void close() { } @Override - public final void setInt(long bytePos, int value) { + public void setInt(long bytePos, int value) { int bufferIndex = (int) (bytePos >> segmentSizePower); int index = (int) (bytePos & indexDivisor); if (index + 4 > segmentSizeInBytes) throw new IllegalStateException("Padding required. Currently an int cannot be distributed over two segments. " + bytePos); ByteBuffer byteBuffer = segments.get(bufferIndex); - synchronized (byteBuffer) { - byteBuffer.putInt(index, value); - } + byteBuffer.putInt(index, value); } @Override - public final int getInt(long bytePos) { + public int getInt(long bytePos) { int bufferIndex = (int) (bytePos >> segmentSizePower); int index = (int) (bytePos & indexDivisor); if (index + 4 > segmentSizeInBytes) throw new IllegalStateException("Padding required. Currently an int cannot be distributed over two segments. " + bytePos); ByteBuffer byteBuffer = segments.get(bufferIndex); - synchronized (byteBuffer) { - return byteBuffer.getInt(index); - } + return byteBuffer.getInt(index); } @Override - public final void setShort(long bytePos, short value) { + public void setShort(long bytePos, short value) { int bufferIndex = (int) (bytePos >>> segmentSizePower); int index = (int) (bytePos & indexDivisor); ByteBuffer byteBuffer = segments.get(bufferIndex); - synchronized (byteBuffer) { - if (index + 2 > segmentSizeInBytes) { - ByteBuffer byteBufferNext = segments.get(bufferIndex + 1); - synchronized (byteBufferNext) { - // special case if short has to be written into two separate segments - byteBuffer.put(index, (byte) value); - byteBufferNext.put(0, (byte) (value >>> 8)); - } - } else { - byteBuffer.putShort(index, value); - } + if (index + 2 > segmentSizeInBytes) { + ByteBuffer byteBufferNext = segments.get(bufferIndex + 1); + // special case if short has to be written into two separate segments + byteBuffer.put(index, (byte) value); + byteBufferNext.put(0, (byte) (value >>> 8)); + } else { + byteBuffer.putShort(index, value); } } @Override - public final short getShort(long bytePos) { + public short getShort(long bytePos) { int bufferIndex = (int) (bytePos >>> segmentSizePower); int index = (int) (bytePos & indexDivisor); ByteBuffer byteBuffer = segments.get(bufferIndex); if (index + 2 > segmentSizeInBytes) { ByteBuffer byteBufferNext = segments.get(bufferIndex + 1); - // never lock byteBuffer and byteBufferNext in a different order to avoid deadlocks (shouldn't happen) - synchronized (byteBuffer) { - synchronized (byteBufferNext) { - return (short) ((byteBufferNext.get(0) & 0xFF) << 8 | byteBuffer.get(index) & 0xFF); - } - } - } - synchronized (byteBuffer) { - return byteBuffer.getShort(index); + return (short) ((byteBufferNext.get(0) & 0xFF) << 8 | byteBuffer.get(index) & 0xFF); } + return byteBuffer.getShort(index); } @Override @@ -396,21 +304,15 @@ public void setBytes(long bytePos, byte[] values, int length) { final int index = (int) (bytePos & indexDivisor); final int delta = index + length - segmentSizeInBytes; final ByteBuffer bb1 = segments.get(bufferIndex); - synchronized (bb1) { - bb1.position(index); - if (delta > 0) { - length -= delta; - bb1.put(values, 0, length); - } else { - bb1.put(values, 0, length); - } + if (delta > 0) { + length -= delta; + bb1.put(index, values, 0, length); + } else { + bb1.put(index, values, 0, length); } if (delta > 0) { final ByteBuffer bb2 = segments.get(bufferIndex + 1); - synchronized (bb2) { - bb2.position(0); - bb2.put(values, length, delta); - } + bb2.put(0, values, length, delta); } } @@ -421,21 +323,14 @@ public void getBytes(long bytePos, byte[] values, int length) { int index = (int) (bytePos & indexDivisor); int delta = index + length - segmentSizeInBytes; final ByteBuffer bb1 = segments.get(bufferIndex); - synchronized (bb1) { - bb1.position(index); - if (delta > 0) { - length -= delta; - bb1.get(values, 0, length); - } else { - bb1.get(values, 0, length); - } - } if (delta > 0) { + length -= delta; + bb1.get(index, values, 0, length); + final ByteBuffer bb2 = segments.get(bufferIndex + 1); - synchronized (bb2) { - bb2.position(0); - bb2.get(values, length, delta); - } + bb2.get(0, values, length, delta); + } else { + bb1.get(index, values, 0, length); } } @@ -444,10 +339,7 @@ public void setByte(long bytePos, byte value) { int bufferIndex = (int) (bytePos >>> segmentSizePower); int index = (int) (bytePos & indexDivisor); final ByteBuffer bb1 = segments.get(bufferIndex); - synchronized (bb1) { - bb1.position(index); - bb1.put(value); - } + bb1.put(index, value); } @Override @@ -455,19 +347,14 @@ public byte getByte(long bytePos) { int bufferIndex = (int) (bytePos >>> segmentSizePower); int index = (int) (bytePos & indexDivisor); final ByteBuffer bb1 = segments.get(bufferIndex); - synchronized (bb1) { - bb1.position(index); - return bb1.get(); - } + return bb1.get(index); } @Override public long getCapacity() { long cap = 0; for (ByteBuffer bb : segments) { - synchronized (bb) { - cap += bb.capacity(); - } + cap += bb.capacity(); } return cap; } diff --git a/core/src/main/java/com/graphhopper/storage/RAMDataAccess.java b/core/src/main/java/com/graphhopper/storage/RAMDataAccess.java index 49918b85ce4..7fb11d1395c 100644 --- a/core/src/main/java/com/graphhopper/storage/RAMDataAccess.java +++ b/core/src/main/java/com/graphhopper/storage/RAMDataAccess.java @@ -24,7 +24,7 @@ /** * This is an in-memory byte-based data structure with the possibility to be stored on flush(). - * Thread safe. + * Read thread-safe. *

* * @author Peter Karich diff --git a/core/src/main/java/com/graphhopper/storage/StorableProperties.java b/core/src/main/java/com/graphhopper/storage/StorableProperties.java index 73519d33a94..374a6511272 100644 --- a/core/src/main/java/com/graphhopper/storage/StorableProperties.java +++ b/core/src/main/java/com/graphhopper/storage/StorableProperties.java @@ -38,8 +38,10 @@ public class StorableProperties { private final Map map = new LinkedHashMap<>(); private final DataAccess da; + private final Directory dir; public StorableProperties(Directory dir) { + this.dir = dir; // reduce size int segmentSize = 1 << 15; this.da = dir.create("properties", segmentSize); @@ -68,6 +70,12 @@ public synchronized void flush() { byte[] bytes = sw.toString().getBytes(UTF_CS); da.setBytes(0, bytes, bytes.length); da.flush(); + // todo: would not be needed if the properties file used a format that is compatible with common text tools + if (dir.getDefaultType().isStoring()) { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(dir.getLocation() + "/properties.txt"))) { + writer.write(sw.toString()); + } + } } catch (IOException ex) { throw new RuntimeException(ex); } diff --git a/core/src/main/java/com/graphhopper/storage/index/LineIntIndex.java b/core/src/main/java/com/graphhopper/storage/index/LineIntIndex.java index e818bdc1df3..6a4b3bdc769 100644 --- a/core/src/main/java/com/graphhopper/storage/index/LineIntIndex.java +++ b/core/src/main/java/com/graphhopper/storage/index/LineIntIndex.java @@ -159,8 +159,12 @@ private void fillIDs(long keyPart, IntConsumer consumer) { } public void query(BBox queryShape, final LocationIndex.Visitor function) { + query(LocationIndex.createBBoxTileFilter(queryShape), function); + } + + public void query(LocationIndex.TileFilter tileFilter, final LocationIndex.Visitor function) { final IntHashSet set = new IntHashSet(); - query(START_POINTER, queryShape, + query(START_POINTER, tileFilter, bounds.minLat, bounds.minLon, bounds.maxLat - bounds.minLat, bounds.maxLon - bounds.minLon, new LocationIndex.Visitor() { @Override @@ -181,7 +185,7 @@ public void onEdge(int edgeId) { }, 0); } - private void query(int intPointer, BBox queryBBox, + private void query(int intPointer, LocationIndex.TileFilter tileFilter, double minLat, double minLon, double deltaLatPerDepth, double deltaLonPerDepth, LocationIndex.Visitor function, int depth) { @@ -213,14 +217,14 @@ private void query(int intPointer, BBox queryBBox, double tmpMinLon = minLon + deltaLonPerDepth * pixelXY[0]; double tmpMinLat = minLat + deltaLatPerDepth * pixelXY[1]; - BBox bbox = (queryBBox != null || function.isTileInfo()) ? new BBox(tmpMinLon, tmpMinLon + deltaLonPerDepth, tmpMinLat, tmpMinLat + deltaLatPerDepth) : null; + BBox bbox = (tileFilter != null || function.isTileInfo()) ? new BBox(tmpMinLon, tmpMinLon + deltaLonPerDepth, tmpMinLat, tmpMinLat + deltaLatPerDepth) : null; if (function.isTileInfo()) function.onTile(bbox, depth); - if (queryBBox == null || queryBBox.contains(bbox)) { + if (tileFilter == null || tileFilter.acceptAll(bbox)) { // fill without a restriction! query(nextIntPointer, null, tmpMinLat, tmpMinLon, deltaLatPerDepth, deltaLonPerDepth, function, depth + 1); - } else if (queryBBox.intersects(bbox)) { - query(nextIntPointer, queryBBox, tmpMinLat, tmpMinLon, deltaLatPerDepth, deltaLonPerDepth, function, depth + 1); + } else if (tileFilter.acceptPartially(bbox)) { + query(nextIntPointer, tileFilter, tmpMinLat, tmpMinLon, deltaLatPerDepth, deltaLonPerDepth, function, depth + 1); } } } diff --git a/core/src/main/java/com/graphhopper/storage/index/LocationIndex.java b/core/src/main/java/com/graphhopper/storage/index/LocationIndex.java index 2c49a7ec7cb..d023c4b2d8b 100644 --- a/core/src/main/java/com/graphhopper/storage/index/LocationIndex.java +++ b/core/src/main/java/com/graphhopper/storage/index/LocationIndex.java @@ -51,10 +51,44 @@ public interface LocationIndex { * and limited by the queryBBox. Also (a few) more edges slightly outside of queryBBox could be * returned that you can avoid via doing an explicit BBox check of the coordinates. */ - void query(BBox queryBBox, Visitor function); + default void query(BBox queryBBox, Visitor function) { + query(createBBoxTileFilter(queryBBox), function); + } + + void query(TileFilter tileFilter, Visitor function); void close(); + + interface TileFilter { + + /** + * @return true if all edges within the given bounding box shall be accepted + */ + boolean acceptAll(BBox tile); + + /** + * @return true if edges within the given bounding box shall potentially be accepted. In this + * case the tile filter will be applied again for smaller bounding boxes on a lower level. + * If this is the lowest level already simply all edges will be accepted. + */ + boolean acceptPartially(BBox tile); + } + + static TileFilter createBBoxTileFilter(BBox queryBBox) { + return queryBBox == null ? null : new TileFilter() { + @Override + public boolean acceptAll(BBox tile) { + return queryBBox.contains(tile); + } + + @Override + public boolean acceptPartially(BBox tile) { + return queryBBox.intersects(tile); + } + }; + } + /** * This interface allows to visit edges stored in the LocationIndex. */ diff --git a/core/src/main/java/com/graphhopper/storage/index/LocationIndexTree.java b/core/src/main/java/com/graphhopper/storage/index/LocationIndexTree.java index 9243ba7db73..f8ff3620d55 100644 --- a/core/src/main/java/com/graphhopper/storage/index/LocationIndexTree.java +++ b/core/src/main/java/com/graphhopper/storage/index/LocationIndexTree.java @@ -309,8 +309,8 @@ public Snap findClosest(final double queryLat, final double queryLon, final Edge } @Override - public void query(BBox queryBBox, Visitor function) { - lineIntIndex.query(queryBBox, function); + public void query(TileFilter tileFilter, Visitor function) { + lineIntIndex.query(tileFilter, function); } public interface EdgeCheck { diff --git a/core/src/main/java/com/graphhopper/util/BitUtil.java b/core/src/main/java/com/graphhopper/util/BitUtil.java index 0fb7a0d1e1f..fadc17f68e7 100644 --- a/core/src/main/java/com/graphhopper/util/BitUtil.java +++ b/core/src/main/java/com/graphhopper/util/BitUtil.java @@ -269,15 +269,7 @@ public static int countBitValue(int maxTurnCosts) { } /** - * This method handles the specified (potentially negative) int as unsigned bit representation - * and returns the positive converted long. - */ - public static long toUnsignedLong(int x) { - return ((long) x) & 0xFFFF_FFFFL; - } - - /** - * Converts the specified long back into a signed int (reverse method for toUnsignedLong) + * Converts the specified long into a signed int ('reverse' method for Integer.toUnsignedLong). */ public static int toSignedInt(long x) { return (int) x; diff --git a/core/src/main/java/com/graphhopper/util/Constants.java b/core/src/main/java/com/graphhopper/util/Constants.java index f5cfcfba0f8..87f6f63d990 100644 --- a/core/src/main/java/com/graphhopper/util/Constants.java +++ b/core/src/main/java/com/graphhopper/util/Constants.java @@ -42,18 +42,10 @@ public class Constants { * True iff running on Linux. */ public static final boolean LINUX = OS_NAME.startsWith("Linux"); - /** - * True iff running on Android. - */ - public static final boolean ANDROID = System.getProperty("java.vendor").contains("Android"); /** * True iff running on Windows. */ public static final boolean WINDOWS = OS_NAME.startsWith("Windows"); - /** - * True iff running on SunOS. - */ - public static final boolean SUN_OS = OS_NAME.startsWith("SunOS"); /** * True iff running on Mac OS X */ @@ -67,7 +59,7 @@ public class Constants { private static final int JVM_MINOR_VERSION; public static final int VERSION_NODE = 9; - public static final int VERSION_EDGE = 21; + public static final int VERSION_EDGE = 22; // this should be increased whenever the format of the serialized EncodingManager is changed public static final int VERSION_EM = 3; public static final int VERSION_SHORTCUT = 9; diff --git a/core/src/main/java/com/graphhopper/util/GHUtility.java b/core/src/main/java/com/graphhopper/util/GHUtility.java index e43ef73a4e1..c4bad9b0230 100644 --- a/core/src/main/java/com/graphhopper/util/GHUtility.java +++ b/core/src/main/java/com/graphhopper/util/GHUtility.java @@ -21,8 +21,7 @@ import com.carrotsearch.hppc.IntArrayList; import com.carrotsearch.hppc.IntIndexedContainer; import com.fasterxml.jackson.databind.ObjectMapper; -import com.graphhopper.coll.GHBitSet; -import com.graphhopper.coll.GHBitSetImpl; +import com.graphhopper.jackson.Jackson; import com.graphhopper.routing.Path; import com.graphhopper.routing.ev.BooleanEncodedValue; import com.graphhopper.routing.ev.Country; @@ -33,7 +32,10 @@ import com.graphhopper.routing.util.CustomArea; import com.graphhopper.routing.util.EdgeFilter; import com.graphhopper.routing.weighting.Weighting; -import com.graphhopper.storage.*; +import com.graphhopper.storage.Graph; +import com.graphhopper.storage.NodeAccess; +import com.graphhopper.storage.RoutingCHEdgeIterator; +import com.graphhopper.storage.TurnCostStorage; import com.graphhopper.storage.index.LocationIndex; import com.graphhopper.storage.index.Snap; import com.graphhopper.util.shapes.BBox; @@ -43,21 +45,18 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.UncheckedIOException; +import java.io.*; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; -import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; import static com.graphhopper.routing.ev.State.ISO_3166_2; import static com.graphhopper.util.DistanceCalcEarth.DIST_EARTH; +import static com.graphhopper.util.Helper.readJSONFileWithoutComments; /** * A helper class to avoid cluttering the Graph interface with all the common methods. Most of the @@ -311,124 +310,6 @@ public static double randomDoubleInRange(Random rnd, double min, double max) { return min + rnd.nextDouble() * (max - min); } - public static Graph shuffle(Graph g, Graph sortedGraph) { - if (g.getTurnCostStorage() != null) - throw new IllegalArgumentException("Shuffling the graph is currently not supported in the presence of turn costs"); - IntArrayList nodes = ArrayUtil.permutation(g.getNodes(), new Random()); - IntArrayList edges = ArrayUtil.permutation(g.getEdges(), new Random()); - return createSortedGraph(g, sortedGraph, nodes, edges); - } - - /** - * Sorts the graph according to depth-first search traversal. Other traversals have either no - * significant difference (bfs) for querying or are worse (z-curve). - */ - public static Graph sortDFS(Graph g, Graph sortedGraph) { - if (g.getTurnCostStorage() != null) { - // not only would we have to sort the turn cost storage when we re-sort the graph, but we'd also have to make - // sure that the location index always snaps to real edges and not the corresponding artificial edge that we - // introduced to deal with via-way restrictions. Without sorting this works automatically because the real - // edges use lower edge ids. Otherwise we'd probably have to use some kind of is_artificial flag for each - // edge. - throw new IllegalArgumentException("Sorting the graph is currently not supported in the presence of turn costs"); - } - int nodes = g.getNodes(); - final IntArrayList nodeList = ArrayUtil.constant(nodes, -1); - final GHBitSetImpl nodeBitset = new GHBitSetImpl(nodes); - final AtomicInteger nodeRef = new AtomicInteger(-1); - - int edges = g.getEdges(); - final IntArrayList edgeList = ArrayUtil.constant(edges, -1); - final GHBitSetImpl edgeBitset = new GHBitSetImpl(edges); - final AtomicInteger edgeRef = new AtomicInteger(-1); - - EdgeExplorer explorer = g.createEdgeExplorer(); - for (int startNode = 0; startNode >= 0 && startNode < nodes; - startNode = nodeBitset.nextClear(startNode + 1)) { - new DepthFirstSearch() { - @Override - protected GHBitSet createBitSet() { - return nodeBitset; - } - - @Override - protected boolean checkAdjacent(EdgeIteratorState edge) { - int edgeId = edge.getEdge(); - if (!edgeBitset.contains(edgeId)) { - edgeBitset.add(edgeId); - edgeList.set(edgeRef.incrementAndGet(), edgeId); - } - return super.checkAdjacent(edge); - } - - @Override - protected boolean goFurther(int nodeId) { - nodeList.set(nodeId, nodeRef.incrementAndGet()); - return super.goFurther(nodeId); - } - }.start(explorer, startNode); - } - return createSortedGraph(g, sortedGraph, nodeList, edgeList); - } - - static Graph createSortedGraph(Graph fromGraph, Graph toSortedGraph, final IntIndexedContainer oldToNewNodeList, final IntIndexedContainer newToOldEdgeList) { - if (fromGraph.getTurnCostStorage() != null) { - throw new IllegalArgumentException("Sorting the graph is currently not supported in the presence of turn costs"); - } - int edges = fromGraph.getEdges(); - for (int i = 0; i < edges; i++) { - int edgeId = newToOldEdgeList.get(i); - if (edgeId < 0) - continue; - - EdgeIteratorState eIter = fromGraph.getEdgeIteratorState(edgeId, Integer.MIN_VALUE); - - int base = eIter.getBaseNode(); - int newBaseIndex = oldToNewNodeList.get(base); - int adj = eIter.getAdjNode(); - int newAdjIndex = oldToNewNodeList.get(adj); - - // ignore empty entries - if (newBaseIndex < 0 || newAdjIndex < 0) - continue; - - toSortedGraph.edge(newBaseIndex, newAdjIndex).copyPropertiesFrom(eIter); - } - - int nodes = fromGraph.getNodes(); - NodeAccess na = fromGraph.getNodeAccess(); - NodeAccess sna = toSortedGraph.getNodeAccess(); - for (int old = 0; old < nodes; old++) { - int newIndex = oldToNewNodeList.get(old); - if (sna.is3D()) - sna.setNode(newIndex, na.getLat(old), na.getLon(old), na.getEle(old)); - else - sna.setNode(newIndex, na.getLat(old), na.getLon(old)); - } - return toSortedGraph; - } - - static Directory guessDirectory(BaseGraph graph) { - if (graph.getDirectory() instanceof MMapDirectory) { - throw new IllegalStateException("not supported yet: mmap will overwrite existing storage at the same location"); - } - String location = graph.getDirectory().getLocation(); - boolean isStoring = ((GHDirectory) graph.getDirectory()).isStoring(); - return new RAMDirectory(location, isStoring); - } - - /** - * Create a new storage from the specified one without copying the data. CHGraphs won't be copied. - */ - public static BaseGraph newGraph(BaseGraph baseGraph) { - Directory outdir = guessDirectory(baseGraph); - return new BaseGraph.Builder(baseGraph.getIntsForFlags()) - .withTurnCosts(baseGraph.getTurnCostStorage() != null) - .set3D(baseGraph.getNodeAccess().is3D()) - .setDir(outdir) - .create(); - } - public static int getAdjNode(Graph g, int edge, int adjNode) { if (EdgeIterator.Edge.isValid(edge)) { EdgeIteratorState iterTo = g.getEdgeIteratorState(edge, adjNode); @@ -751,4 +632,17 @@ private static double getMinDist(Graph graph, int p, int q) { private static void fail(String message) { throw new AssertionError(message); } + + public static CustomModel loadCustomModelFromJar(String name) { + try { + InputStream is = GHUtility.class.getResourceAsStream("/com/graphhopper/custom_models/" + name); + if (is == null) + throw new IllegalArgumentException("There is no built-in custom model '" + name + "'"); + String json = readJSONFileWithoutComments(new InputStreamReader(is)); + ObjectMapper objectMapper = Jackson.newObjectMapper(); + return objectMapper.readValue(json, CustomModel.class); + } catch (IOException e) { + throw new IllegalArgumentException("Could not load built-in custom model '" + name + "'"); + } + } } diff --git a/core/src/main/java/com/graphhopper/util/GitInfo.java b/core/src/main/java/com/graphhopper/util/GitInfo.java index b7cbef6f4d2..36708c249d4 100644 --- a/core/src/main/java/com/graphhopper/util/GitInfo.java +++ b/core/src/main/java/com/graphhopper/util/GitInfo.java @@ -56,6 +56,6 @@ public boolean isDirty() { } public String toString() { - return Helper.join("|", Arrays.asList(commitHash, branch, "dirty=" + dirty, commitTime, commitMessage)); + return String.join("|", Arrays.asList(commitHash, branch, "dirty=" + dirty, commitTime, commitMessage)); } } diff --git a/core/src/main/java/com/graphhopper/util/details/ConstantDetailsBuilder.java b/core/src/main/java/com/graphhopper/util/details/ConstantDetailsBuilder.java index d308e22acb7..6c240a778e3 100644 --- a/core/src/main/java/com/graphhopper/util/details/ConstantDetailsBuilder.java +++ b/core/src/main/java/com/graphhopper/util/details/ConstantDetailsBuilder.java @@ -18,8 +18,13 @@ package com.graphhopper.util.details; +import com.graphhopper.coll.MapEntry; import com.graphhopper.util.EdgeIteratorState; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + /** * Simply returns the same value everywhere, useful to represent values that are the same between two (via-)points */ @@ -45,4 +50,12 @@ public boolean isEdgeDifferentToLastEdge(EdgeIteratorState edge) { } else return false; } + + @Override + public Map.Entry> build() { + if (firstEdge) + // #2915 if there was no edge at all we need to add a single entry manually here + return new MapEntry<>(getName(), new ArrayList<>(List.of(new PathDetail(value)))); + return super.build(); + } } diff --git a/core/src/main/java/com/graphhopper/util/details/IntersectionDetails.java b/core/src/main/java/com/graphhopper/util/details/IntersectionDetails.java index a42fe2f949b..7528f5b2142 100644 --- a/core/src/main/java/com/graphhopper/util/details/IntersectionDetails.java +++ b/core/src/main/java/com/graphhopper/util/details/IntersectionDetails.java @@ -17,15 +17,25 @@ */ package com.graphhopper.util.details; +import static com.graphhopper.util.Parameters.Details.INTERSECTION; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + import com.graphhopper.routing.querygraph.VirtualEdgeIteratorState; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.storage.Graph; import com.graphhopper.storage.NodeAccess; -import com.graphhopper.util.*; - -import java.util.*; - -import static com.graphhopper.util.Parameters.Details.INTERSECTION; +import com.graphhopper.util.AngleCalc; +import com.graphhopper.util.EdgeExplorer; +import com.graphhopper.util.EdgeIterator; +import com.graphhopper.util.EdgeIteratorState; +import com.graphhopper.util.FetchMode; +import com.graphhopper.util.GHUtility; +import com.graphhopper.util.PointList; /** * Calculate the intersections for a route. Every change of the edge id is considered an intersection. @@ -44,7 +54,7 @@ public class IntersectionDetails extends AbstractPathDetailsBuilder { private int fromEdge = -1; - private Map intersectionMap = new HashMap<>(); + private Map intersectionMap = null; private final EdgeExplorer crossingExplorer; private final NodeAccess nodeAccess; @@ -89,25 +99,10 @@ public boolean isEdgeDifferentToLastEdge(EdgeIteratorState edge) { intersectingEdges.add(intersectionValues); } - intersectingEdges.sort(null); - - List bearings = new ArrayList<>(intersectingEdges.size()); - List entries = new ArrayList<>(intersectingEdges.size()); - - for (int i = 0; i < intersectingEdges.size(); i++) { - IntersectionValues intersectionValues = intersectingEdges.get(i); - bearings.add(intersectionValues.bearing); - entries.add(intersectionValues.entry); - if (intersectionValues.in) { - intersectionMap.put("in", i); - } - if (intersectionValues.out) { - intersectionMap.put("out", i); - } - } + intersectingEdges = intersectingEdges.stream(). + sorted((x, y) -> Integer.compare(x.bearing, y.bearing)).collect(Collectors.toList()); - intersectionMap.put("bearings", bearings); - intersectionMap.put("entries", entries); + intersectionMap = IntersectionValues.createIntersection(intersectingEdges); fromEdge = toEdge; return true; @@ -134,20 +129,4 @@ private int edgeId(EdgeIteratorState edge) { public Object getCurrentValue() { return this.intersectionMap; } - - private class IntersectionValues implements Comparable { - - public int bearing; - public boolean entry; - public boolean in; - public boolean out; - - @Override - public int compareTo(Object o) { - if (o instanceof IntersectionValues) { - return Integer.compare(this.bearing, ((IntersectionValues) o).bearing); - } - return 0; - } - } } diff --git a/core/src/main/java/com/graphhopper/util/details/IntersectionValues.java b/core/src/main/java/com/graphhopper/util/details/IntersectionValues.java new file mode 100644 index 00000000000..59f897d84e8 --- /dev/null +++ b/core/src/main/java/com/graphhopper/util/details/IntersectionValues.java @@ -0,0 +1,82 @@ +/* + * 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.util.details; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class IntersectionValues { + public int bearing; + public boolean entry; + public boolean in; + public boolean out; + + /** + * create a List of IntersectionValues from a PathDetail + */ + public static List createList(Map intersectionMap) { + List list = new ArrayList<>(); + + List bearings = (List) intersectionMap.get("bearings"); + Integer in = (Integer) intersectionMap.get("in"); + Integer out = (Integer) intersectionMap.get("out"); + List entry = (List) intersectionMap.get("entries"); + + if (bearings.size() != entry.size()) { + throw new IllegalStateException("Bearings and entry array sizes different"); + } + int numEntries = bearings.size(); + + for (int i = 0; i < numEntries; i++) { + IntersectionValues iv = new IntersectionValues(); + iv.bearing = bearings.get(i); + iv.entry = entry.get(i); + iv.in = (in == i); + iv.out = (out == i); + + list.add(iv); + } + return list; + } + + /** + * create a PathDetail from a List of IntersectionValues + */ + public static Map createIntersection(List list) { + Map intersection = new HashMap<>(); + + intersection.put("bearings", + list.stream().map(x -> x.bearing).collect(Collectors.toList())); + intersection.put("entries", + list.stream().map(x -> x.entry).collect(Collectors.toList())); + + for (int m = 0; m < list.size(); m++) { + IntersectionValues intersectionValues = list.get(m); + if (intersectionValues.in) { + intersection.put("in", m); + } + if (intersectionValues.out) { + intersection.put("out", m); + } + } + return intersection; + } +} diff --git a/core/src/main/java/com/graphhopper/util/details/KVStringDetails.java b/core/src/main/java/com/graphhopper/util/details/KVStringDetails.java index a599df1fde4..d930b40ba1b 100644 --- a/core/src/main/java/com/graphhopper/util/details/KVStringDetails.java +++ b/core/src/main/java/com/graphhopper/util/details/KVStringDetails.java @@ -27,6 +27,7 @@ public class KVStringDetails extends AbstractPathDetailsBuilder { private String curString; + private boolean initial = true; public KVStringDetails(String name) { super(name); @@ -34,13 +35,17 @@ public KVStringDetails(String name) { @Override public boolean isEdgeDifferentToLastEdge(EdgeIteratorState edge) { - if (curString == null) { - curString = (String) edge.getValue(getName()); + String value = (String) edge.getValue(getName()); + if (initial) { + curString = value; + initial = false; return true; - } - String val = (String) edge.getValue(getName()); - if (!curString.equals(val)) { - curString = val; + } else if (curString == null) { + curString = value; + // do not create separate details if value stays null + return value != null; + } else if (!curString.equals(value)) { + curString = value; return true; } return false; diff --git a/core/src/main/java/com/graphhopper/util/details/PathDetailsBuilderFactory.java b/core/src/main/java/com/graphhopper/util/details/PathDetailsBuilderFactory.java index 6e456a72876..a2dd1d9ff05 100644 --- a/core/src/main/java/com/graphhopper/util/details/PathDetailsBuilderFactory.java +++ b/core/src/main/java/com/graphhopper/util/details/PathDetailsBuilderFactory.java @@ -44,6 +44,13 @@ public List createPathDetailsBuilders(List requested if (requestedPathDetails.contains(LEG_WEIGHT)) builders.add(new ConstantDetailsBuilder(LEG_WEIGHT, path.getWeight())); + for (String key : requestedPathDetails) { + if (key.endsWith("_conditional")) + builders.add(new KVStringDetails(key)); + } + + if (requestedPathDetails.contains(MOTORWAY_JUNCTION)) + builders.add(new KVStringDetails(MOTORWAY_JUNCTION)); if (requestedPathDetails.contains(STREET_NAME)) builders.add(new KVStringDetails(STREET_NAME)); if (requestedPathDetails.contains(STREET_REF)) @@ -73,7 +80,8 @@ public List createPathDetailsBuilders(List requested builders.add(new IntersectionDetails(graph, weighting)); for (String pathDetail : requestedPathDetails) { - if (!evl.hasEncodedValue(pathDetail)) continue; // path details like "time" won't be found + if (!evl.hasEncodedValue(pathDetail)) + continue; // path details like "time" won't be found EncodedValue ev = evl.getEncodedValue(pathDetail, EncodedValue.class); if (ev instanceof DecimalEncodedValue) @@ -86,7 +94,8 @@ else if (ev instanceof StringEncodedValue) builders.add(new StringDetails(pathDetail, (StringEncodedValue) ev)); else if (ev instanceof IntEncodedValue) builders.add(new IntDetails(pathDetail, (IntEncodedValue) ev)); - else throw new IllegalArgumentException("unknown EncodedValue class " + ev.getClass().getName()); + else + throw new IllegalArgumentException("unknown EncodedValue class " + ev.getClass().getName()); } if (requestedPathDetails.size() > builders.size()) { diff --git a/core/src/main/resources/com/graphhopper/custom_models/bike.json b/core/src/main/resources/com/graphhopper/custom_models/bike.json index 74a81723925..8de26dbbbd0 100644 --- a/core/src/main/resources/com/graphhopper/custom_models/bike.json +++ b/core/src/main/resources/com/graphhopper/custom_models/bike.json @@ -1,11 +1,7 @@ // to use this custom model you need to set the following option in the config.yml -// graph.vehicles: roads,bike -// graph.encoded_values: average_slope,max_slope // graph.elevation.provider: srtm # enables elevation // profiles: // - name: bike -// vehicle: roads -// weighting: custom // custom_model_files: [bike.json, bike_elevation.json] { @@ -18,4 +14,4 @@ { "if": "true", "limit_to": "bike_average_speed" }, { "if": "!bike_access && backward_bike_access && !roundabout", "limit_to": "5" } ] -} \ No newline at end of file +} diff --git a/core/src/main/resources/com/graphhopper/custom_models/bike_elevation.json b/core/src/main/resources/com/graphhopper/custom_models/bike_elevation.json index 75e2bf4df96..8778361cd54 100644 --- a/core/src/main/resources/com/graphhopper/custom_models/bike_elevation.json +++ b/core/src/main/resources/com/graphhopper/custom_models/bike_elevation.json @@ -1,7 +1,7 @@ { "speed": [ { "if": "average_slope >= 15", "limit_to": "3"}, - { "if": "average_slope >= 12", "limit_to": "6"}, + { "else_if": "average_slope >= 12", "limit_to": "6"}, { "else_if": "average_slope >= 8", "multiply_by": "0.80"}, { "else_if": "average_slope >= 4", "multiply_by": "0.90"}, { "else_if": "average_slope <= -4", "multiply_by": "1.10"} diff --git a/core/src/main/resources/com/graphhopper/custom_models/bus.json b/core/src/main/resources/com/graphhopper/custom_models/bus.json index 2dba951ed47..5b29f5831f3 100644 --- a/core/src/main/resources/com/graphhopper/custom_models/bus.json +++ b/core/src/main/resources/com/graphhopper/custom_models/bus.json @@ -1,19 +1,20 @@ // to use this custom model you need to set the following option in the config.yml -// graph.vehicles: roads|transportation_mode=BUS,car -// graph.encoded_values: max_width,max_height // profiles: // - name: bus -// vehicle: roads -// weighting: custom // custom_model_files: [bus.json] { "distance_influence": 90, "priority": [ - { "if": "car_access == false || max_width < 3 || max_height < 4", "multiply_by": "0" } + { "if": "max_weight < 5 || max_width < 3 || max_height < 4", "multiply_by": "0" }, + { "if": "bus_access && (road_class == MOTORWAY || road_class == TRUNK || road_class == PRIMARY || road_class == SECONDARY || road_class == TERTIARY || road_class == UNCLASSIFIED || road_class == LIVING_STREET || road_class == RESIDENTIAL || road_class == SERVICE || road_class == ROAD)", + "multiply_by": "1" + }, + { "else": "", "multiply_by": "0" } ], "speed": [ - { "if": "true", "limit_to": "car_average_speed * 0.9" }, - { "if": "true", "limit_to": "120" } + { "if": "bus_access && car_average_speed < 10", "limit_to": "10" }, + { "else": "", "limit_to": "car_average_speed * 0.9" }, + { "if": "true", "limit_to": "100" } ] -} \ No newline at end of file +} diff --git a/core/src/main/resources/com/graphhopper/custom_models/car.json b/core/src/main/resources/com/graphhopper/custom_models/car.json new file mode 100644 index 00000000000..aa864585f0d --- /dev/null +++ b/core/src/main/resources/com/graphhopper/custom_models/car.json @@ -0,0 +1,16 @@ +// to use this custom model you need to set the following option in the config.yml +// profiles: +// - name: car +// turn_costs: +// vehicle_types: [motorcar, motor_vehicle] +// custom_model_files: [car.json] + +{ + "distance_influence": 90, + "priority": [ + { "if": "!car_access", "multiply_by": "0" } + ], + "speed": [ + { "if": "true", "limit_to": "car_average_speed" } + ] +} diff --git a/core/src/main/resources/com/graphhopper/custom_models/car4wd.json b/core/src/main/resources/com/graphhopper/custom_models/car4wd.json index fa5db9d809e..541c742f963 100644 --- a/core/src/main/resources/com/graphhopper/custom_models/car4wd.json +++ b/core/src/main/resources/com/graphhopper/custom_models/car4wd.json @@ -1,10 +1,6 @@ // to use this custom model you need to set the following option in the config.yml -// graph.vehicles: roads -// graph.encoded_values: track_type // profiles: // - name: car4wd -// vehicle: roads -// weighting: custom // custom_model_files: [car4wd.json] { @@ -23,4 +19,4 @@ } ] -} \ No newline at end of file +} diff --git a/core/src/main/resources/com/graphhopper/custom_models/foot.json b/core/src/main/resources/com/graphhopper/custom_models/foot.json new file mode 100644 index 00000000000..0a861f1a932 --- /dev/null +++ b/core/src/main/resources/com/graphhopper/custom_models/foot.json @@ -0,0 +1,15 @@ +// to use this custom model you need to set the following option in the config.yml +// graph.elevation.provider: srtm # enables elevation +// profiles: +// - name: foot +// custom_model_files: [foot.json, foot_elevation.json] + +{ + "priority": [ + { "if": "!foot_access || hike_rating >= 2", "multiply_by": "0" }, + { "else": "", "multiply_by": "foot_priority"} + ], + "speed": [ + { "if": "true", "limit_to": "foot_average_speed" } + ] +} diff --git a/core/src/main/resources/com/graphhopper/custom_models/hike.json b/core/src/main/resources/com/graphhopper/custom_models/hike.json index 9771a3f5e6d..c201a6ab145 100644 --- a/core/src/main/resources/com/graphhopper/custom_models/hike.json +++ b/core/src/main/resources/com/graphhopper/custom_models/hike.json @@ -1,23 +1,19 @@ // to use this custom model you set the following option in the config.yml: -// graph.vehicles: roads,foot -// graph.encoded_values: hike_rating,average_slope,max_slope // graph.elevation.provider: srtm # enables elevation // profiles: // - name: hike -// vehicle: roads -// weighting: custom // custom_model_files: [hike.json, foot_elevation.json] { "priority": [ - { "if": "!foot_access && (hike_rating < 4 || road_access == PRIVATE)", "multiply_by": "0"}, - { "if": "true", "multiply_by": "foot_priority"}, + { "if": "!foot_access || hike_rating >= 5", "multiply_by": "0"}, + { "else": "", "multiply_by": "foot_priority"}, { "if": "foot_network == INTERNATIONAL || foot_network == NATIONAL", "multiply_by": "1.7"}, { "else_if": "foot_network == REGIONAL || foot_network == LOCAL", "multiply_by": "1.5"} ], "speed": [ { "if": "hike_rating < 1", "limit_to": "foot_average_speed" }, - { "else_if": "hike_rating > 2", "limit_to": "2" }, + { "else_if": "hike_rating > 2", "limit_to": "1.5" }, { "else": "", "limit_to": "4" } ] } diff --git a/core/src/main/resources/com/graphhopper/custom_models/motorcycle.json b/core/src/main/resources/com/graphhopper/custom_models/motorcycle.json index c36e1fe9af1..a686611ceba 100644 --- a/core/src/main/resources/com/graphhopper/custom_models/motorcycle.json +++ b/core/src/main/resources/com/graphhopper/custom_models/motorcycle.json @@ -1,11 +1,7 @@ // to use this custom model you need to set the following option in the config.yml -// graph.vehicles: roads|transportation_mode=MOTORCYCLE,car // graph.urban_density.threads: 4 # expensive to calculate but very useful -// graph.encoded_values: curvature,track_type,surface // profiles: // - name: motorcycle -// vehicle: roads -// weighting: custom // custom_model_files: [motorcycle.json,curvature.json] { @@ -18,11 +14,11 @@ // { "if": "urban_density != RURAL", "multiply_by": "0.3" }, ], "speed": [ - { "if": "true", "multiply_by": "0.9 * car_average_speed" }, + { "if": "true", "limit_to": "0.9 * car_average_speed" }, { "if": "true", "limit_to": "120" }, { "if": "surface==COBBLESTONE || surface==GRASS || surface==GRAVEL || surface==SAND || surface==PAVING_STONES || surface==DIRT || surface==GROUND || surface==UNPAVED || surface==COMPACTED", "limit_to": "30" } ] -} \ No newline at end of file +} diff --git a/core/src/main/resources/com/graphhopper/custom_models/mtb.json b/core/src/main/resources/com/graphhopper/custom_models/mtb.json new file mode 100644 index 00000000000..fb42e89d6af --- /dev/null +++ b/core/src/main/resources/com/graphhopper/custom_models/mtb.json @@ -0,0 +1,17 @@ +// to use this custom model you need to set the following option in the config.yml +// graph.elevation.provider: srtm # enables elevation +// profiles: +// - name: mtb +// custom_model_files: [mtb.json, bike_elevation.json] + +{ + "priority": [ + { "if": "true", "multiply_by": "mtb_priority" }, + { "if": "!mtb_access && (!backward_mtb_access || roundabout)", "multiply_by": "0" }, + { "else_if": "!mtb_access && backward_mtb_access", "multiply_by": "0.2" } + ], + "speed": [ + { "if": "true", "limit_to": "mtb_average_speed" }, + { "if": "!mtb_access && backward_mtb_access && !roundabout", "limit_to": "5" } + ] +} diff --git a/core/src/main/resources/com/graphhopper/custom_models/racingbike.json b/core/src/main/resources/com/graphhopper/custom_models/racingbike.json new file mode 100644 index 00000000000..19af214d632 --- /dev/null +++ b/core/src/main/resources/com/graphhopper/custom_models/racingbike.json @@ -0,0 +1,17 @@ +// to use this custom model you set the following option in the config.yml: +// graph.elevation.provider: srtm # enables elevation +// profiles: +// - name: racingbike +// custom_model_files: [racingbike.json, bike_elevation.json] + +{ +"priority": [ +{ "if": "true", "multiply_by": "racingbike_priority" }, +{ "if": "!racingbike_access && (!backward_racingbike_access || roundabout)", "multiply_by": "0" }, +{ "else_if": "!racingbike_access && backward_racingbike_access", "multiply_by": "0.2" } +], +"speed": [ +{ "if": "true", "limit_to": "racingbike_average_speed" }, +{ "if": "!racingbike_access && backward_racingbike_access && !roundabout", "limit_to": "5" } +] +} diff --git a/core/src/main/resources/com/graphhopper/custom_models/truck.json b/core/src/main/resources/com/graphhopper/custom_models/truck.json index 7e96751d22f..6bc505b6204 100644 --- a/core/src/main/resources/com/graphhopper/custom_models/truck.json +++ b/core/src/main/resources/com/graphhopper/custom_models/truck.json @@ -1,10 +1,8 @@ // to use this custom model you need to set the following option in the config.yml -// graph.vehicles: roads|transportation_mode=HGV,car -// graph.encoded_values: toll,hgv,surface,max_width,max_height // profiles: // - name: truck -// vehicle: roads -// weighting: custom +// turn_costs: +// vehicle_types: [hgv, motorcar, motor_vehicle] // custom_model_files: [truck.json] { @@ -17,4 +15,4 @@ { "if": "true", "limit_to": "car_average_speed * 0.9" }, { "if": "true", "limit_to": "95" } ] -} \ No newline at end of file +} diff --git a/core/src/main/resources/com/graphhopper/util/ar.txt b/core/src/main/resources/com/graphhopper/util/ar.txt index 523bd826d8c..5480069e512 100644 --- a/core/src/main/resources/com/graphhopper/util/ar.txt +++ b/core/src/main/resources/com/graphhopper/util/ar.txt @@ -1,7 +1,7 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=استمر -continue_onto=استمر في %1$s +continue_onto=استمر في %1$s finish=النهاية keep_left=احفظ الشمال keep_right=احفظ اليمين @@ -16,7 +16,7 @@ u_turn=الدوران للإتجاه المعاكس toward_destination= toward_destination_ref_only= toward_destination_with_ref= -unknown=علامة غير معرفة '%1$s' +unknown=علامة غير معرفة '%1$s' via=من خلال hour_abbr=ساعة day_abbr=يوم @@ -44,6 +44,7 @@ web.way_contains_private=طريق خاص web.way_contains_toll=طريق برسم عبور web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -79,7 +83,7 @@ web.exclude_area_example= web.combined_example= web.examples_custom_model= web.marker=رمز او علامة -web.gh_offline_info= واجهة برمجة التطبيقات (API) بلا اتصال؟ +web.gh_offline_info=واجهة برمجة التطبيقات (API) بلا اتصال؟ web.refresh_button=اعادة تنشيط web.server_status=الحالة web.zoom_in=تكبير @@ -94,6 +98,10 @@ web.via_hint=خلال web.from_hint=من web.gpx_export_button=GPX التصدير web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=إلي @@ -105,6 +113,11 @@ web.pt_route_info_walking=الوصول عند %1$s مشياً (%2$s) web.locations_not_found=لا يوجد طريق من هذه المنطقة web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=دراجة web.racingbike=دراجة سباق web.mtb=دراجة جبلية @@ -116,9 +129,24 @@ web.bus=باص web.truck=شاحنة web.staticlink=رابط ثابت web.motorcycle=دراجة نارية +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km= navigate.for_mi= diff --git a/core/src/main/resources/com/graphhopper/util/ast.txt b/core/src/main/resources/com/graphhopper/util/ast.txt index ae15f5b78cb..7c5eaea13cc 100644 --- a/core/src/main/resources/com/graphhopper/util/ast.txt +++ b/core/src/main/resources/com/graphhopper/util/ast.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=sigue continue_onto=sigue per %1$s @@ -44,6 +44,7 @@ web.way_contains_private= web.way_contains_toll= web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint=Pasando per web.from_hint=Dende web.gpx_export_button=Esportar GPX web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint= @@ -105,6 +113,11 @@ web.pt_route_info_walking=llegada a les %1$s sólo caminando (%2$s) web.locations_not_found=Nun se puede trazar una ruta. Dalgún llugar nun s'alcuentra nel área web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Bicicleta web.racingbike=Bici de carreres web.mtb=Bici de montaña @@ -116,9 +129,24 @@ web.bus=Autobús web.truck=Camión web.staticlink=enllaz estáticu web.motorcycle=Motocicleta +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km= navigate.for_mi= diff --git a/core/src/main/resources/com/graphhopper/util/az.txt b/core/src/main/resources/com/graphhopper/util/az.txt index f63f9766a5f..7bad3afd11c 100644 --- a/core/src/main/resources/com/graphhopper/util/az.txt +++ b/core/src/main/resources/com/graphhopper/util/az.txt @@ -1,11 +1,11 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=Davam edin continue_onto=Davam edin - %1$s finish=Məntəqəyə çatdınız keep_left=Sol tərəfi tutaraq hərəkət edin keep_right=Sağ tərəfi tutaraq hərəkət edin -turn_onto=%1$s - %2$s +turn_onto=%1$s - %2$s turn_left=Sola dönün turn_right=Sağa dönün turn_slight_left=Yavaşca sola dönün @@ -44,6 +44,7 @@ web.way_contains_private=özəl yol web.way_contains_toll=ödənişli yol web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint=vasitəsilə web.from_hint=-dan web.gpx_export_button=GPX ixrac web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=-dək @@ -105,6 +113,11 @@ web.pt_route_info_walking=Piyada olaraq təxmini çatma vaxtı %1$s ( %2$s) web.locations_not_found=Marşrut qurulmağı mümkün deyil. Yer müəyyən edilməyib. web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Velosiped web.racingbike=Yarış velosipedi web.mtb=Dağ velosipedi @@ -116,9 +129,24 @@ web.bus=Avtobus web.truck=Yük maşını web.staticlink=Daimi keçid web.motorcycle=Motosiklet +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km=%1$s kilometrdə navigate.for_mi=%1$s mildə diff --git a/core/src/main/resources/com/graphhopper/util/bg.txt b/core/src/main/resources/com/graphhopper/util/bg.txt index b8bf26f4228..162a231d94a 100644 --- a/core/src/main/resources/com/graphhopper/util/bg.txt +++ b/core/src/main/resources/com/graphhopper/util/bg.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=продължете continue_onto=продължете по %1$s @@ -44,6 +44,7 @@ web.way_contains_private=частен път web.way_contains_toll=път с такса web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint=През web.from_hint=От web.gpx_export_button=Изнасяне като GPX web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=До @@ -105,6 +113,11 @@ web.pt_route_info_walking=пристига в %1$s само с ходене (%2$ web.locations_not_found=Не са изтеглени упътвания. Търсеното местоположение не е открито. web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Велосипед web.racingbike=Състезателно колело web.mtb=Планинско колело @@ -116,9 +129,24 @@ web.bus=Автобус web.truck=Камион web.staticlink=Постоянна препратка web.motorcycle=Мотоциклет +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning=Разбирам и се съгласявам navigate.for_km=за %1$s километра navigate.for_mi=за %1$s мили diff --git a/core/src/main/resources/com/graphhopper/util/bn_BN.txt b/core/src/main/resources/com/graphhopper/util/bn_BN.txt index 61b3548401e..95e116b3794 100644 --- a/core/src/main/resources/com/graphhopper/util/bn_BN.txt +++ b/core/src/main/resources/com/graphhopper/util/bn_BN.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=সোজা যেতে থাকুন continue_onto=%1$s সড়কে সোজা যেতে থাকুন @@ -44,6 +44,7 @@ web.way_contains_private= web.way_contains_toll=রুটে টোল আছে web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint= web.from_hint= web.gpx_export_button= web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint= @@ -105,6 +113,11 @@ web.pt_route_info_walking= web.locations_not_found= web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike= web.racingbike= web.mtb= @@ -116,9 +129,24 @@ web.bus= web.truck= web.staticlink= web.motorcycle= +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km= navigate.for_mi= diff --git a/core/src/main/resources/com/graphhopper/util/ca.txt b/core/src/main/resources/com/graphhopper/util/ca.txt index ff29acb1df7..23823e0a294 100644 --- a/core/src/main/resources/com/graphhopper/util/ca.txt +++ b/core/src/main/resources/com/graphhopper/util/ca.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=continua continue_onto=continua per %1$s @@ -44,6 +44,7 @@ web.way_contains_private=camí privat web.way_contains_toll=via de peatge web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint=Passant per web.from_hint=Des de web.gpx_export_button=Exporta a GPX web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=Cap a @@ -105,6 +113,11 @@ web.pt_route_info_walking=arriba caminant a les %1$s (%2$s) web.locations_not_found=No hi ha cap ruta. El destí no es troba dins l'àrea. web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Bicicleta web.racingbike=Bicicleta de curses web.mtb=Bicicleta de muntanya @@ -116,9 +129,24 @@ web.bus=Autobús web.truck=Camió web.staticlink=Enllaç web.motorcycle=Motocicleta +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km=per %1$s quilòmetres navigate.for_mi=per %1$s milles diff --git a/core/src/main/resources/com/graphhopper/util/cs_CZ.txt b/core/src/main/resources/com/graphhopper/util/cs_CZ.txt index cbb6df476f5..1a1731f4ceb 100644 --- a/core/src/main/resources/com/graphhopper/util/cs_CZ.txt +++ b/core/src/main/resources/com/graphhopper/util/cs_CZ.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=pokračujte continue_onto=pokračujte směr %1$s @@ -44,13 +44,14 @@ web.way_contains_private=Trasa obsahuje soukromé cesty web.way_contains_toll=Trasa obsahuje zpoplatněné úseky web.way_crosses_border=Trasa obsahuje překročení hranic web.way_contains=Trasa obsahuje %1$s +web.way_contains_restrictions= web.tracks=nezpevněné cesty web.steps=schody web.footways=stezky pro pěší web.steep_sections=příkré úseky web.private_sections=soukromé úseky web.trunk_roads_warn= -web.get_off_bike_for=Je třeba sesednout z kola a %1$s jej tlačit +web.get_off_bike_for=Je třeba sesednout z kola a %1$s jej tlačit pt_transfer_to=přestupte na %1$s web.start_label=Start web.intermediate_label=Zastávka @@ -64,7 +65,7 @@ web.query_osm=Dotaz do OSM web.route=Trasa web.add_to_route=Přidat místo web.delete_from_route=Odstranit z trasy -web.open_custom_model_box=Otevřít rámeček s vlastním modelem +web.open_custom_model_box=Otevřít rámeček s vlastním modelem web.draw_areas_enabled=Kreslit a měnit oblasti na mapě web.help_custom_model=Nápověda web.apply_custom_model=Použít @@ -72,6 +73,9 @@ web.custom_model_enabled=Vlastní model aktivní web.settings=Nastavení web.settings_close=Zavřít web.exclude_motorway_example=Vynechat dálnice +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example=Rychlostní omezení web.cargo_bike_example=Nákladní kolo web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint=Přes web.from_hint=Z web.gpx_export_button=Export do GPX web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button=Skrýt web.details_button=Detaily web.to_hint=Do @@ -105,6 +113,11 @@ web.pt_route_info_walking=dorazí v %1$s pěšky (%2$s) web.locations_not_found= web.search_with_nominatim=Hledat pomocí Nominatim web.powered_by=Běží na +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Kolo web.racingbike=Závodní kolo web.mtb=Horské kolo @@ -116,9 +129,24 @@ web.bus=Autobus web.truck=Nákladní automobil web.staticlink=statický odkaz web.motorcycle=Motocykl +web.scooter= web.back_to_map=Zpět web.distance_unit=Vzdálenosti jsou uváděny v %1$s web.waiting_for_gps=Čekání na signál GPS… +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning=Rozumím a souhlasím navigate.for_km=%1$s kilometrů navigate.for_mi=%1$s mil diff --git a/core/src/main/resources/com/graphhopper/util/da_DK.txt b/core/src/main/resources/com/graphhopper/util/da_DK.txt index 99f67f9b16a..66a4616ba9f 100644 --- a/core/src/main/resources/com/graphhopper/util/da_DK.txt +++ b/core/src/main/resources/com/graphhopper/util/da_DK.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=fortsæt continue_onto=fortsæt på %1$s @@ -44,6 +44,7 @@ web.way_contains_private=Rute med private veje web.way_contains_toll=Rute med betalingsveje web.way_crosses_border=Rute der krydser landegrænser web.way_contains=Rute inkluderer %1$s +web.way_contains_restrictions= web.tracks=Grusveje uden asfalt web.steps=Trapper web.footways=Fortove @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example=Eksludér motorvej +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example=Max hastighed web.cargo_bike_example=Ladcykel web.prefer_bike_network= @@ -91,9 +95,13 @@ web.current_location= web.searching_location= web.searching_location_failed= web.via_hint=Via -web.from_hint=Fra +web.from_hint=Fra web.gpx_export_button=Eksportér GPX-fil web.gpx_button=GPX +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button=Gem web.details_button=Detaljer web.to_hint=Til @@ -105,6 +113,11 @@ web.pt_route_info_walking=ankommer til fods %1$s (%2$s) web.locations_not_found=Kan ikke beregne ruten. Lokation(erne) kan ikke findes i området. web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Cykel web.racingbike=Racercykel web.mtb=Mountain bike @@ -116,9 +129,24 @@ web.bus=Bus web.truck=Lastbil web.staticlink=statisk link web.motorcycle=Motorcykel +web.scooter= web.back_to_map=Tilbage web.distance_unit=Distance er i %1$s web.waiting_for_gps=Venter på GPS signal +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km=i %1$s kilometer navigate.for_mi=i %1$s mil diff --git a/core/src/main/resources/com/graphhopper/util/de_DE.txt b/core/src/main/resources/com/graphhopper/util/de_DE.txt index 1afd927b0ed..6e8f941a430 100644 --- a/core/src/main/resources/com/graphhopper/util/de_DE.txt +++ b/core/src/main/resources/com/graphhopper/util/de_DE.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=dem Straßenverlauf folgen continue_onto=dem Straßenverlauf von %1$s folgen @@ -44,6 +44,7 @@ web.way_contains_private=Route mit Privatwegen web.way_contains_toll=Route mit mautpflichtigen Straßen web.way_crosses_border=Route überquert Landesgrenzen web.way_contains=Auf der Route sind %1$s +web.way_contains_restrictions=Auf der Route gibt es potentielle Zugangsbeschränkungen web.tracks=unbefestigte Feldwege web.steps=Treppen web.footways=Fußwege @@ -72,6 +73,9 @@ web.custom_model_enabled=Custom Model aktiv web.settings=Optionen web.settings_close=Schließen web.exclude_motorway_example=Autobahn vermeiden +web.exclude_disneyland_paris_example= +web.simple_electric_car_example=Elektroauto (vereinfacht) +web.avoid_tunnels_bridges_example=Brücken u. Tunnel vermeiden web.limit_speed_example=Max. Geschwindigkeit web.cargo_bike_example=Lastenfahrrad web.prefer_bike_network=Radnetz bevorzugen @@ -94,6 +98,10 @@ web.via_hint=Über web.from_hint=Von web.gpx_export_button=GPX Export web.gpx_button=GPX +web.settings_gpx_export=GPX Export +web.settings_gpx_export_trk=Mit Track +web.settings_gpx_export_rte=Mit Route +web.settings_gpx_export_wpt=Mit Wegpunkten web.hide_button=Weniger web.details_button=Details web.to_hint=Nach @@ -105,6 +113,11 @@ web.pt_route_info_walking=Ankunft zu Fuß %1$s (%2$s) web.locations_not_found=Routing war nicht möglich. Ort(e) nicht gefunden in diesem Gebiet. web.search_with_nominatim=Suche mit Nominatim web.powered_by=Powered by +web.info=Info +web.feedback=Feedback +web.imprint=Impressum +web.privacy=Datenschutz +web.terms=AGB web.bike=Fahrrad web.racingbike=Rennrad web.mtb=Mountainbike @@ -116,9 +129,24 @@ web.bus=Bus web.truck=LKW web.staticlink=Link web.motorcycle=Motorrad +web.scooter=Roller web.back_to_map=Zurück web.distance_unit=Distanzen sind in %1$s web.waiting_for_gps=Warten auf das GPS Signal... +web.elevation=Höhe +web.slope=Gefälle +web.towerslope=Towerslope +web.country=Land +web.surface=Oberfläche +web.road_environment=Straßenumgebung +web.road_access=Straßenzugang +web.road_class=Straßentyp +web.max_speed=Max. Geschwindigkeit +web.average_speed=Durchsch. Geschwindigkeit +web.track_type=Straßenbefestigung +web.toll=Maut +web.next=Weiter +web.back=Zurück navigate.accept_risks_after_warning=Ich verstehe und akzeptiere navigate.for_km=für %1$s Kilometer navigate.for_mi=für %1$s Meilen diff --git a/core/src/main/resources/com/graphhopper/util/el.txt b/core/src/main/resources/com/graphhopper/util/el.txt index d0295413602..66d06104e82 100644 --- a/core/src/main/resources/com/graphhopper/util/el.txt +++ b/core/src/main/resources/com/graphhopper/util/el.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=συνεχίστε continue_onto=συνεχίστε στην %1$s @@ -13,18 +13,18 @@ turn_slight_right=στρίψτε λοξά δεξιά turn_sharp_left=στρίψτε κλειστά αριστερά turn_sharp_right=στρίψτε κλειστά δεξιά u_turn=κάνετε αναστροφή -toward_destination= -toward_destination_ref_only= -toward_destination_with_ref= +toward_destination=%1$s και οδηγείστε προς %2$s +toward_destination_ref_only=%1$s προς %2$s +toward_destination_with_ref=%1$s και πάρτε τον δρόμο %2$s προς %3$s unknown=άγνωστη οδηγία '%1$s' via=μέσω -hour_abbr=h -day_abbr=d -min_abbr=min -km_abbr=km -m_abbr=m -mi_abbr=mi -ft_abbr=ft +hour_abbr=ω +day_abbr=μ +min_abbr=λεπ +km_abbr=χλμ +m_abbr=μ +mi_abbr=μι +ft_abbr=πδ road=δρόμος off_bike=κατεβείτε από το ποδήλατο cycleway=ποδηλατόδρομος @@ -38,19 +38,20 @@ roundabout_exit=Στον κυκλικό κόμβο βγείτε στην έξο roundabout_exit_onto=Στον κυκλικό κόμβο βγείτε στην έξοδο %1$s στην %2$s web.total_ascend=%1$s συνολική ανάβαση web.total_descend=%1$s συνολική κατάβαση -web.way_contains_ford=υπάρχει πέρασμα στο δρόμο -web.way_contains_ferry=χρησιμοποιήστε το ferry -web.way_contains_private=ιδιωτικός δρόμος -web.way_contains_toll=δρόμος με διόδια -web.way_crosses_border= -web.way_contains= -web.tracks= -web.steps= -web.footways= -web.steep_sections= -web.private_sections= -web.trunk_roads_warn= -web.get_off_bike_for= +web.way_contains_ford=διαδρομή με περάσματα από ποτάμια +web.way_contains_ferry=διαδρομή με ferry +web.way_contains_private=διαδρομή με ιδιωτικούς δρόμους +web.way_contains_toll=διαδρομή με διόδια +web.way_crosses_border=διαδρομή διασχίζει σύνορα χώρας +web.way_contains=διαδρομή περιλαμβάνει %1$s +web.way_contains_restrictions= +web.tracks=μη ασφαλτοστρωμένοι χωματόδρομοι +web.steps=σκαλοπάτια +web.footways=μονοπάτια +web.steep_sections=απότομα τμήματα +web.private_sections=ιδιωτικά τμήματα +web.trunk_roads_warn=διαδρομή περιλαμβάνει πιθανά επικίνδυνους αυτοκινητρόδρομους ή χειρότερα +web.get_off_bike_for=Πρέπει να κατεβείτε από το ποδήλατο και να σπρώξετε για %1$s pt_transfer_to=αλλάξτε στο %1$s web.start_label=Αφετηρία web.intermediate_label=Ενδιάμεσο σημείο @@ -60,26 +61,29 @@ web.set_intermediate=Ορισμός ενδιάμεσου σημείου web.set_end=Ορισμός προορισμού web.center_map=Κεντράρισμα χάρτη εδώ web.show_coords=Εμφάνιση συντεταγμένων -web.query_osm= +web.query_osm=Ερώτημα OSM web.route=Διαδρομή -web.add_to_route= +web.add_to_route=Προσθήκη τοποθεσίας web.delete_from_route=Αφαίρεση από διαδρομή -web.open_custom_model_box= -web.draw_areas_enabled= -web.help_custom_model= -web.apply_custom_model= -web.custom_model_enabled= -web.settings= -web.settings_close= -web.exclude_motorway_example= -web.limit_speed_example= -web.cargo_bike_example= -web.prefer_bike_network= -web.exclude_area_example= -web.combined_example= -web.examples_custom_model= +web.open_custom_model_box=Άνοιγμα πλαισίου προσαρμοσμένου μοντέλου +web.draw_areas_enabled=Σχεδίαση και τροποποίηση περιοχών στον χάρτη +web.help_custom_model=Βοήθεια +web.apply_custom_model=Εφαρμογή +web.custom_model_enabled=Προσαρμοσμένο Μοντέλο Ενεργό +web.settings=Ρυθμίσεις +web.settings_close=Κλείσιμο +web.exclude_motorway_example=Αποφυγή αυτοκινητόδρομου +web.exclude_disneyland_paris_example=Αποφυγή Disneyland Παρισιού +web.simple_electric_car_example=Απλό ηλεκτρικό αυτοκίνητο +web.avoid_tunnels_bridges_example=Αποφυγή γεφυρών και σηράγγων +web.limit_speed_example=Περιορισμός ταχύτητας +web.cargo_bike_example=Ποδήλατο μεταφοράς +web.prefer_bike_network=Προτίμησε ποδηλατικές διαδρομές +web.exclude_area_example=Αποφυγή περιοχής +web.combined_example=Συνδυαστικό παράδειγμα +web.examples_custom_model=Παραδείγματα web.marker=Σημάδι -web.gh_offline_info=GraphHopper API offline? +web.gh_offline_info=GraphHopper API offline; web.refresh_button=Ανανέωση σελίδας web.server_status=Κατάσταση web.zoom_in=Μεγέθυνση @@ -93,9 +97,13 @@ web.searching_location_failed=Η αναζήτηση τοποθεσίας απέ web.via_hint=Μέσω web.from_hint=Αφετηρία web.gpx_export_button=Εξαγωγή GPX -web.gpx_button= -web.hide_button= -web.details_button= +web.gpx_button=GPX +web.settings_gpx_export=Εξαγγωγή GPX +web.settings_gpx_export_trk=Με ίχνος +web.settings_gpx_export_rte=Με διαδρομή +web.settings_gpx_export_wpt=Με σημεία +web.hide_button=Απόκρυψη +web.details_button=Λεπτομέρειες web.to_hint=Προορισμός web.route_info=%1$s σε %2$s web.search_button=Αναζήτηση @@ -103,8 +111,13 @@ web.more_button=περισσότερα web.pt_route_info=φτάνει στις %1$s με %2$s μεταβιβάσεις (%3$s) web.pt_route_info_walking=φτάνει στις %1$s απλά περπατώντας (%2$s) web.locations_not_found=Η δρομολόγηση δεν είναι δυνατή. Οι τοποθεσίες δεν βρέθηκαν στην περιοχή. -web.search_with_nominatim= -web.powered_by= +web.search_with_nominatim=Αναζήτηση με Nominatim +web.powered_by=Τροφοδοτείται από +web.info=Πληροφορίες +web.feedback=Ανατροφοδότηση +web.imprint=Αποτύπωμα +web.privacy=Απόρρητο +web.terms=Όροι web.bike=Ποδήλατο web.racingbike=Αγωνιστικό ποδήλατο web.mtb=Ποδήλατο βουνού @@ -116,23 +129,38 @@ web.bus=Λεωφορείο web.truck=Φορτηγό web.staticlink=στατική διεύθυνση web.motorcycle=Μοτοσυκλέτα -web.back_to_map= -web.distance_unit= -web.waiting_for_gps= +web.scooter=Σκούτερ +web.back_to_map=Πίσω +web.distance_unit=Οι αποστάσεις είναι σε %1$s +web.waiting_for_gps=Αναμονή για σήμα GPS... +web.elevation=Ανύψωση +web.slope=Πλαγιά +web.towerslope=Towerslope +web.country=Χώρα +web.surface=Επιφάνεια +web.road_environment=Περιβάλλον δρόμου +web.road_access=Πρόσβαση δρόμου +web.road_class=Κλάση δρόμου +web.max_speed=Μέγιστη ταχύτητα +web.average_speed=Μέση ταχύτητα +web.track_type=Τύπος δρόμου +web.toll=Διόδια +web.next=Επόμενο +web.back=Πίσω navigate.accept_risks_after_warning=Καταλαβαίνω και συμφωνώ -navigate.for_km=Για %1$s χιλιόμετρα +navigate.for_km=για %1$s χιλιόμετρα navigate.for_mi=για %1$s μίλια -navigate.full_screen_for_navigation= +navigate.full_screen_for_navigation=Θέσε σε πλήρη οθόνη navigate.in_km_singular=Σε 1 χιλιόμετρο navigate.in_km=Σε %1$s χιλιόμετρα navigate.in_m=Σε %1$s μέτρα -navigate.in_mi_singular=Σε 1 μίλη +navigate.in_mi_singular=Σε 1 μίλι navigate.in_mi=Σε %1$s μίλια navigate.in_ft=Σε %1$s πόδια -navigate.reroute= -navigate.start_navigation= +navigate.reroute=επαναδρομολόγηση +navigate.start_navigation=Πλοήγηση navigate.then=τότε -navigate.thenSign= -navigate.turn_navigation_settings_title= -navigate.vector_tiles_for_navigation= -navigate.warning=ΠΡΟΣΟΧΗ: Αυτή η εφαρμογή είναι πειραματική! Χρησιμοποιήστε την με δική σας ευθύνη! +navigate.thenSign=Τότε +navigate.turn_navigation_settings_title=Πλοήγηγη στροφή-στροφή +navigate.vector_tiles_for_navigation=Χρήση διανυσματικών πλακιδίων +navigate.warning=ΠΡΟΣΟΧΗ: Η πλοήγηση στροφή-στροφή είναι άκρως πειραματική! Χρησιμοποιήστε την με δική σας ευθύνη, προσέξτε το δρόμο και μην αγγίζετε τη συσκευή ενώ οδηγείτε! diff --git a/core/src/main/resources/com/graphhopper/util/en_US.txt b/core/src/main/resources/com/graphhopper/util/en_US.txt index e6f39d1dd96..956b019d620 100644 --- a/core/src/main/resources/com/graphhopper/util/en_US.txt +++ b/core/src/main/resources/com/graphhopper/util/en_US.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=continue continue_onto=continue onto %1$s @@ -44,6 +44,7 @@ web.way_contains_private=Route with private roads web.way_contains_toll=Route has tolls web.way_crosses_border=Route crosses a country border web.way_contains=Route includes %1$s +web.way_contains_restrictions=Route with potential access restrictions web.tracks=unpaved dirt roads web.steps=steps web.footways=footways @@ -72,6 +73,9 @@ web.custom_model_enabled=Custom Model Active web.settings=Settings web.settings_close=Close web.exclude_motorway_example=Exclude Motorway +web.exclude_disneyland_paris_example=Exclude Disneyland Paris +web.simple_electric_car_example=Simple Electric Car +web.avoid_tunnels_bridges_example=Avoid Bridges & Tunnels web.limit_speed_example=Limit Speed web.cargo_bike_example=Cargo Bike web.prefer_bike_network=Prefer Cycle Routes @@ -94,6 +98,10 @@ web.via_hint=Via web.from_hint=From web.gpx_export_button=GPX Export web.gpx_button=GPX +web.settings_gpx_export=GPX export +web.settings_gpx_export_trk=With track +web.settings_gpx_export_rte=With route +web.settings_gpx_export_wpt=With waypoint web.hide_button=Hide web.details_button=Details web.to_hint=To @@ -105,6 +113,11 @@ web.pt_route_info_walking=arrives at %1$s with just walking (%2$s) web.locations_not_found=Routing not possible. Location(s) not found in the area. web.search_with_nominatim=Search with Nominatim web.powered_by=Powered by +web.info=Info +web.feedback=Feedback +web.imprint=Imprint +web.privacy=Privacy +web.terms=Terms web.bike=Bike web.racingbike=Racing Bike web.mtb=MTB @@ -116,9 +129,24 @@ web.bus=Bus web.truck=Truck web.staticlink=static link web.motorcycle=Motorcycle +web.scooter=Scooter web.back_to_map=Back web.distance_unit=Distances are in %1$s web.waiting_for_gps=Waiting for the GPS signal... +web.elevation=Elevation +web.slope=Slope +web.towerslope=Towerslope +web.country=Country +web.surface=Surface +web.road_environment=Road environment +web.road_access=Road access +web.road_class=Road class +web.max_speed=Max. speed +web.average_speed=Avg. speed +web.track_type=Track type +web.toll=Toll +web.next=Next +web.back=Back navigate.accept_risks_after_warning=I understand and agree navigate.for_km=for %1$s kilometers navigate.for_mi=for %1$s miles diff --git a/core/src/main/resources/com/graphhopper/util/eo.txt b/core/src/main/resources/com/graphhopper/util/eo.txt index 73d457a3521..05325b5e76b 100644 --- a/core/src/main/resources/com/graphhopper/util/eo.txt +++ b/core/src/main/resources/com/graphhopper/util/eo.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=pluu continue_onto=pluu laŭlonge de %1$s @@ -44,6 +44,7 @@ web.way_contains_private=privata vojo web.way_contains_toll=pegenda vojo web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint=Tra web.from_hint=El web.gpx_export_button=Elporti GPX web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=Al @@ -105,6 +113,11 @@ web.pt_route_info_walking=alveno je %1$s nur perpiede (%2$s) web.locations_not_found=Vojdifinado ne eblas. Ne trovis loko(j)n en la ĉirkaŭaĵo. web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Biciklo web.racingbike=Sporta biciklo web.mtb=Montara biciklo @@ -116,9 +129,24 @@ web.bus=Aŭtobuso web.truck=Kamiono web.staticlink=konstanta ligilo web.motorcycle=Motorciklo +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km= navigate.for_mi= diff --git a/core/src/main/resources/com/graphhopper/util/es.txt b/core/src/main/resources/com/graphhopper/util/es.txt index 147d003f256..9be51bf1cd1 100644 --- a/core/src/main/resources/com/graphhopper/util/es.txt +++ b/core/src/main/resources/com/graphhopper/util/es.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=continúa continue_onto=continúa por %1$s @@ -27,7 +27,7 @@ mi_abbr=mi ft_abbr=ft road=carretera off_bike=bájate de la bicicleta -cycleway=ciclovía +cycleway=ciclovía way=camino small_way=camino chico paved=pavimentado @@ -44,6 +44,7 @@ web.way_contains_private=carretera privada web.way_contains_toll=carretera con peaje web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -94,10 +98,14 @@ web.via_hint=Pasando por web.from_hint=Desde web.gpx_export_button=Exportar GPX web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=Hasta -web.route_info=%1$s tardará %2$s +web.route_info=%1$s tardará %2$s web.search_button=Buscar web.more_button=más web.pt_route_info=llega a las %1$s con %2$s transferencias (%3$s) @@ -105,6 +113,11 @@ web.pt_route_info_walking=llega sólo caminando a las %1$s (%2$s) web.locations_not_found=No se ha encontrado la ruta. El destino no se encuentra en el área. web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Bicicleta web.racingbike=Bicicleta de carrera web.mtb=Bicicleta de montaña @@ -116,9 +129,24 @@ web.bus=Autobús web.truck=Camión web.staticlink=enlace estático web.motorcycle=Motocicleta +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km=por %1$s kilómetros navigate.for_mi= diff --git a/core/src/main/resources/com/graphhopper/util/fa.txt b/core/src/main/resources/com/graphhopper/util/fa.txt index 0169b868c35..658ddc16506 100644 --- a/core/src/main/resources/com/graphhopper/util/fa.txt +++ b/core/src/main/resources/com/graphhopper/util/fa.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=ادامه دهید continue_onto=در %1$s ادامه دهید @@ -39,11 +39,12 @@ roundabout_exit_onto=در فلکه، از خروجی %1$s به %2$s بروید web.total_ascend=مجموع صعود %1$s web.total_descend=مجموع نزول %1$s web.way_contains_ford=در طول مسیر گُدار وجود دارد -web.way_contains_ferry=این مسیر دارای کشتی است +web.way_contains_ferry=این مسیر دارای کشتی است web.way_contains_private=مسیردارای گذرگاه های شخصی (مالکیت خصوصی) web.way_contains_toll=این مسیر دارای عوارض است web.way_crosses_border=این مسیر از مرز یک کشور عبور میکند -web.way_contains=این مسیر دارای %1$s است +web.way_contains=این مسیر دارای %1$s است +web.way_contains_restrictions= web.tracks=هشدار: این مسیر دارای جاده های خاکی و غیر آسفالت است web.steps=پله web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint=با گذر از web.from_hint=از web.gpx_export_button=دریافت در قالب GPX web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=به @@ -105,6 +113,11 @@ web.pt_route_info_walking=ساعت %1$s می‌رسید، فقط پیاده (%2$ web.locations_not_found=مسیریابی ممکن نیست. مکان(ها) در ناحیهٔ موردنظر پیدا نشد. web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=دوچرخه web.racingbike=دوچرخه کورسی web.mtb=دوچرخه کوهستان @@ -116,9 +129,24 @@ web.bus=اتوبوس web.truck=کامیون web.staticlink=پیوند ثابت web.motorcycle=موتورسیکلت +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km= navigate.for_mi= diff --git a/core/src/main/resources/com/graphhopper/util/fi.txt b/core/src/main/resources/com/graphhopper/util/fi.txt index 9d5d2c07734..35602ccf9d7 100644 --- a/core/src/main/resources/com/graphhopper/util/fi.txt +++ b/core/src/main/resources/com/graphhopper/util/fi.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=jatka continue_onto=jatka tielle %1$S @@ -44,6 +44,7 @@ web.way_contains_private=yksityinen tie web.way_contains_toll=maksullinen tie web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint=Reittipiste web.from_hint=Lähtöpaikka web.gpx_export_button=GPX-tuonti web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=Määränpää @@ -105,6 +113,11 @@ web.pt_route_info_walking=saapuu kello %1$s, vain kävely (%3$s) web.locations_not_found=Reittiohjeiden luonti epäonnistui. Paikkaa ei löydy tältä alueelta. web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Pyörä web.racingbike=Kilpapyörä web.mtb=Maastopyörä @@ -116,9 +129,24 @@ web.bus=Linja-auto web.truck=Kuorma-auto web.staticlink=yhteys web.motorcycle=Moottoripyörällä +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning=Ymmärrän ja hyväksyn navigate.for_km=kulje %1$s kilometriä navigate.for_mi=kulje %1$s mailia diff --git a/core/src/main/resources/com/graphhopper/util/fil.txt b/core/src/main/resources/com/graphhopper/util/fil.txt index bff0e3dc773..0ea03475f5a 100644 --- a/core/src/main/resources/com/graphhopper/util/fil.txt +++ b/core/src/main/resources/com/graphhopper/util/fil.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=tuwirín ang daán continue_onto=magpatuloy papunta sa %1$s @@ -25,12 +25,12 @@ km_abbr=km m_abbr=m mi_abbr=mi ft_abbr=ft -road=kalsada -off_bike=seksyon pagtulak +road=kalsada +off_bike=seksyon pagtulak cycleway=cycleway way=landas small_way= -paved=aspaltado +paved=aspaltado unpaved=hindi aspaltado stopover=pamahingahan %1$s roundabout_enter=Lpasok Rotonda @@ -44,6 +44,7 @@ web.way_contains_private= web.way_contains_toll= web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -91,9 +95,13 @@ web.current_location= web.searching_location= web.searching_location_failed= web.via_hint= -web.from_hint=mula sa +web.from_hint=mula sa web.gpx_export_button=GPX Export web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=upang sa @@ -105,6 +113,11 @@ web.pt_route_info_walking= web.locations_not_found=Routing hindi maaari, hindi nahanap Lokasyon sa lugar. web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Bike web.racingbike=Racing Bike web.mtb=Mountain Bike @@ -116,9 +129,24 @@ web.bus= web.truck= web.staticlink=static link web.motorcycle=motorsiklo +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km= navigate.for_mi= diff --git a/core/src/main/resources/com/graphhopper/util/fr_CH.txt b/core/src/main/resources/com/graphhopper/util/fr_CH.txt index 2e6a96a3c9e..37ce5490695 100644 --- a/core/src/main/resources/com/graphhopper/util/fr_CH.txt +++ b/core/src/main/resources/com/graphhopper/util/fr_CH.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=continuez continue_onto=continuez sur %1$s @@ -44,6 +44,7 @@ web.way_contains_private=chemin privé web.way_contains_toll=route à péage web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -94,10 +98,14 @@ web.via_hint=Via web.from_hint=De web.gpx_export_button=Export GPX web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=À -web.route_info=%1$s durera %2$s +web.route_info=%1$s durera %2$s web.search_button=Rechercher web.more_button=plus web.pt_route_info=arrivée à %1$s avec %2$s changements (%3$s) @@ -105,6 +113,11 @@ web.pt_route_info_walking=arrivée à pied à %1$s (%2$s) web.locations_not_found=Calcul d'itinéraire impossible. Position(s) non trouvée(s) dans la zone. web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Vélo web.racingbike=Vélo de course web.mtb=VTT @@ -116,9 +129,24 @@ web.bus=Bus web.truck=Camion web.staticlink=Lien web.motorcycle=Moto +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km=pendant %1$s kilomètres navigate.for_mi= diff --git a/core/src/main/resources/com/graphhopper/util/fr_FR.txt b/core/src/main/resources/com/graphhopper/util/fr_FR.txt index 13bbd807ca6..2b93eb3c293 100644 --- a/core/src/main/resources/com/graphhopper/util/fr_FR.txt +++ b/core/src/main/resources/com/graphhopper/util/fr_FR.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=continuez continue_onto=continuez sur %1$s @@ -44,6 +44,7 @@ web.way_contains_private=chemin privé web.way_contains_toll=route à péage web.way_crosses_border=L'itinéraire traverse une frontière nationale web.way_contains=L'itinéraire inclut %1$s +web.way_contains_restrictions= web.tracks=chemins de terre non pavés web.steps=pas web.footways=chemin pédestre @@ -72,12 +73,15 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example=Eviter les Autoroutes +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example=Vitesse Limite web.cargo_bike_example=Vélo cargo web.prefer_bike_network= web.exclude_area_example=Exclure Zone web.combined_example=Exemple Combiné -web.examples_custom_model=Examples +web.examples_custom_model=Exemples web.marker=Marqueur web.gh_offline_info=GraphHopper API hors connexion? web.refresh_button=Rafraîchir @@ -94,10 +98,14 @@ web.via_hint=Via web.from_hint=De web.gpx_export_button=Export GPX web.gpx_button=GPX +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button=Cacher web.details_button=Détails web.to_hint=À -web.route_info=%1$s durera %2$s +web.route_info=%1$s durera %2$s web.search_button=Rechercher web.more_button=plus web.pt_route_info=arrivée à %1$s avec %2$s changements (%3$s) @@ -105,6 +113,11 @@ web.pt_route_info_walking=arrivée à pied à %1$s (%2$s) web.locations_not_found=Calcul d'itinéraire impossible. Position(s) non trouvée(s) dans la zone. web.search_with_nominatim=Rechercher avec Nominatim web.powered_by=Alimenté par +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Vélo web.racingbike=Vélo de course web.mtb=VTT @@ -116,15 +129,30 @@ web.bus=Bus web.truck=Camion web.staticlink=Lien web.motorcycle=Moto +web.scooter= web.back_to_map=Retour web.distance_unit=Les distances sont en %1$s web.waiting_for_gps=En attente du signal GPS... +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning=Je comprends et j'accepte navigate.for_km=pendant %1$s kilomètres navigate.for_mi=Pendant %1$s miles navigate.full_screen_for_navigation= navigate.in_km_singular=à 1 kilomètre -navigate.in_km=à %1$s kilomètres +navigate.in_km=à %1$s kilomètres navigate.in_m=à %1$s mètres navigate.in_mi_singular=Dans 1 mile navigate.in_mi=Dans %1$s miles diff --git a/core/src/main/resources/com/graphhopper/util/gl.txt b/core/src/main/resources/com/graphhopper/util/gl.txt index 290a4d7eac1..7e22c7cafde 100644 --- a/core/src/main/resources/com/graphhopper/util/gl.txt +++ b/core/src/main/resources/com/graphhopper/util/gl.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=continúe continue_onto=continúe por %1$s @@ -28,7 +28,7 @@ ft_abbr=ft road=estrada off_bike=sector a pé cycleway=vía ciclista -way=vía +way=vía small_way= paved=asfaltada unpaved=non pavimentada @@ -44,6 +44,7 @@ web.way_contains_private= web.way_contains_toll= web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint=Vía web.from_hint=dende web.gpx_export_button=GPX Exportación web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=ata @@ -105,6 +113,11 @@ web.pt_route_info_walking= web.locations_not_found=Non se atopou a ruta. O destino non se atopa na área web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Bicicleta web.racingbike=Bicleta de carreiras web.mtb=Bicicleta de montaña @@ -116,9 +129,24 @@ web.bus= web.truck= web.staticlink=Enlace web.motorcycle=Motocicleta +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km= navigate.for_mi= diff --git a/core/src/main/resources/com/graphhopper/util/he.txt b/core/src/main/resources/com/graphhopper/util/he.txt index 0f8d8b3b78f..5ed4e3bee5b 100644 --- a/core/src/main/resources/com/graphhopper/util/he.txt +++ b/core/src/main/resources/com/graphhopper/util/he.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=להמשיך continue_onto=להמשיך אל %1$s @@ -44,6 +44,7 @@ web.way_contains_private=המסלול כולל דרכים פרטיות web.way_contains_toll=המסלול כולל כבישי אגרה web.way_crosses_border=המסלול כולל חציית גבולות בין מדינות web.way_contains=המסלול כולל %1$s +web.way_contains_restrictions= web.tracks=דרכי עפר לא סלולות web.steps=מדרגות web.footways=שבילים להולכי רגל @@ -72,6 +73,9 @@ web.custom_model_enabled=מודל מותאם אישית מופעל web.settings=הגדרות web.settings_close=סגירה web.exclude_motorway_example=ללא כבישים מהירים +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example=הגבלת מהירות web.cargo_bike_example=אופני משא web.prefer_bike_network=העדפת מסלולי אופניים @@ -94,6 +98,10 @@ web.via_hint=נקודת ביניים web.from_hint=מוצא web.gpx_export_button=ייצוא GPX web.gpx_button=GPX +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button=הסתרה web.details_button=פרטים web.to_hint=יעד @@ -105,6 +113,11 @@ web.pt_route_info_walking=מגיע ב־%1$s עם הליכה בלבד (%3$s) web.locations_not_found=אין נתיב ישיר אל היעד. המקום/ות לא נמצא/ים באזור. web.search_with_nominatim=חיפוש בעזרת Nominatim web.powered_by=פועל בעזרת +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=אופניים web.racingbike=מסלולי אופניים web.mtb=אופני הרים @@ -116,9 +129,24 @@ web.bus=אוטובוס web.truck=משאית web.staticlink=קישור קבוע web.motorcycle=אופנוע +web.scooter= web.back_to_map=חזרה web.distance_unit=מרחקים ב%1$s web.waiting_for_gps=המתנה לאות GPS... +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning=מובן ומוסכם עלי navigate.for_km=במשך %1$s קילומטרים navigate.for_mi=במשך %1$s מיילים diff --git a/core/src/main/resources/com/graphhopper/util/hr_HR.txt b/core/src/main/resources/com/graphhopper/util/hr_HR.txt index 942669122e9..3d5310f31e3 100644 --- a/core/src/main/resources/com/graphhopper/util/hr_HR.txt +++ b/core/src/main/resources/com/graphhopper/util/hr_HR.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=nastavite continue_onto=nastavite na %1$s @@ -44,6 +44,7 @@ web.way_contains_private= web.way_contains_toll= web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint=Preko web.from_hint=Od web.gpx_export_button=Izvezi kao GPX web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=Do @@ -105,6 +113,11 @@ web.pt_route_info_walking= web.locations_not_found=Rutiranje nije moguće. Lokacije nisu pronađene na ovome području. web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Bicikl web.racingbike=Trkaći motocikl web.mtb=Brdski bicikl @@ -116,9 +129,24 @@ web.bus=Autobus web.truck=Kamion web.staticlink=statička poveznica web.motorcycle=Motocikl +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km= navigate.for_mi= diff --git a/core/src/main/resources/com/graphhopper/util/hsb.txt b/core/src/main/resources/com/graphhopper/util/hsb.txt index fc33039d07a..4ec8525f4ce 100644 --- a/core/src/main/resources/com/graphhopper/util/hsb.txt +++ b/core/src/main/resources/com/graphhopper/util/hsb.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=runjewon continue_onto=runjewon na %1$s @@ -44,6 +44,7 @@ web.way_contains_private= web.way_contains_toll= web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint=přez web.from_hint=wot web.gpx_export_button=eksport do GPX web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=do @@ -105,6 +113,11 @@ web.pt_route_info_walking= web.locations_not_found=Čara njeje móžna. Městno so w tutej kónčinje njenamaka. web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=koleso web.racingbike=wubědźowanske koleso web.mtb=mountainbike @@ -116,9 +129,24 @@ web.bus= web.truck= web.staticlink=link web.motorcycle=motorske +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km= navigate.for_mi= diff --git a/core/src/main/resources/com/graphhopper/util/hu_HU.txt b/core/src/main/resources/com/graphhopper/util/hu_HU.txt index 2025fcaa0c1..c75270218a7 100644 --- a/core/src/main/resources/com/graphhopper/util/hu_HU.txt +++ b/core/src/main/resources/com/graphhopper/util/hu_HU.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=haladjon tovább continue_onto=haladjon tovább erre: %1$s @@ -44,6 +44,7 @@ web.way_contains_private=Az útvonalon magánút is található web.way_contains_toll=Az útvonalon útdíjat kell fizetni web.way_crosses_border=Az útvonal országhatárt keresztez web.way_contains=Az útvonalon előfordul %1$s +web.way_contains_restrictions= web.tracks=burkolatlan földút web.steps=lépcső web.footways=gyalogút @@ -72,6 +73,9 @@ web.custom_model_enabled=Egyedi modell bekapcsolva web.settings=Beállítások web.settings_close=Bezárás web.exclude_motorway_example=Autópálya nélkül +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example=Sebességkorlátozás web.cargo_bike_example=Viszikli (teherkerékpár) web.prefer_bike_network=Kerékpárutak előnyben részesítése @@ -94,6 +98,10 @@ web.via_hint=Ezen keresztül web.from_hint=Innen web.gpx_export_button=GPX export web.gpx_button=GPX +web.settings_gpx_export=GPX-be való exportálás beállításai +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button=Elrejtés web.details_button=Részletek web.to_hint=Ide @@ -105,6 +113,11 @@ web.pt_route_info_walking=érkezés %1$s órakor, csak gyalog (%2$s) web.locations_not_found=Útvonaltervezés nem lehetséges. A megadott hely(ek) nem található(k) meg a területen. web.search_with_nominatim=Keresés a Nominatim segítségével web.powered_by=Motor: +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Kerékpár web.racingbike=Versenykerékpár web.mtb=Hegyi kerékpár @@ -116,9 +129,24 @@ web.bus=Busz web.truck=Teherautó web.staticlink=Statikus hivatkozás web.motorcycle=Motorkerékpár +web.scooter= web.back_to_map=Vissza web.distance_unit=Távolság mértékegysége: %1$s web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning=Megértettem és elfogadom navigate.for_km=%1$s kilométert navigate.for_mi=%1$s mérföldet diff --git a/core/src/main/resources/com/graphhopper/util/in_ID.txt b/core/src/main/resources/com/graphhopper/util/in_ID.txt index 71352038726..25291c0c1db 100644 --- a/core/src/main/resources/com/graphhopper/util/in_ID.txt +++ b/core/src/main/resources/com/graphhopper/util/in_ID.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=lanjut continue_onto=menuju pada %1$s @@ -44,6 +44,7 @@ web.way_contains_private= web.way_contains_toll= web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint=melalui web.from_hint=dari web.gpx_export_button=Ekspor GPX web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=ke @@ -105,6 +113,11 @@ web.pt_route_info_walking=sampai pada %1$s dengan berjalan kaki (%2$s) web.locations_not_found=Penentuan rute tidak dapat dilakukan. Lokasi tidak ditemukan web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Sepeda web.racingbike=Sepeda Balap web.mtb=Sepeda Gunung @@ -116,9 +129,24 @@ web.bus=Bus web.truck=Truk web.staticlink=Jalur tetap web.motorcycle=Motor +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km= navigate.for_mi= diff --git a/core/src/main/resources/com/graphhopper/util/it.txt b/core/src/main/resources/com/graphhopper/util/it.txt index 241f22eb70c..4713ac2902e 100644 --- a/core/src/main/resources/com/graphhopper/util/it.txt +++ b/core/src/main/resources/com/graphhopper/util/it.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=continua continue_onto=continua su %1$s @@ -44,6 +44,7 @@ web.way_contains_private=Il percorso include strade private web.way_contains_toll=Il percorso include pedaggi web.way_crosses_border=Il percorso attraversa un confine di stato web.way_contains=Il percorso include %1$s +web.way_contains_restrictions= web.tracks=strade sterrate web.steps=Passi web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example=Escludi autostrada +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example=Limita velocità web.cargo_bike_example= web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint=Attraverso web.from_hint=Da web.gpx_export_button=Esporta GPX web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button=Nascondi web.details_button=Dettagli web.to_hint=A @@ -105,6 +113,11 @@ web.pt_route_info_walking=arrivato alle %1$s solo camminando (%2$s) web.locations_not_found=Percorso non calcolabile. Località non trovata(e) nell'area. web.search_with_nominatim=Cerca con Nominatim web.powered_by=Offerto da +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Bicicletta web.racingbike=Bici da corsa web.mtb=Mountainbike @@ -116,9 +129,24 @@ web.bus=Tram web.truck=Camion web.staticlink=permalink web.motorcycle=Moto +web.scooter= web.back_to_map=Indietro web.distance_unit=Le distanze sono in %1$s web.waiting_for_gps=Aspettando il segnale GPS... +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning=Ho capito e accetto navigate.for_km=per %1$s chilometri navigate.for_mi=per %1$s miglia diff --git a/core/src/main/resources/com/graphhopper/util/ja.txt b/core/src/main/resources/com/graphhopper/util/ja.txt index 77be3a68b0d..ecadd77bf32 100644 --- a/core/src/main/resources/com/graphhopper/util/ja.txt +++ b/core/src/main/resources/com/graphhopper/util/ja.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=進む continue_onto=%1$sまで進む @@ -44,6 +44,7 @@ web.way_contains_private= web.way_contains_toll= web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint=経由 web.from_hint=出発地点 web.gpx_export_button=GPX形式でエクスポート web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=目的地点 @@ -105,6 +113,11 @@ web.pt_route_info_walking= web.locations_not_found=経路を検索できませんでした.指定の地点が存在しません. web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=自転車 web.racingbike=レースバイク web.mtb=マウンテンバイク @@ -116,9 +129,24 @@ web.bus= web.truck= web.staticlink=パーマリンク web.motorcycle=オートバイ +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km= navigate.for_mi= diff --git a/core/src/main/resources/com/graphhopper/util/ko.txt b/core/src/main/resources/com/graphhopper/util/ko.txt index d4eeacee10e..76c4263549b 100644 --- a/core/src/main/resources/com/graphhopper/util/ko.txt +++ b/core/src/main/resources/com/graphhopper/util/ko.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=계속 continue_onto=%1$s(으)로 계속 이동 @@ -44,6 +44,7 @@ web.way_contains_private=사유 도로 web.way_contains_toll=유료 도로 web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint=경유 web.from_hint=출발 web.gpx_export_button=GPX 내보내기 web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=도착 @@ -105,6 +113,11 @@ web.pt_route_info_walking=도보 이용, %1$s에 도착 (%2$s) web.locations_not_found=경로를 탐색할 수 없습니다. 이 지역에서 위치를 찾을 수 없습니다. web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=자전거 web.racingbike=경주용 자전거 web.mtb=산악자전거 @@ -116,9 +129,24 @@ web.bus=버스 web.truck=트럭 web.staticlink=정적 링크 web.motorcycle=모터사이클 +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km= navigate.for_mi= diff --git a/core/src/main/resources/com/graphhopper/util/kz.txt b/core/src/main/resources/com/graphhopper/util/kz.txt index 36f3591b54a..99c65f80b7b 100644 --- a/core/src/main/resources/com/graphhopper/util/kz.txt +++ b/core/src/main/resources/com/graphhopper/util/kz.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=Жалғастырыңыз continue_onto=%1$s бойынша жалғастырыңыз @@ -39,11 +39,12 @@ roundabout_exit_onto=Айналымда %1$s-ден %2$s-ге web.total_ascend=%1$s көтерілу web.total_descend=%1$s төмен түсу web.way_contains_ford=Жолда өткел бар -web.way_contains_ferry= паромға отырыңыз +web.way_contains_ferry=паромға отырыңыз web.way_contains_private=жекеменшік жол web.way_contains_toll=жол ақылы web.way_crosses_border=жол ел шекарасын кесіп жатыр web.way_contains=жолда %1$s бар +web.way_contains_restrictions= web.tracks=асфальтталмаған қара жолдар web.steps=қадамдар web.footways=жаяу жүргіншілер жолы @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example=Автомогистральді алып тастау +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example=шектеулі жылдамдық web.cargo_bike_example=жүк велосипеді web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint=арқылы web.from_hint=Бастауы web.gpx_export_button=GPX Экспорты web.gpx_button=GPX +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button=жасыру web.details_button=толығырақ web.to_hint=дейін @@ -105,6 +113,11 @@ web.pt_route_info_walking=%1$s-ге дейін тек жаяу жүру (%2$s) web.locations_not_found=Маршрут құру мүмкін болмады. Орын анықталмады. web.search_with_nominatim=Nominatim арқылы іздеу web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=велосипед web.racingbike= web.mtb=Таулы велосипед @@ -116,9 +129,24 @@ web.bus=автобус web.truck=үлкен жүк көлігі web.staticlink= web.motorcycle=мотоцикл +web.scooter= web.back_to_map=Қайту web.distance_unit= web.waiting_for_gps=GPS сигналын күтіңіз... +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning=мен түсіндім және келісемін navigate.for_km= navigate.for_mi= @@ -131,7 +159,7 @@ navigate.in_mi= navigate.in_ft= navigate.reroute= navigate.start_navigation=навигация -navigate.then=одан соң +navigate.then=одан соң navigate.thenSign= navigate.turn_navigation_settings_title= navigate.vector_tiles_for_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/lt_LT.txt b/core/src/main/resources/com/graphhopper/util/lt_LT.txt index a5e10308e4a..3eca6e4847a 100644 --- a/core/src/main/resources/com/graphhopper/util/lt_LT.txt +++ b/core/src/main/resources/com/graphhopper/util/lt_LT.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=tęskite continue_onto=tęskite toliau %1$s @@ -44,6 +44,7 @@ web.way_contains_private= web.way_contains_toll= web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint=Per web.from_hint=Nuo web.gpx_export_button=GPX eksportas web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=Iki @@ -105,6 +113,11 @@ web.pt_route_info_walking=Atvyksta %1$s, su ėjimu pėščiomis (%2$s) web.locations_not_found=Neįmanoma sukurti maršruto. Nurodyti taškai nerasti šioje zonoje. web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Dviratis web.racingbike=Plentinis dviratis web.mtb=MTB dviratis @@ -116,9 +129,24 @@ web.bus=Autobusas web.truck=Sunkvežimis web.staticlink=statinė nuoroda web.motorcycle=Motociklas +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km=%1$s kilometrus navigate.for_mi= diff --git a/core/src/main/resources/com/graphhopper/util/nb_NO.txt b/core/src/main/resources/com/graphhopper/util/nb_NO.txt index 81ee7ccf9ba..3cfae26d703 100644 --- a/core/src/main/resources/com/graphhopper/util/nb_NO.txt +++ b/core/src/main/resources/com/graphhopper/util/nb_NO.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=fortsett continue_onto=fortsett på %1$s @@ -44,6 +44,7 @@ web.way_contains_private=ruten inneholder privat vei web.way_contains_toll=ruten er avgiftsbelagt web.way_crosses_border=ruten krysser landegrense web.way_contains=ruten inneholder %1$s +web.way_contains_restrictions= web.tracks=grusvei web.steps=trapper web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example=Unngå motorvei +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example=Begrens hastighet web.cargo_bike_example= web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint=Via web.from_hint=Fra web.gpx_export_button=Eksporter GPX web.gpx_button=GPX +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button=Skjul web.details_button=Detaljer web.to_hint=Til @@ -105,6 +113,11 @@ web.pt_route_info_walking=ankommer %1$s ved å gå (%2$s) web.locations_not_found=Ruteplanlegging mislykket. Destinasjon(ene) ikke funnet i valgt område web.search_with_nominatim=Søk med Nominatim web.powered_by=Powered by +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Sykkel web.racingbike=Landeveisykkel web.mtb=Mountain bike @@ -116,9 +129,24 @@ web.bus=Buss web.truck=Lastebil web.staticlink=lenke web.motorcycle=Motorsykkel +web.scooter= web.back_to_map=Tilbake web.distance_unit=Avstand måles i %1$s web.waiting_for_gps=Venter på GPS-signal +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning=Jeg forstår og samtykker navigate.for_km=I %1$s kilometer navigate.for_mi= diff --git a/core/src/main/resources/com/graphhopper/util/ne.txt b/core/src/main/resources/com/graphhopper/util/ne.txt index 0e008e3fb34..131af658ee6 100644 --- a/core/src/main/resources/com/graphhopper/util/ne.txt +++ b/core/src/main/resources/com/graphhopper/util/ne.txt @@ -1,17 +1,17 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=गहिरख्नुहोस continue_onto=%1$s गहिरख्नुहोस finish=सकियो keep_left= keep_right= -turn_onto=%2$s मा %1$s मोड्नुहोस -turn_left=बाया मोड्नुहोस -turn_right=दाया मोड्नुहोस -turn_slight_left=थोरै बाया मोड्नुहोस -turn_slight_right=थोरै दाया मोड्नुहोस -turn_sharp_left=धेरै बाया मोड्नुहोस -turn_sharp_right=धेरै दाया मोड्नुहोस +turn_onto=%2$s मा %1$s मोड्नुहोस +turn_left=बाया मोड्नुहोस +turn_right=दाया मोड्नुहोस +turn_slight_left=थोरै बाया मोड्नुहोस +turn_slight_right=थोरै दाया मोड्नुहोस +turn_sharp_left=धेरै बाया मोड्नुहोस +turn_sharp_right=धेरै दाया मोड्नुहोस u_turn= toward_destination= toward_destination_ref_only= @@ -21,7 +21,7 @@ via=बाट hour_abbr=घण्टा day_abbr=दिन min_abbr=मिनेट -km_abbr=किलोमीटर +km_abbr=किलोमीटर m_abbr=मीटर mi_abbr=माइल्स ft_abbr=फुट @@ -32,9 +32,9 @@ way=बाटो small_way= paved=पक्कि unpaved=कच्ची -stopover=%1$s रोकिने ठाउँ +stopover=%1$s रोकिने ठाउँ roundabout_enter=घुम्ती मा छिर्नुहोस -roundabout_exit=घुम्तीमा %1$s नम्बर को मोडबाट निस्कनुहोस +roundabout_exit=घुम्तीमा %1$s नम्बर को मोडबाट निस्कनुहोस roundabout_exit_onto=घुम्तीमा %1$s नम्बर को मोडबाट निस्केर %2$s मा जानुहोस web.total_ascend= web.total_descend= @@ -44,6 +44,7 @@ web.way_contains_private= web.way_contains_toll= web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -91,21 +95,30 @@ web.current_location= web.searching_location= web.searching_location_failed= web.via_hint=बाट -web.from_hint=सुरु +web.from_hint=सुरु web.gpx_export_button=GPX मा परिबर्तन गर्नुहोस web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=अन्त्य web.route_info=%1$s को लागि %2$s लाग्नेछ -web.search_button=खोज +web.search_button=खोज web.more_button=अझै web.pt_route_info= web.pt_route_info_walking= web.locations_not_found=ठाउँ नभेटीनाले बाटो पत्ता लगाउन सकिएन web.search_with_nominatim= web.powered_by= -web.bike=बाईक +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= +web.bike=बाईक web.racingbike=छिटो गतिका बाईक web.mtb=माउन्टेन बाईक web.car=गाडी @@ -115,10 +128,25 @@ web.small_truck= web.bus= web.truck= web.staticlink=ईस्ट्यातिक लिंक -web.motorcycle=मोटरसाइकल +web.motorcycle=मोटरसाइकल +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km= navigate.for_mi= diff --git a/core/src/main/resources/com/graphhopper/util/nl.txt b/core/src/main/resources/com/graphhopper/util/nl.txt index c11d37f6eaa..d5c4158055a 100644 --- a/core/src/main/resources/com/graphhopper/util/nl.txt +++ b/core/src/main/resources/com/graphhopper/util/nl.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=Ga rechtdoor continue_onto=blijf op %1$s @@ -34,8 +34,8 @@ paved=verhard unpaved=onverhard stopover=marker %1$s roundabout_enter=ga de rotonde op -roundabout_exit=neem afslag %1$s -roundabout_exit_onto=neem afslag %1$s naar %2$s +roundabout_exit=neem afslag %1$s +roundabout_exit_onto=neem afslag %1$s naar %2$s web.total_ascend=%1$s totale klim web.total_descend=%1$s totale daling web.way_contains_ford=Er is een doorwaadbare plaats @@ -44,12 +44,13 @@ web.way_contains_private=route met privé wegen web.way_contains_toll=route met tolwegen web.way_crosses_border=route met grensovergangen web.way_contains=route bevat %1$s +web.way_contains_restrictions= web.tracks=route bevat onverharde wegen web.steps=trappen web.footways=voetpaden web.steep_sections=route bevat stijle hellingen web.private_sections=privé gedeelten -web.trunk_roads_warn= +web.trunk_roads_warn=Route bevat mogelijk gevaarlijke stukken web.get_off_bike_for=stap af en duw voor %1$s pt_transfer_to=Stap over op de %1$s web.start_label=Start @@ -64,20 +65,23 @@ web.query_osm=Query OSM web.route=Route web.add_to_route=voeg locatie toe web.delete_from_route=Van route verwijderen -web.open_custom_model_box= -web.draw_areas_enabled= -web.help_custom_model= -web.apply_custom_model= +web.open_custom_model_box=Open op maat gemaakte box +web.draw_areas_enabled=Teken en verander gebied op de kaart +web.help_custom_model=Help +web.apply_custom_model=Activeer web.custom_model_enabled= -web.settings= -web.settings_close= -web.exclude_motorway_example= -web.limit_speed_example= -web.cargo_bike_example= -web.prefer_bike_network= -web.exclude_area_example= -web.combined_example= -web.examples_custom_model= +web.settings=Instellingen +web.settings_close=Sluit +web.exclude_motorway_example=Vermijd snelweg +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= +web.limit_speed_example=Max snelheid +web.cargo_bike_example=Bestel fiets +web.prefer_bike_network=Prefereer fiets routes +web.exclude_area_example=Exclusief gebied +web.combined_example=Samengesteld voorbeeld +web.examples_custom_model=Voorbeeld web.marker=Pin web.gh_offline_info=GrabHopper API offline? web.refresh_button=Ververs pagina @@ -86,7 +90,7 @@ web.zoom_in=Zoom in web.zoom_out=Zoom uit web.drag_to_reorder=Slepen om opnieuw te organiseren web.route_timed_out=server berekening time out -web.route_request_failed= +web.route_request_failed=Aanvraag mislukt web.current_location=huidige locatie web.searching_location=zoek locatie web.searching_location_failed=locatie zoeken mislukt @@ -94,6 +98,10 @@ web.via_hint=via web.from_hint=van web.gpx_export_button=GPX export web.gpx_button=GPX export te groot +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button=verberg web.details_button=details web.to_hint=naar @@ -104,21 +112,41 @@ web.pt_route_info=aankomst om %1$s met %2$s overstappen (%3$s) web.pt_route_info_walking=aankomst om %1$s te voet (%2$s) web.locations_not_found=Route niet mogelijk. Locatie(s) niet gevonden. web.search_with_nominatim= -web.powered_by= +web.powered_by=Powered by +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=fiets web.racingbike=racefiets web.mtb=mountainbike web.car=auto web.foot=te voet web.hike=wandelen -web.small_truck=kleine vrachtwagen +web.small_truck=bestelbus web.bus=bus web.truck=vrachtwagen web.staticlink=statische link web.motorcycle=motorfiets +web.scooter= web.back_to_map=Terug web.distance_unit=Afstanden zijn in %1$s web.waiting_for_gps=Wacht op GPS signaal +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning=Ik snap het navigate.for_km=Voor %1$s kilometer navigate.for_mi=Voor %1$s mijl diff --git a/core/src/main/resources/com/graphhopper/util/pl_PL.txt b/core/src/main/resources/com/graphhopper/util/pl_PL.txt index 6f1db4ea4f5..31873643e81 100644 --- a/core/src/main/resources/com/graphhopper/util/pl_PL.txt +++ b/core/src/main/resources/com/graphhopper/util/pl_PL.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=kontynuuj continue_onto=kontynuuj na %1$s @@ -44,13 +44,14 @@ web.way_contains_private=Trasa przez prywatne drogi web.way_contains_toll=Trasa jest płatna web.way_crosses_border=Trasa przebiega przez granicę państwową web.way_contains=Trasa przez %1$s +web.way_contains_restrictions= web.tracks=nieutwardzone drogi gruntowe web.steps=schody -web.footways= -web.steep_sections= -web.private_sections= +web.footways=chodniki +web.steep_sections=strome fragmenty +web.private_sections=tereny prywatne web.trunk_roads_warn= -web.get_off_bike_for= +web.get_off_bike_for=piechotą przez %1$s pt_transfer_to=przesiądź się na %1$s web.start_label=Początek web.intermediate_label=Punkt pośredni @@ -65,16 +66,19 @@ web.route=Trasa web.add_to_route=Dodaj punkt pośredni web.delete_from_route=Usuń z trasy web.open_custom_model_box=Otwórz okienko modelu niestandardowego -web.draw_areas_enabled= +web.draw_areas_enabled=Rysuj i zmieniaj obszar na mapie web.help_custom_model=Pomoc web.apply_custom_model=Zastosuj web.custom_model_enabled= -web.settings= -web.settings_close= +web.settings=Ustawienia +web.settings_close=Zamknij web.exclude_motorway_example=Pomijaj autostrady +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example=Ogranicz prędkość web.cargo_bike_example=Rower towarowy -web.prefer_bike_network= +web.prefer_bike_network=Preferuj ścieżki rowerowe web.exclude_area_example=Wyklucz obszar web.combined_example=Przykład łączony web.examples_custom_model=Przykłady @@ -94,6 +98,10 @@ web.via_hint=Przez web.from_hint=Z web.gpx_export_button=Eksportuj GPX web.gpx_button=GPX +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button=Ukryj web.details_button=Szczegóły web.to_hint=Do @@ -105,6 +113,11 @@ web.pt_route_info_walking=przybycie o %1$s pieszo (%2$s) web.locations_not_found=Nie można wyznaczyć trasy. Nie znaleziono lokalizacji. web.search_with_nominatim=Szukaj z Nominatimem web.powered_by=Dostarczony przez +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Rower web.racingbike=Rower wyścigowy web.mtb=Rower górski @@ -116,9 +129,24 @@ web.bus=Autobus web.truck=Ciężarówka web.staticlink=link web.motorcycle=Motocykl +web.scooter= web.back_to_map=Wróć web.distance_unit=Odległości w %1$s web.waiting_for_gps=Oczekiwanie na sygnał GPS... +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning=Rozumiem i akceptuję navigate.for_km=przez %1$s kilometrów navigate.for_mi=przez %1$s mil diff --git a/core/src/main/resources/com/graphhopper/util/pt_BR.txt b/core/src/main/resources/com/graphhopper/util/pt_BR.txt index 9a1368c5891..cdeac27be87 100644 --- a/core/src/main/resources/com/graphhopper/util/pt_BR.txt +++ b/core/src/main/resources/com/graphhopper/util/pt_BR.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=continuar continue_onto=continue na %1$s @@ -44,6 +44,7 @@ web.way_contains_private= web.way_contains_toll= web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint=Via web.from_hint=De web.gpx_export_button=Exportar GPX web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=Para @@ -105,6 +113,11 @@ web.pt_route_info_walking=chega às %1$s caminhando (%2$s) web.locations_not_found=Rota inviável. Localização(ões) não encontrada(s) na área. web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Bicicleta web.racingbike=Bicicleta de corrida web.mtb=Mountainbike @@ -116,9 +129,24 @@ web.bus=Ônibus web.truck=Caminhão web.staticlink=Link estático web.motorcycle=Motocicleta +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km= navigate.for_mi= diff --git a/core/src/main/resources/com/graphhopper/util/pt_PT.txt b/core/src/main/resources/com/graphhopper/util/pt_PT.txt index 209450e3bcb..93bbc8636d4 100644 --- a/core/src/main/resources/com/graphhopper/util/pt_PT.txt +++ b/core/src/main/resources/com/graphhopper/util/pt_PT.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=continuar continue_onto=continue na %1$s @@ -44,6 +44,7 @@ web.way_contains_private= web.way_contains_toll= web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint=Por web.from_hint=De web.gpx_export_button=Exportar GPX web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=Para @@ -105,6 +113,11 @@ web.pt_route_info_walking= web.locations_not_found=Roteamento impossível. Localização(ões) não encontrada(s) na área. web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Bicicleta web.racingbike=Bicicleta de corrida web.mtb=Bicicleta de montanha @@ -116,9 +129,24 @@ web.bus=Autocarro web.truck=Camião web.staticlink=Ligação permanente web.motorcycle=Motocicleta +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km= navigate.for_mi= diff --git a/core/src/main/resources/com/graphhopper/util/ro.txt b/core/src/main/resources/com/graphhopper/util/ro.txt index 0564ffab77f..498634a88c2 100644 --- a/core/src/main/resources/com/graphhopper/util/ro.txt +++ b/core/src/main/resources/com/graphhopper/util/ro.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=continuă continue_onto=continuă pe %1$s @@ -33,7 +33,7 @@ small_way=potecă paved=pavat unpaved=nepavat stopover=escala %1$s -roundabout_enter=Intrați în sensul giratoriu +roundabout_enter=Intrați în sensul giratoriu roundabout_exit=În sensul giratoriu folosiți ieșirea %1$s roundabout_exit_onto=În sensul giratoriu folosiți ieșirea %1$s către %2$s web.total_ascend=urcare %1$s @@ -44,6 +44,7 @@ web.way_contains_private= web.way_contains_toll= web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint=Prin web.from_hint=De la web.gpx_export_button=Exportă GPX web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=La @@ -105,6 +113,11 @@ web.pt_route_info_walking= web.locations_not_found=Traseul nu este posibil. Locul(locurile) nu pot fi găsite în zonă. web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=bicicletă web.racingbike=bicicletă de curse web.mtb=Mountainbike @@ -116,9 +129,24 @@ web.bus=Autobuz web.truck=Camion web.staticlink=link web.motorcycle=Motocicletă +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km= navigate.for_mi= diff --git a/core/src/main/resources/com/graphhopper/util/ru.txt b/core/src/main/resources/com/graphhopper/util/ru.txt index 2fa03442325..8f7d96e812e 100644 --- a/core/src/main/resources/com/graphhopper/util/ru.txt +++ b/core/src/main/resources/com/graphhopper/util/ru.txt @@ -1,17 +1,17 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=Продолжите движение -continue_onto=Продолжите движение по %1$s +continue_onto=Продолжите движение по %1$s finish=Цель достигнута! keep_left=Держитесь левее keep_right=Держитесь правее turn_onto=%1$s на %2$s turn_left=Поверните налево turn_right=Поверните направо -turn_slight_left=Плавно поверните налево -turn_slight_right=Плавно поверните направо +turn_slight_left=Плавно поверните налево +turn_slight_right=Плавно поверните направо turn_sharp_left=Резко поверните налево -turn_sharp_right=Резко поверните направо +turn_sharp_right=Резко поверните направо u_turn=Развернитесь toward_destination= toward_destination_ref_only= @@ -33,9 +33,9 @@ small_way=узкий путь paved=с покрытием unpaved=без покрытия stopover=остановка %1$s -roundabout_enter=Поверните на кольцо -roundabout_exit=Сверните на %1$s-й съезд -roundabout_exit_onto=Сверните на %1$s-й съезд на %2$s +roundabout_enter=Поверните на кольцо +roundabout_exit=Сверните на %1$s-й съезд +roundabout_exit_onto=Сверните на %1$s-й съезд на %2$s web.total_ascend=подъём на %1$s web.total_descend=спуск на %1$s web.way_contains_ford=На пути есть брод @@ -44,6 +44,7 @@ web.way_contains_private=частная дорога web.way_contains_toll=платная дорога web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -58,8 +59,8 @@ web.end_label=Конец web.set_start=Установить начало здесь web.set_intermediate=Установить промежуточной точкой web.set_end=Установить конец здесь -web.center_map=Сдвинуть карту сюда -web.show_coords=Показать координаты +web.center_map=Сдвинуть карту сюда +web.show_coords=Показать координаты web.query_osm= web.route=Маршрут web.add_to_route= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -82,7 +86,7 @@ web.marker=Значок web.gh_offline_info=GraphHopper API недоступен? web.refresh_button=Обновить страницу web.server_status=Статус сервера -web.zoom_in=Приблизить +web.zoom_in=Приблизить web.zoom_out=Отдалить web.drag_to_reorder=Переместите путевую точку web.route_timed_out= @@ -94,17 +98,26 @@ web.via_hint=Через web.from_hint=От web.gpx_export_button=Экспорт GPX web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=До web.route_info=%1$s займет %2$s web.search_button=Поиск web.more_button=еще -web.pt_route_info=Прибытие в %1$s с %2$s пересадками (%3$s) -web.pt_route_info_walking=Прибытие в %1$s пешком (%2$s)  +web.pt_route_info=Прибытие в %1$s с %2$s пересадками (%3$s) +web.pt_route_info_walking=Прибытие в %1$s пешком (%2$s) web.locations_not_found=Построить маршрут невозможно. Не определено местоположение. web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Велосипед web.racingbike=Гоночный велосипед web.mtb=Горный велосипед @@ -116,9 +129,24 @@ web.bus=Автобус web.truck=Грузовой автомобиль web.staticlink=Постоянная ссылка web.motorcycle=Мотоцикл +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km=За %1$s километрах navigate.for_mi=В %1$s милях diff --git a/core/src/main/resources/com/graphhopper/util/sk.txt b/core/src/main/resources/com/graphhopper/util/sk.txt index 6cb32a44f8a..240eeed8676 100644 --- a/core/src/main/resources/com/graphhopper/util/sk.txt +++ b/core/src/main/resources/com/graphhopper/util/sk.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=pokračujte continue_onto=pokračujte na %1$s @@ -44,6 +44,7 @@ web.way_contains_private=Trasa obsahuje súkromné cesty. web.way_contains_toll=Trasa obsahuje spoplatnené úseky. web.way_crosses_border=Trasa križuje štátnu hranicu. web.way_contains=Trasa obsahuje %1$s +web.way_contains_restrictions= web.tracks=nespevnené cesty web.steps=schody web.footways=chodníky @@ -72,6 +73,9 @@ web.custom_model_enabled=Je aktívny vlastný model web.settings=Nastavenia web.settings_close=Zavrieť web.exclude_motorway_example=Vynechať diaľnice +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example=Limit rýchlosti web.cargo_bike_example= web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint=Cez web.from_hint=Odkiaľ web.gpx_export_button=Export do GPX web.gpx_button=GPX +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button=Skryť web.details_button=Podrobnosti web.to_hint=Kam @@ -105,6 +113,11 @@ web.pt_route_info_walking=príchod o %1$s iba chôdzou (%2$s) web.locations_not_found=Navigovanie nie je možné. Umiestnenie nebolo nájdené v oblasti. web.search_with_nominatim=Hľadať pomocou Nominatim-u web.powered_by=Beží na +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Bicykel web.racingbike=Cestný bicykel web.mtb=Horský bicykel @@ -116,9 +129,24 @@ web.bus=Autobus web.truck=Kamión web.staticlink=nemenný odkaz web.motorcycle=Motocykel +web.scooter= web.back_to_map=Späť web.distance_unit=Vzdialenosti sú v %1$s web.waiting_for_gps=Čakám na GPS signál... +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning=Rozumiem a súhlasím navigate.for_km=%1$s kilometrov navigate.for_mi=%1$s míľ diff --git a/core/src/main/resources/com/graphhopper/util/sl_SI.txt b/core/src/main/resources/com/graphhopper/util/sl_SI.txt index cbb34444b8b..921863c64a4 100644 --- a/core/src/main/resources/com/graphhopper/util/sl_SI.txt +++ b/core/src/main/resources/com/graphhopper/util/sl_SI.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=nadaljujte continue_onto=nadaljujte po %1$s @@ -44,6 +44,7 @@ web.way_contains_private= web.way_contains_toll= web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -91,9 +95,13 @@ web.current_location= web.searching_location= web.searching_location_failed= web.via_hint=Preko -web.from_hint=Od +web.from_hint=Od web.gpx_export_button=Izvozi GPX web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=Do @@ -105,6 +113,11 @@ web.pt_route_info_walking=prispe ob %1$s s samo hojo (%3$s) web.locations_not_found=Usmerjanje ni mogoče. Lokacije ni bilo mogoče najti na tem območju. web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Kolo web.racingbike=Cestno kolo web.mtb=Gorsko kolo @@ -116,9 +129,24 @@ web.bus=Avtobus web.truck=Tovornjak web.staticlink=Povezava web.motorcycle=Motorno kolo +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km= navigate.for_mi= diff --git a/core/src/main/resources/com/graphhopper/util/sr_RS.txt b/core/src/main/resources/com/graphhopper/util/sr_RS.txt index a5e066a61fd..9ae0813e892 100644 --- a/core/src/main/resources/com/graphhopper/util/sr_RS.txt +++ b/core/src/main/resources/com/graphhopper/util/sr_RS.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=nastavite continue_onto=nastavite na %1$s @@ -44,6 +44,7 @@ web.way_contains_private= web.way_contains_toll= web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint=Preko web.from_hint=Od web.gpx_export_button=Izvezi kao GPX web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=Do @@ -105,6 +113,11 @@ web.pt_route_info_walking= web.locations_not_found=Rutiranje nije moguće. Lokacije nisu pronađene na ovome području. web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Bicikl web.racingbike=Trkački motocikl web.mtb=Brdski bicikl @@ -116,9 +129,24 @@ web.bus=Autobus web.truck=Kamion web.staticlink=statička veza web.motorcycle=Motocikl +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km= navigate.for_mi= diff --git a/core/src/main/resources/com/graphhopper/util/sv_SE.txt b/core/src/main/resources/com/graphhopper/util/sv_SE.txt index 4a72366e8bb..473c10438e2 100644 --- a/core/src/main/resources/com/graphhopper/util/sv_SE.txt +++ b/core/src/main/resources/com/graphhopper/util/sv_SE.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=fortsätt continue_onto=fortsätt in på %1$s @@ -13,18 +13,18 @@ turn_slight_right=sväng svagt höger turn_sharp_left=sväng kraftigt vänster turn_sharp_right=sväng kraftigt höger u_turn=gör en u-sväng -toward_destination= %1$s och kör mot %2$s +toward_destination=%1$s och kör mot %2$s toward_destination_ref_only=%1$s mot %2$s toward_destination_with_ref=%1$s och ta %2$s mot %3$s unknown=okänd instruktionsskylt '%1$s' via=via -hour_abbr= tim -day_abbr= d -min_abbr= min -km_abbr= km -m_abbr= m -mi_abbr= mi -ft_abbr= ft +hour_abbr=tim +day_abbr=d +min_abbr=min +km_abbr=km +m_abbr=m +mi_abbr=mi +ft_abbr=ft road=gata off_bike=hoppa av cykeln cycleway=cykelväg @@ -44,6 +44,7 @@ web.way_contains_private=Privat väg web.way_contains_toll=Avgiftsbelagd väg web.way_crosses_border= web.way_contains=Rutten inkluderar %1$s +web.way_contains_restrictions= web.tracks= web.steps=steg web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint=Via web.from_hint=Från web.gpx_export_button=exportera GPX-fil web.gpx_button=GPX +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button=Göm web.details_button=Detaljer web.to_hint=Till @@ -105,6 +113,11 @@ web.pt_route_info_walking= web.locations_not_found=Kan inte beräkna rutt. Platsen eller platserna kan inte hittas i området. web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Cykel web.racingbike=Tävlingscykel web.mtb=Mountain bike @@ -116,9 +129,24 @@ web.bus=Buss web.truck=Lastbil web.staticlink=direktlänk web.motorcycle=Motorcykel +web.scooter= web.back_to_map=Backa web.distance_unit=Avstånden är i %1$s web.waiting_for_gps=Väntar på GPS signal... +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning=Jag förstår och godkänner navigate.for_km=i %1$s kilometer navigate.for_mi=i %1$s mil diff --git a/core/src/main/resources/com/graphhopper/util/tr.txt b/core/src/main/resources/com/graphhopper/util/tr.txt index d181076afe3..50d01c82e76 100644 --- a/core/src/main/resources/com/graphhopper/util/tr.txt +++ b/core/src/main/resources/com/graphhopper/util/tr.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=devam continue_onto=%1$s üstünde devam edin @@ -8,7 +8,7 @@ keep_right=sağdan ilerleyin turn_onto=%2$s üstünde %1$s turn_left=sola dönün turn_right=sağa dönün -turn_slight_left=hafif sola dönün +turn_slight_left=hafif sola dönün turn_slight_right=hafif sağa dönün turn_sharp_left=tam sola dönün turn_sharp_right=tam sağa dönün @@ -35,15 +35,16 @@ unpaved=Asfaltsız yol stopover=varış noktası %1$s roundabout_enter=dönel kavşağa gir roundabout_exit=dönel kavşaktan %1$s çıkışa girin -roundabout_exit_onto=dönel kavşaktan %2$s üzerinde %1$s çıkışa girin +roundabout_exit_onto=dönel kavşaktan %2$s üzerinde %1$s çıkışa girin web.total_ascend=%1$s toplam tırmanış web.total_descend=%1$s toplam alçalış web.way_contains_ford=Dikkat, yolda bir sığ yer var web.way_contains_ferry=feribota binin -web.way_contains_private=özel yol +web.way_contains_private=özel yol web.way_contains_toll=Ücretli yol web.way_crosses_border=Rota ülke sınırından geçmektedir web.way_contains=Rota %1$s içermektedir +web.way_contains_restrictions= web.tracks=Asfaltsız toprak yol web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example=Hız limiti web.cargo_bike_example= web.prefer_bike_network= @@ -83,7 +87,7 @@ web.gh_offline_info=GraphHopper arayüzüne ulaşılamıyor web.refresh_button=Sayfayı yinele web.server_status=Durum web.zoom_in=Yakınlaştır -web.zoom_out=Uzaklaştır +web.zoom_out=Uzaklaştır web.drag_to_reorder=Yeniden sıralamak için sürükle bırak web.route_timed_out=Rota planlaması zaman aşımına uğradı web.route_request_failed=Rota oluşturulamadı @@ -94,6 +98,10 @@ web.via_hint=Yoluyla web.from_hint=Şuradan web.gpx_export_button=GPX dışa aktar web.gpx_button=GPX +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button=Gizle web.details_button=Ayrıntılar web.to_hint=Yönüne @@ -105,6 +113,11 @@ web.pt_route_info_walking=yürüyerek varış saati %1$s (%2$s) web.locations_not_found=Rota oluşturulamadı. Bölgedeki yer(ler) bulunamadı. web.search_with_nominatim=Nominatim ile ara web.powered_by=... tarafından desteklenmektedir +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Bisiklet web.racingbike=Yarış Bisikleti web.mtb=Dağ Bisikleti @@ -116,11 +129,26 @@ web.bus=Otobüs web.truck=Kamyon web.staticlink=kalıcı bağlantı web.motorcycle=motosiklet +web.scooter= web.back_to_map=geri web.distance_unit=Mesafe birimi %1$s web.waiting_for_gps=GPS sinyali bekleniyor +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning=Anladım ve kabul ediyorum -navigate.for_km= %1$s kilometre boyunca +navigate.for_km=%1$s kilometre boyunca navigate.for_mi=%1$s mil boyunca navigate.full_screen_for_navigation= navigate.in_km_singular=1 kilometre sonra diff --git a/core/src/main/resources/com/graphhopper/util/uk.txt b/core/src/main/resources/com/graphhopper/util/uk.txt index f8340ab064e..6cf45fb9413 100644 --- a/core/src/main/resources/com/graphhopper/util/uk.txt +++ b/core/src/main/resources/com/graphhopper/util/uk.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=продовжуйте continue_onto=продовжуйте по %1$s @@ -18,13 +18,13 @@ toward_destination_ref_only= toward_destination_with_ref= unknown=Невідома інструкція %1$s via=через -hour_abbr= год -day_abbr= д -min_abbr= хв -km_abbr= км -m_abbr= м -mi_abbr= милі -ft_abbr= фути +hour_abbr=год +day_abbr=д +min_abbr=хв +km_abbr=км +m_abbr=м +mi_abbr=милі +ft_abbr=фути road=дорога off_bike=злізьте з велосипеда cycleway=велосипедна доріжка @@ -44,6 +44,7 @@ web.way_contains_private= web.way_contains_toll= web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -94,17 +98,26 @@ web.via_hint=через web.from_hint=Від web.gpx_export_button=Експорт в GPX web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=До web.route_info=%1$s займе %2$s web.search_button=Пошук web.more_button=ще -web.pt_route_info=Прибуття до %1$s з %2$s пересадками (%3$s) -web.pt_route_info_walking=Прибуття до %1$s пішки (%2$s)  +web.pt_route_info=Прибуття до %1$s з %2$s пересадками (%3$s) +web.pt_route_info_walking=Прибуття до %1$s пішки (%2$s) web.locations_not_found=Побудова маршруту неможлива. Місцезнаходження не визначено. web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Велосипед web.racingbike=Шосейний велосипед web.mtb=Гірський велосипед @@ -116,9 +129,24 @@ web.bus=Автобус web.truck=Велика вантажівка web.staticlink=статичне посилання web.motorcycle=Мотоцикл +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km= navigate.for_mi= diff --git a/core/src/main/resources/com/graphhopper/util/uz.txt b/core/src/main/resources/com/graphhopper/util/uz.txt index af0e25159c0..bb600976af1 100644 --- a/core/src/main/resources/com/graphhopper/util/uz.txt +++ b/core/src/main/resources/com/graphhopper/util/uz.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=Harakatni davom ettiring continue_onto=%1$s bo'ylab harakatni davom ettiring @@ -8,16 +8,16 @@ keep_right=O'ngroq tuting turn_onto=%1$s dan %2$s ga turn_left=Chapga buriling turn_right=O'ngga buriling -turn_slight_left=Yengil chapga buriling -turn_slight_right=Yengil o'ngga buriling -turn_sharp_left=Keskin chapga buriling -turn_sharp_right=Keskin o'ngga buriling -u_turn=Orqaga qayriling +turn_slight_left=Yengil chapga buriling +turn_slight_right=Yengil o'ngga buriling +turn_sharp_left=Keskin chapga buriling +turn_sharp_right=Keskin o'ngga buriling +u_turn=Orqaga qayriling toward_destination= toward_destination_ref_only= toward_destination_with_ref= unknown=Noma'lum qo'llanma '%1$s' -via=orqali +via=orqali hour_abbr=soat day_abbr=kun min_abbr=daqiqa @@ -25,25 +25,26 @@ km_abbr=km m_abbr=m mi_abbr=mi ft_abbr=ft -road=yo'l +road=yo'l off_bike=Velosipeddan tushing cycleway=velosiped yo'lakchasi -way=yo'l -small_way=tor yo'l +way=yo'l +small_way=tor yo'l paved=yopiqlik bilan unpaved=yopiq emas stopover=%1$s da to'xtash -roundabout_enter=Aylanma -roundabout_exit=%1$s-chi chiqish yo'liga buring -roundabout_exit_onto=%1$s-chi chiqish yo'li %2$s ga buring -web.total_ascend=%1$s ga ko'tarilish -web.total_descend=%1$s ga tushish +roundabout_enter=Aylanma +roundabout_exit=%1$s-chi chiqish yo'liga buring +roundabout_exit_onto=%1$s-chi chiqish yo'li %2$s ga buring +web.total_ascend=%1$s ga ko'tarilish +web.total_descend=%1$s ga tushish web.way_contains_ford=Yo'lda ford bor -web.way_contains_ferry=paromga o'tiring -web.way_contains_private=xususiy yo'l -web.way_contains_toll=pullik yo'l +web.way_contains_ferry=paromga o'tiring +web.way_contains_private=xususiy yo'l +web.way_contains_toll=pullik yo'l web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -58,8 +59,8 @@ web.end_label=Tamom web.set_start=Boshlash nuqtasi deb belgilash web.set_intermediate=Oraliq nuqtasi deb belgilash web.set_end=Yakunlash nuqtasi deb belgilash -web.center_map=Xaritani shu yerga siljitish -web.show_coords=Kordinatalarni ko'rsatish +web.center_map=Xaritani shu yerga siljitish +web.show_coords=Kordinatalarni ko'rsatish web.query_osm= web.route=Marshrut web.add_to_route= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -81,7 +85,7 @@ web.examples_custom_model= web.marker=Belgi web.gh_offline_info=GraphHopper API mavjud emasmi? web.refresh_button=Sahifani yangilash -web.server_status=Server holati +web.server_status=Server holati web.zoom_in=Yaqinlashtirish web.zoom_out=Uzoqlashtirish web.drag_to_reorder=Yoʻnalish nuqtasini koʻchirish @@ -94,6 +98,10 @@ web.via_hint=orqali web.from_hint=dan web.gpx_export_button=GPX yuklab olish web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=gacha @@ -103,9 +111,14 @@ web.more_button=yana web.pt_route_info= web.pt_route_info_walking=%1$s ga piyoda yetib kelish (%2$s) web.locations_not_found=Marshrut qurish imkonsiz. Joylashuvingiz aniqlanmadi -web.search_with_nominatim=Nominatim orqali qidirish +web.search_with_nominatim=Nominatim orqali qidirish web.powered_by= -web.bike=Velosiped +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= +web.bike=Velosiped web.racingbike=Poyga velosipedi web.mtb=Tog' velosipedi web.car=Avtoulov @@ -116,9 +129,24 @@ web.bus=Avtobus web.truck=Yuk mashinasi web.staticlink=Doimiy havola web.motorcycle=Mototsikl +web.scooter= web.back_to_map=Xaritaga qaytish web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km=%1$s kilometr uchun navigate.for_mi= @@ -131,7 +159,7 @@ navigate.in_mi= navigate.in_ft= navigate.reroute= navigate.start_navigation= -navigate.then=So'ng +navigate.then=So'ng navigate.thenSign= navigate.turn_navigation_settings_title= navigate.vector_tiles_for_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/vi_VN.txt b/core/src/main/resources/com/graphhopper/util/vi_VN.txt index b2d29c175ca..11d59a0e419 100644 --- a/core/src/main/resources/com/graphhopper/util/vi_VN.txt +++ b/core/src/main/resources/com/graphhopper/util/vi_VN.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=tiếp tục continue_onto=tiếp tục đến %1$s @@ -37,13 +37,14 @@ roundabout_enter=Đi vào vòng xoay roundabout_exit=Tại vòng xoay, rẽ lối rẽ %1$s roundabout_exit_onto=Tại vòng xoay, rẽ lối rẽ %1$s vào đường %2$s web.total_ascend=Đi tiếp %1$s nữa -web.total_descend=Tổng số hạ %1$s +web.total_descend=Tổng số hạ %1$s web.way_contains_ford=Chú ý, có khúc sông cạn trên đường web.way_contains_ferry= web.way_contains_private= web.way_contains_toll=đường thu phí web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint=Qua web.from_hint=Từ web.gpx_export_button=Xuất GPX web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=Đến @@ -105,6 +113,11 @@ web.pt_route_info_walking=Đến lúc %1$s chỉ bằng cách đi bộ (%2$s) web.locations_not_found=Không tìm thấy lộ trình! Các điểm đã chọn không tìm thấy trong vùng này. web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=Xe đạp web.racingbike=Xe đạp đua web.mtb=Xe leo núi @@ -116,9 +129,24 @@ web.bus=Xe buýt web.truck=Xe tải web.staticlink=liên kết tĩnh web.motorcycle=Mô tô +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km=Khoảng %1$s km navigate.for_mi=Khoảng %1$s dặm diff --git a/core/src/main/resources/com/graphhopper/util/zh_CN.txt b/core/src/main/resources/com/graphhopper/util/zh_CN.txt index 1238111d15d..c9ed5fd0eb5 100644 --- a/core/src/main/resources/com/graphhopper/util/zh_CN.txt +++ b/core/src/main/resources/com/graphhopper/util/zh_CN.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=继续 continue_onto=继续行驶到 %1$s @@ -30,8 +30,8 @@ off_bike=下自行车 cycleway=自行车道 way=路 small_way=小路 -paved=路面铺就 -unpaved=路面未铺就 +paved=铺好的路面 +unpaved=没有铺好的路面 stopover=中途点 %1$s roundabout_enter=进入环岛 roundabout_exit=在环岛内,使用 %1$s 出口出环岛 @@ -44,13 +44,14 @@ web.way_contains_private=路径中包含私有道路 web.way_contains_toll=路径中包含收费路段 web.way_crosses_border=路径中跨越了国界 web.way_contains=路径中包含%1$s +web.way_contains_restrictions=可能存在访问限制的路线 web.tracks=未铺设的土路 web.steps=台阶 -web.footways= -web.steep_sections= -web.private_sections= -web.trunk_roads_warn= -web.get_off_bike_for= +web.footways=人行道 +web.steep_sections=陡峭路段 +web.private_sections=私人路段 +web.trunk_roads_warn=路线中可能包含潜在危险的主干道或其他更糟糕的道路条件 +web.get_off_bike_for=骑行者必须下车并推行自行车 %1$s pt_transfer_to=换乘%1$s web.start_label=起点 web.intermediate_label=途经点 @@ -65,16 +66,19 @@ web.route=路线 web.add_to_route=增加位置 web.delete_from_route=从线路中移除 web.open_custom_model_box=打开自定义模型选项 -web.draw_areas_enabled= +web.draw_areas_enabled= 在地图上绘制和修改区域 web.help_custom_model=帮助 web.apply_custom_model=应用 -web.custom_model_enabled= -web.settings= -web.settings_close= +web.custom_model_enabled=激活自定义模型 +web.settings=设置 +web.settings_close=关闭 web.exclude_motorway_example=排除高速公路 +web.exclude_disneyland_paris_example=请避开巴黎迪士尼乐园 +web.simple_electric_car_example=适合电动汽车行驶的路线 +web.avoid_tunnels_bridges_example=请避开桥梁和隧道 web.limit_speed_example=限速 -web.cargo_bike_example= -web.prefer_bike_network= +web.cargo_bike_example=货运自行车 +web.prefer_bike_network=优先选择自行车道 web.exclude_area_example=排除区域 web.combined_example=综合范例 web.examples_custom_model=范例 @@ -94,6 +98,10 @@ web.via_hint=途经点 web.from_hint=起点 web.gpx_export_button=GPX 格式导出 web.gpx_button=GPX +web.settings_gpx_export=GPX导出 +web.settings_gpx_export_trk=轨迹 +web.settings_gpx_export_rte=路线 +web.settings_gpx_export_wpt=航点 web.hide_button=隐藏 web.details_button=详情 web.to_hint=终点 @@ -105,6 +113,11 @@ web.pt_route_info_walking=仅需步行,在 %1$s 到达 (%2$s) web.locations_not_found=无法导航,因为地点未找到。 web.search_with_nominatim=用 Nominatim 搜索 web.powered_by=Powered by +web.info=信息 +web.feedback=反馈 +web.imprint=印记 +web.privacy=个人隐私 +web.terms=条款 web.bike=自行车 web.racingbike=竞技自行车 web.mtb=山地自行车 @@ -116,23 +129,38 @@ web.bus=公交车 web.truck=卡车 web.staticlink=永久链接 web.motorcycle=摩托车 +web.scooter=滑板车 web.back_to_map=返回 web.distance_unit=距离单位:%1$s web.waiting_for_gps=正在搜索 GPS 信号…… +web.elevation=高程(地面某点相对于海平面或其他参考平面的垂直距离) +web.slope=坡度 +web.towerslope=塔形坡 +web.country=国家/乡村 +web.surface=表面 +web.road_environment=道路环境 +web.road_access=道路通行 +web.road_class=道路等级 +web.max_speed=最高速度 +web.average_speed=平均速度 +web.track_type=轨道类型 +web.toll=收费 +web.next=下一页 +web.back=返回 navigate.accept_risks_after_warning=我理解并同意 -navigate.for_km=行驶 %1$skm +navigate.for_km=行驶 %1$s km navigate.for_mi=行驶 %1$s 英里 -navigate.full_screen_for_navigation= +navigate.full_screen_for_navigation=全屏 navigate.in_km_singular=1km 后 navigate.in_km=%1$skm 后 navigate.in_m=%1$sm 后 navigate.in_mi_singular=1 英里后 navigate.in_mi=%1$s 英里后 navigate.in_ft=%1$s 英尺后 -navigate.reroute= -navigate.start_navigation= +navigate.reroute=重新规划路线 +navigate.start_navigation=导航 navigate.then=然后 -navigate.thenSign= -navigate.turn_navigation_settings_title= -navigate.vector_tiles_for_navigation= +navigate.thenSign=然后 +navigate.turn_navigation_settings_title=逐向导航 +navigate.vector_tiles_for_navigation=使用矢量加速 navigate.warning=警告:该应用程序处于早期实验阶段,使用时风险自负! diff --git a/core/src/main/resources/com/graphhopper/util/zh_HK.txt b/core/src/main/resources/com/graphhopper/util/zh_HK.txt index e639a5eac24..6cc10de2c59 100644 --- a/core/src/main/resources/com/graphhopper/util/zh_HK.txt +++ b/core/src/main/resources/com/graphhopper/util/zh_HK.txt @@ -1,10 +1,10 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=繼續 continue_onto=繼續行駛到 %1$s finish=到達目的地 -keep_left= -keep_right= +keep_left=保持左行 +keep_right=保持右行 turn_onto=%1$s 到 %2$s turn_left=左轉 turn_right=右轉 @@ -12,11 +12,11 @@ turn_slight_left=左轉 turn_slight_right=右轉 turn_sharp_left=左急轉 turn_sharp_right=右急轉 -u_turn= -toward_destination= -toward_destination_ref_only= -toward_destination_with_ref= -unknown= +u_turn=轉頭 +toward_destination=%1$s,向 %2$s 行駛 +toward_destination_ref_only=往 %2$s 方向%1$s +toward_destination_with_ref=%1$s,由 %2$s 往 %3$s 方向行駛 +unknown=未知標誌牌“%1$s” via=途經 hour_abbr=小時 day_abbr=天 @@ -26,113 +26,141 @@ m_abbr=米 mi_abbr=英里 ft_abbr=英尺 road=道路 -off_bike= +off_bike=落單車 cycleway=單車徑 way=道路 small_way=小路 -paved= -unpaved= +paved=鋪好的路面 +unpaved=没有鋪好的路面 stopover=中途站 %1$s roundabout_enter=進入迴旋處 roundabout_exit=使用 %1$s 出口離開迴旋處 roundabout_exit_onto=使用 %1$s 出口離開迴旋處到 %2$s web.total_ascend=總共上昇 %1$s web.total_descend=總共下降 %1$s -web.way_contains_ford= -web.way_contains_ferry= -web.way_contains_private= -web.way_contains_toll= -web.way_crosses_border= -web.way_contains= -web.tracks= -web.steps= -web.footways= -web.steep_sections= -web.private_sections= -web.trunk_roads_warn= -web.get_off_bike_for= -pt_transfer_to= +web.way_contains_ford=路径中包含河灘 +web.way_contains_ferry=路径中包含渡輪 +web.way_contains_private=路径中包含私人道路 +web.way_contains_toll=路径中包含收費路段 +web.way_crosses_border=路径跨越了國界 +web.way_contains=路径中包含%1$s +web.way_contains_restrictions=可能存在訪問限制的路線 +web.tracks=未鋪設的土路 +web.steps=台階 +web.footways=人行道 +web.steep_sections=陡峭路段 +web.private_sections=私人路段 +web.trunk_roads_warn=路線中可能包含潛在危險的主幹道或其他更糟糕的路況 +web.get_off_bike_for=騎行者必須下車並推行自行車 %1$s +pt_transfer_to=換乘%1$s web.start_label=起點 web.intermediate_label=途經點 web.end_label=目的地 web.set_start=設置為起點 web.set_intermediate=設置為途經點 web.set_end=設置為目的地 -web.center_map= +web.center_map=地圖居中 web.show_coords=顯示坐標 -web.query_osm= +web.query_osm=查詢 OSM web.route=路線 -web.add_to_route= +web.add_to_route=增加位置 web.delete_from_route=從路線移除 -web.open_custom_model_box= -web.draw_areas_enabled= -web.help_custom_model= -web.apply_custom_model= -web.custom_model_enabled= -web.settings= -web.settings_close= -web.exclude_motorway_example= -web.limit_speed_example= -web.cargo_bike_example= -web.prefer_bike_network= -web.exclude_area_example= -web.combined_example= -web.examples_custom_model= +web.open_custom_model_box=打開自定義模型選項 +web.draw_areas_enabled=在地圖上繪製和修改區域 +web.help_custom_model=幫助 +web.apply_custom_model=應用 +web.custom_model_enabled=啟用自定義模型 +web.settings=設置 +web.settings_close=關閉 +web.exclude_motorway_example=排除高速公路 +web.exclude_disneyland_paris_example=請避開巴黎迪士尼樂園 +web.simple_electric_car_example=適合電動汽車行駛的路線 +web.avoid_tunnels_bridges_example=請避開橋樑和隧道 +web.limit_speed_example=限速 +web.cargo_bike_example=貨運自行車 +web.prefer_bike_network=優先選擇自行車道 +web.exclude_area_example=排除區域 +web.combined_example=綜合範例 +web.examples_custom_model=範例 web.marker=標記 web.gh_offline_info=無法連接 GraphHopper API web.refresh_button=刷新網頁 web.server_status=狀況 web.zoom_in=放大 web.zoom_out=縮小 -web.drag_to_reorder= -web.route_timed_out= -web.route_request_failed= -web.current_location= -web.searching_location= -web.searching_location_failed= +web.drag_to_reorder=拖動可重新排序 +web.route_timed_out=導航計算超時 +web.route_request_failed=導航請求失敗 +web.current_location=當前位置 +web.searching_location=正在搜索位置 +web.searching_location_failed=搜索位置失敗 web.via_hint=途經 web.from_hint=起點 web.gpx_export_button=GPX格式輸出 -web.gpx_button= -web.hide_button= -web.details_button= +web.gpx_button=GPX +web.settings_gpx_export=GPX導出 +web.settings_gpx_export_trk=軌跡 +web.settings_gpx_export_rte=路線 +web.settings_gpx_export_wpt=航點 +web.hide_button=隱藏 +web.details_button=詳情 web.to_hint=目的地 web.route_info=%1$s 需時 %2$s web.search_button=搜尋 web.more_button=更多 -web.pt_route_info= -web.pt_route_info_walking= -web.locations_not_found=找不到起點/目的地 -web.search_with_nominatim= -web.powered_by= +web.pt_route_info=换乘 %2$s 次,在 %1$s 到達 (%3$s) +web.pt_route_info_walking=僅需步行,在 %1$s 到達 (%2$s) +web.locations_not_found=無法導航,因為地點未找到。 +web.search_with_nominatim=用 Nominatim 搜索 +web.powered_by=Powered by +web.info=信息 +web.feedback=反饋 +web.imprint=印記 +web.privacy=個人隱私 +web.terms=條款 web.bike=單車 web.racingbike=競技單車 web.mtb=越野單車 web.car=汽車 web.foot=步行 -web.hike= +web.hike=徒步 web.small_truck=小型貨車 web.bus=巴士 web.truck=貨車 web.staticlink=鏈接 web.motorcycle=電單車 -web.back_to_map= -web.distance_unit= -web.waiting_for_gps= -navigate.accept_risks_after_warning= -navigate.for_km= -navigate.for_mi= -navigate.full_screen_for_navigation= -navigate.in_km_singular= -navigate.in_km= -navigate.in_m= -navigate.in_mi_singular= -navigate.in_mi= -navigate.in_ft= -navigate.reroute= -navigate.start_navigation= -navigate.then= -navigate.thenSign= -navigate.turn_navigation_settings_title= -navigate.vector_tiles_for_navigation= -navigate.warning= +web.scooter=滑板車 +web.back_to_map=返回 +web.distance_unit=距離單位:%1$s +web.waiting_for_gps=正在搜索 GPS 信號…… +web.elevation=高程(地面某點相對於海平面或其他參考平面的垂直距離) +web.slope=坡度 +web.towerslope=塔形坡 +web.country=國家/鄉村 +web.surface=表面 +web.road_environment=道路環境 +web.road_access=道路通行 +web.road_class=道路等級 +web.max_speed=最高速度 +web.average_speed=平均速度 +web.track_type=軌道類型 +web.toll=收費 +web.next=下一頁 +web.back=返回 +navigate.accept_risks_after_warning=我理解並同意 +navigate.for_km=行駛 %1$s km +navigate.for_mi=行駛 %1$s 英里 +navigate.full_screen_for_navigation=全屏 +navigate.in_km_singular=1km 後 +navigate.in_km=%1$skm 後 +navigate.in_m=%1$sm 後 +navigate.in_mi_singular=1 英里後 +navigate.in_mi=%1$s 英里後 +navigate.in_ft=%1$s 英尺後 +navigate.reroute=重新規劃路線 +navigate.start_navigation=導航 +navigate.then=然後 +navigate.thenSign=然後 +navigate.turn_navigation_settings_title=逐向導航 +navigate.vector_tiles_for_navigation=使用矢量加速 +navigate.warning=警告:該應用程序處於早期實驗階段,使用時風險自負! diff --git a/core/src/main/resources/com/graphhopper/util/zh_TW.txt b/core/src/main/resources/com/graphhopper/util/zh_TW.txt index f8213356932..a4468377e6b 100644 --- a/core/src/main/resources/com/graphhopper/util/zh_TW.txt +++ b/core/src/main/resources/com/graphhopper/util/zh_TW.txt @@ -1,4 +1,4 @@ -# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh +# do not edit manually, instead use spreadsheet from translations.md and script ./core/files/update-translations.sh continue=繼續 continue_onto=繼續行駛到 %1$s @@ -44,6 +44,7 @@ web.way_contains_private=私人道路 web.way_contains_toll=收費道路 web.way_crosses_border= web.way_contains= +web.way_contains_restrictions= web.tracks= web.steps= web.footways= @@ -72,6 +73,9 @@ web.custom_model_enabled= web.settings= web.settings_close= web.exclude_motorway_example= +web.exclude_disneyland_paris_example= +web.simple_electric_car_example= +web.avoid_tunnels_bridges_example= web.limit_speed_example= web.cargo_bike_example= web.prefer_bike_network= @@ -94,6 +98,10 @@ web.via_hint=途經 web.from_hint=起點 web.gpx_export_button=匯出GPS web.gpx_button= +web.settings_gpx_export= +web.settings_gpx_export_trk= +web.settings_gpx_export_rte= +web.settings_gpx_export_wpt= web.hide_button= web.details_button= web.to_hint=迄點 @@ -105,6 +113,11 @@ web.pt_route_info_walking=於 %1$s 抵達,僅步行 (%2$s) web.locations_not_found=無法進行規劃。無法在此區域內找到指定的地點 web.search_with_nominatim= web.powered_by= +web.info= +web.feedback= +web.imprint= +web.privacy= +web.terms= web.bike=自行車 web.racingbike=競技自行車 web.mtb=登山車 @@ -116,9 +129,24 @@ web.bus=公車 web.truck=貨車 web.staticlink=永久鏈結 web.motorcycle=摩托車 +web.scooter= web.back_to_map= web.distance_unit= web.waiting_for_gps= +web.elevation= +web.slope= +web.towerslope= +web.country= +web.surface= +web.road_environment= +web.road_access= +web.road_class= +web.max_speed= +web.average_speed= +web.track_type= +web.toll= +web.next= +web.back= navigate.accept_risks_after_warning= navigate.for_km= navigate.for_mi= diff --git a/core/src/test/java/com/graphhopper/GraphHopperProfileTest.java b/core/src/test/java/com/graphhopper/GraphHopperProfileTest.java index 38381a4945e..5d2b17dbc1a 100644 --- a/core/src/test/java/com/graphhopper/GraphHopperProfileTest.java +++ b/core/src/test/java/com/graphhopper/GraphHopperProfileTest.java @@ -23,10 +23,12 @@ import com.graphhopper.config.LMProfile; import com.graphhopper.config.Profile; import com.graphhopper.jackson.Jackson; +import com.graphhopper.routing.TestProfiles; import org.junit.jupiter.api.Test; import java.io.IOException; import java.util.Arrays; +import java.util.List; import static org.junit.jupiter.api.Assertions.*; @@ -37,12 +39,12 @@ public class GraphHopperProfileTest { @Test public void deserialize() throws IOException { ObjectMapper objectMapper = Jackson.newObjectMapper(); - String json = "{\"name\":\"my_car\",\"vehicle\":\"car\",\"weighting\":\"custom\",\"turn_costs\":true,\"foo\":\"bar\",\"baz\":\"buzz\"}"; + String json = "{\"name\":\"my_car\",\"weighting\":\"custom\",\"turn_costs\":{\"vehicle_types\":[\"motorcar\"]},\"foo\":\"bar\",\"baz\":\"buzz\"}"; Profile profile = objectMapper.readValue(json, Profile.class); assertEquals("my_car", profile.getName()); - assertEquals("car", profile.getVehicle()); + assertEquals(List.of("motorcar"), profile.getTurnCostsConfig().getVehicleTypes()); assertEquals("custom", profile.getWeighting()); - assertTrue(profile.isTurnCosts()); + assertTrue(profile.hasTurnCosts()); assertEquals(2, profile.getHints().toMap().size()); assertEquals("bar", profile.getHints().getString("foo", "")); assertEquals("buzz", profile.getHints().getString("baz", "")); @@ -52,49 +54,16 @@ public void deserialize() throws IOException { public void duplicateProfileName_error() { final GraphHopper hopper = createHopper(); assertIllegalArgument(() -> hopper.setProfiles( - new Profile("my_profile").setVehicle("car"), - new Profile("your_profile").setVehicle("car"), - new Profile("my_profile").setVehicle("car") + new Profile("my_profile"), + new Profile("your_profile"), + new Profile("my_profile") ), "Profile names must be unique. Duplicate name: 'my_profile'"); } - @Test - public void vehicleDoesNotExist_error() { - final GraphHopper hopper = new GraphHopper(); - hopper.setGraphHopperLocation(GH_LOCATION).setStoreOnFlush(false). - setProfiles(new Profile("profile").setVehicle("your_car")); - assertIllegalArgument(hopper::importOrLoad, "entry in vehicle list not supported: your_car"); - } - - @Test - public void vehicleDoesNotExist_error2() { - final GraphHopper hopper = new GraphHopper().setGraphHopperLocation(GH_LOCATION).setStoreOnFlush(false). - setProfiles(new Profile("profile").setVehicle("your_car")); - assertIllegalArgument(hopper::importOrLoad, "entry in vehicle list not supported: your_car"); - } - - @Test - public void oneVehicleTwoProfilesWithAndWithoutTC_noError() { - final GraphHopper hopper = createHopper(); - hopper.setProfiles( - new Profile("profile1").setVehicle("car").setTurnCosts(false), - new Profile("profile2").setVehicle("car").setTurnCosts(true)); - hopper.load(); - } - - @Test - public void oneVehicleTwoProfilesWithAndWithoutTC2_noError() { - final GraphHopper hopper = createHopper(); - hopper.setProfiles( - new Profile("profile2").setVehicle("car").setTurnCosts(true), - new Profile("profile1").setVehicle("car").setTurnCosts(false)); - hopper.load(); - } - @Test public void profileWithUnknownWeighting_error() { final GraphHopper hopper = createHopper(); - hopper.setProfiles(new Profile("profile").setVehicle("car").setWeighting("your_weighting")); + hopper.setProfiles(new Profile("profile").setWeighting("your_weighting")); assertIllegalArgument(hopper::importOrLoad, "Could not create weighting for profile: 'profile'", "Weighting 'your_weighting' not supported" @@ -104,7 +73,7 @@ public void profileWithUnknownWeighting_error() { @Test public void chProfileDoesNotExist_error() { final GraphHopper hopper = createHopper(); - hopper.setProfiles(new Profile("profile1").setVehicle("car")); + hopper.setProfiles(TestProfiles.constantSpeed("profile1")); hopper.getCHPreparationHandler().setCHProfiles(new CHProfile("other_profile")); assertIllegalArgument(hopper::importOrLoad, "CH profile references unknown profile 'other_profile'"); } @@ -112,7 +81,7 @@ public void chProfileDoesNotExist_error() { @Test public void duplicateCHProfile_error() { final GraphHopper hopper = createHopper(); - hopper.setProfiles(new Profile("profile").setVehicle("car")); + hopper.setProfiles(TestProfiles.constantSpeed("profile")); hopper.getCHPreparationHandler().setCHProfiles( new CHProfile("profile"), new CHProfile("profile") @@ -123,7 +92,7 @@ public void duplicateCHProfile_error() { @Test public void lmProfileDoesNotExist_error() { final GraphHopper hopper = createHopper(); - hopper.setProfiles(new Profile("profile1").setVehicle("car")); + hopper.setProfiles(TestProfiles.constantSpeed("profile1")); hopper.getLMPreparationHandler().setLMProfiles(new LMProfile("other_profile")); assertIllegalArgument(hopper::importOrLoad, "LM profile references unknown profile 'other_profile'"); } @@ -131,7 +100,7 @@ public void lmProfileDoesNotExist_error() { @Test public void duplicateLMProfile_error() { final GraphHopper hopper = createHopper(); - hopper.setProfiles(new Profile("profile").setVehicle("car")); + hopper.setProfiles(TestProfiles.constantSpeed("profile")); hopper.getLMPreparationHandler().setLMProfiles( new LMProfile("profile"), new LMProfile("profile") @@ -142,7 +111,7 @@ public void duplicateLMProfile_error() { @Test public void unknownLMPreparationProfile_error() { final GraphHopper hopper = createHopper(); - hopper.setProfiles(new Profile("profile").setVehicle("car")); + hopper.setProfiles(TestProfiles.constantSpeed("profile")); hopper.getLMPreparationHandler().setLMProfiles( new LMProfile("profile").setPreparationProfile("xyz") ); @@ -153,9 +122,9 @@ public void unknownLMPreparationProfile_error() { public void lmPreparationProfileChain_error() { final GraphHopper hopper = createHopper(); hopper.setProfiles( - new Profile("profile1").setVehicle("car"), - new Profile("profile2").setVehicle("bike"), - new Profile("profile3").setVehicle("foot") + TestProfiles.constantSpeed("profile1"), + TestProfiles.constantSpeed("profile2"), + TestProfiles.constantSpeed("profile3") ); hopper.getLMPreparationHandler().setLMProfiles( new LMProfile("profile1"), @@ -169,9 +138,9 @@ public void lmPreparationProfileChain_error() { public void noLMProfileForPreparationProfile_error() { final GraphHopper hopper = createHopper(); hopper.setProfiles( - new Profile("profile1").setVehicle("car"), - new Profile("profile2").setVehicle("bike"), - new Profile("profile3").setVehicle("foot") + TestProfiles.constantSpeed("profile1"), + TestProfiles.constantSpeed("profile2"), + TestProfiles.constantSpeed("profile3") ); hopper.getLMPreparationHandler().setLMProfiles( new LMProfile("profile1").setPreparationProfile("profile2") diff --git a/core/src/test/java/com/graphhopper/GraphHopperTest.java b/core/src/test/java/com/graphhopper/GraphHopperTest.java index c983a20fd5c..f76ac3f21e5 100644 --- a/core/src/test/java/com/graphhopper/GraphHopperTest.java +++ b/core/src/test/java/com/graphhopper/GraphHopperTest.java @@ -20,20 +20,17 @@ import com.graphhopper.config.CHProfile; import com.graphhopper.config.LMProfile; import com.graphhopper.config.Profile; +import com.graphhopper.config.TurnCostsConfig; import com.graphhopper.reader.ReaderWay; import com.graphhopper.reader.dem.SRTMProvider; import com.graphhopper.reader.dem.SkadiProvider; -import com.graphhopper.routing.ev.EdgeIntAccess; -import com.graphhopper.routing.ev.EncodedValueLookup; -import com.graphhopper.routing.ev.RoadEnvironment; -import com.graphhopper.routing.ev.Subnetwork; +import com.graphhopper.routing.TestProfiles; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.AllEdgesIterator; import com.graphhopper.routing.util.DefaultSnapFilter; import com.graphhopper.routing.util.EdgeFilter; import com.graphhopper.routing.util.countryrules.CountryRuleFactory; -import com.graphhopper.routing.util.parsers.DefaultTagParserFactory; import com.graphhopper.routing.util.parsers.OSMRoadEnvironmentParser; -import com.graphhopper.routing.util.parsers.TagParser; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.search.KVStorage; import com.graphhopper.storage.IntsRef; @@ -65,6 +62,7 @@ import java.util.concurrent.atomic.AtomicInteger; import static com.graphhopper.json.Statement.If; +import static com.graphhopper.json.Statement.Op.LIMIT; import static com.graphhopper.json.Statement.Op.MULTIPLY; import static com.graphhopper.util.GHUtility.createCircle; import static com.graphhopper.util.GHUtility.createRectangle; @@ -103,7 +101,7 @@ public void setup() { @ParameterizedTest @CsvSource({ - DIJKSTRA + ",false,705", + DIJKSTRA + ",false,703", ASTAR + ",false,361", DIJKSTRA_BI + ",false,340", ASTAR_BI + ",false,192", @@ -111,12 +109,11 @@ public void setup() { DIJKSTRA_BI + ",true,51" }) public void testMonacoDifferentAlgorithms(String algo, boolean withCH, int expectedVisitedNodes) { - final String vehicle = "car"; - GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setProfiles(new Profile("profile").setVehicle(vehicle)). + setEncodedValuesString("car_access, car_average_speed"). + setProfiles(TestProfiles.accessAndSpeed("profile", "car")). setStoreOnFlush(true); hopper.getCHPreparationHandler() .setCHProfiles(new CHProfile("profile")); @@ -131,9 +128,9 @@ public void testMonacoDifferentAlgorithms(String algo, boolean withCH, int expec assertEquals(expectedVisitedNodes, rsp.getHints().getLong("visited_nodes.sum", 0)); ResponsePath res = rsp.getBest(); - assertEquals(3586.9, res.getDistance(), .1); - assertEquals(274209, res.getTime(), 10); - assertEquals(91, res.getPoints().size()); + assertEquals(3587.6, res.getDistance(), .1); + assertEquals(274255, res.getTime(), 10); + assertEquals(105, res.getPoints().size()); assertEquals(43.7276852, res.getWaypoints().getLat(0), 1e-7); assertEquals(43.7495432, res.getWaypoints().getLat(1), 1e-7); @@ -148,12 +145,12 @@ public void testMonacoDifferentAlgorithms(String algo, boolean withCH, int expec @Test public void testMonacoWithInstructions() { final String profile = "profile"; - final String vehicle = "foot"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setProfiles(new Profile(profile).setCustomModel(new CustomModel().setDistanceInfluence(70d)).setVehicle(vehicle)). + setEncodedValuesString("foot_access, foot_priority, foot_average_speed"). + setProfiles(TestProfiles.accessSpeedAndPriority(profile, "foot")). setStoreOnFlush(true). importOrLoad(); @@ -161,11 +158,11 @@ public void testMonacoWithInstructions() { setAlgorithm(ASTAR).setProfile(profile)); // identify the number of counts to compare with CH foot route - assertEquals(1094, rsp.getHints().getLong("visited_nodes.sum", 0)); + assertEquals(1033, rsp.getHints().getLong("visited_nodes.sum", 0)); ResponsePath res = rsp.getBest(); - assertEquals(3535, res.getDistance(), 1); - assertEquals(115, res.getPoints().size()); + assertEquals(3536, res.getDistance(), 1); + assertEquals(131, res.getPoints().size()); assertEquals(43.7276852, res.getWaypoints().getLat(0), 1e-7); assertEquals(43.7495432, res.getWaypoints().getLat(1), 1e-7); @@ -176,7 +173,7 @@ public void testMonacoWithInstructions() { // TODO roundabout fine tuning -> enter + leave roundabout (+ two roundabouts -> is it necessary if we do not leave the street?) Translation tr = hopper.getTranslationMap().getWithFallBack(Locale.US); assertEquals("continue onto Avenue des Guelfes", il.get(0).getTurnDescription(tr)); - assertEquals("turn slight left onto Avenue des Papalins", il.get(1).getTurnDescription(tr)); + assertEquals("continue onto Avenue des Papalins", il.get(1).getTurnDescription(tr)); assertEquals("turn sharp right onto Quai Jean-Charles Rey", il.get(4).getTurnDescription(tr)); assertEquals("turn left", il.get(5).getTurnDescription(tr)); assertEquals("turn right onto Avenue Albert II", il.get(6).getTurnDescription(tr)); @@ -195,18 +192,18 @@ public void testMonacoWithInstructions() { assertEquals(7, il.get(4).getTime() / 1000); assertEquals(30, il.get(5).getTime() / 1000); - assertEquals(115, res.getPoints().size()); + assertEquals(131, res.getPoints().size()); } @Test public void withoutInstructions() { final String profile = "profile"; - final String vehicle = "foot"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setProfiles(new Profile(profile).setVehicle(vehicle)). + setEncodedValuesString("foot_access, foot_priority, foot_average_speed"). + setProfiles(TestProfiles.accessSpeedAndPriority(profile, "foot")). setStoreOnFlush(true). importOrLoad(); @@ -218,29 +215,30 @@ public void withoutInstructions() { hopper.getRouterConfig().setSimplifyResponse(false); GHResponse routeRsp = hopper.route(request); assertEquals(8, routeRsp.getBest().getInstructions().size()); - assertEquals(42, routeRsp.getBest().getPoints().size()); + assertEquals(50, routeRsp.getBest().getPoints().size()); // with simplification hopper.getRouterConfig().setSimplifyResponse(true); routeRsp = hopper.route(request); assertEquals(8, routeRsp.getBest().getInstructions().size()); - assertEquals(37, routeRsp.getBest().getPoints().size()); + assertEquals(46, routeRsp.getBest().getPoints().size()); // no instructions request.getHints().putObject("instructions", false); routeRsp = hopper.route(request); // the path is still simplified - assertEquals(39, routeRsp.getBest().getPoints().size()); + assertEquals(46, routeRsp.getBest().getPoints().size()); } @Test public void testUTurnInstructions() { final String profile = "profile"; - final String vehicle = "car"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setProfiles(new Profile(profile).setVehicle(vehicle).setTurnCosts(true).putHint(U_TURN_COSTS, 20)); + setEncodedValuesString("car_access, car_average_speed"). + setProfiles(TestProfiles.accessAndSpeed(profile, "car"). + setTurnCostsConfig(new TurnCostsConfig(List.of("motorcar", "motor_vehicle"), 20))); hopper.importOrLoad(); Translation tr = hopper.getTranslationMap().getWithFallBack(Locale.US); @@ -256,7 +254,7 @@ public void testUTurnInstructions() { ResponsePath res = rsp.getBest(); assertEquals(286, res.getDistance(), 1); // note that this includes the u-turn time for the second u-turn, but not the first, because it's a waypoint! - assertEquals(54351, res.getTime(), 1); + assertEquals(54358, res.getTime(), 1); // the route follows Avenue de l'Annonciade to the waypoint, u-turns there, then does a sharp right turn onto the parallel (dead-end) road, // does a u-turn at the dead-end and then arrives at the destination InstructionList il = res.getInstructions(); @@ -291,14 +289,13 @@ public void testUTurnInstructions() { } } - private void testImportCloseAndLoad(boolean ch, boolean lm, boolean sort) { - final String vehicle = "foot"; + private void testImportCloseAndLoad(boolean ch, boolean lm) { final String profileName = "profile"; GraphHopper hopper = new GraphHopper(). + setEncodedValuesString("foot_access, foot_priority, foot_average_speed"). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setStoreOnFlush(true). - setSortGraph(sort); + setStoreOnFlush(true); JsonFeature area51Feature = new JsonFeature(); area51Feature.setId("area51"); @@ -307,10 +304,10 @@ private void testImportCloseAndLoad(boolean ch, boolean lm, boolean sort) { new Coordinate(7.4198, 43.7355), new Coordinate(7.4207, 43.7344), new Coordinate(7.4174, 43.7345)})); - CustomModel customModel = new CustomModel().setDistanceInfluence(0d); + Profile profile = TestProfiles.accessSpeedAndPriority(profileName, "foot"); + CustomModel customModel = profile.getCustomModel(); customModel.getPriority().add(If("in_area51", MULTIPLY, "0.1")); customModel.getAreas().getFeatures().add(area51Feature); - Profile profile = new Profile(profileName).setCustomModel(customModel).setVehicle(vehicle); hopper.setProfiles(profile); if (ch) { @@ -352,8 +349,8 @@ private void testImportCloseAndLoad(boolean ch, boolean lm, boolean sort) { long sum = rsp.getHints().getLong("visited_nodes.sum", 0); assertNotEquals(sum, 0); assertTrue(sum < 155, "Too many nodes visited " + sum); - assertEquals(3535, bestPath.getDistance(), 1); - assertEquals(115, bestPath.getPoints().size()); + assertEquals(3536, bestPath.getDistance(), 1); + assertEquals(131, bestPath.getPoints().size()); } if (lm) { @@ -368,8 +365,8 @@ private void testImportCloseAndLoad(boolean ch, boolean lm, boolean sort) { long sum = rsp.getHints().getLong("visited_nodes.sum", 0); assertNotEquals(sum, 0); assertTrue(sum < 125, "Too many nodes visited " + sum); - assertEquals(3535, bestPath.getDistance(), 1); - assertEquals(115, bestPath.getPoints().size()); + assertEquals(3536, bestPath.getDistance(), 1); + assertEquals(131, bestPath.getPoints().size()); } // flexible @@ -383,46 +380,46 @@ private void testImportCloseAndLoad(boolean ch, boolean lm, boolean sort) { long sum = rsp.getHints().getLong("visited_nodes.sum", 0); assertNotEquals(sum, 0); assertTrue(sum > 120, "Too few nodes visited " + sum); - assertEquals(3535, bestPath.getDistance(), 1); - assertEquals(115, bestPath.getPoints().size()); + assertEquals(3536, bestPath.getDistance(), 1); + assertEquals(131, bestPath.getPoints().size()); hopper.close(); } @Test public void testImportThenLoadCH() { - testImportCloseAndLoad(true, false, false); + testImportCloseAndLoad(true, false); } @Test public void testImportThenLoadLM() { - testImportCloseAndLoad(false, true, false); + testImportCloseAndLoad(false, true); } @Test public void testImportThenLoadCHLM() { - testImportCloseAndLoad(true, true, false); + testImportCloseAndLoad(true, true); } @Test public void testImportThenLoadCHLMAndSort() { - testImportCloseAndLoad(true, true, true); + testImportCloseAndLoad(true, true); } @Test public void testImportThenLoadFlexible() { - testImportCloseAndLoad(false, false, false); + testImportCloseAndLoad(false, false); } @Test public void testAlternativeRoutes() { final String profile = "profile"; - final String vehicle = "foot"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setProfiles(new Profile(profile).setVehicle(vehicle)). + setEncodedValuesString("car_access, car_average_speed, foot_access, foot_priority, foot_average_speed"). + setProfiles(TestProfiles.accessSpeedAndPriority(profile, "foot")). setStoreOnFlush(true). importOrLoad(); @@ -450,12 +447,12 @@ public void testAlternativeRoutes() { @Test public void testAlternativeRoutesBike() { final String profile = "profile"; - final String vehicle = "bike"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(BAYREUTH). - setProfiles(new Profile(profile).setVehicle(vehicle)); + setEncodedValuesString("car_access, car_average_speed, bike_access, bike_priority, bike_average_speed"). + setProfiles(TestProfiles.accessSpeedAndPriority(profile, "bike")); hopper.importOrLoad(); GHRequest req = new GHRequest(50.028917, 11.496506, 49.985228, 11.600876). @@ -476,12 +473,12 @@ public void testAlternativeRoutesBike() { @Test public void testAlternativeRoutesCar() { final String profile = "profile"; - final String vehicle = "car"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(BAYREUTH). - setProfiles(new Profile(profile).setVehicle(vehicle)); + setEncodedValuesString("car_access, car_average_speed"). + setProfiles(TestProfiles.accessAndSpeed(profile, "car")); hopper.importOrLoad(); GHRequest req = new GHRequest(50.023513, 11.548862, 49.969441, 11.537876). @@ -503,18 +500,16 @@ public void testAlternativeRoutesCar() { @Test public void testPointHint() { final String profile = "profile"; - final String vehicle = "car"; - GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(LAUF). - setProfiles(new Profile(profile).setVehicle(vehicle)); + setEncodedValuesString("car_access,car_average_speed"). + setProfiles(TestProfiles.accessAndSpeed(profile, "car")); hopper.importOrLoad(); GHRequest req = new GHRequest(49.46553, 11.154669, 49.465244, 11.152577). setProfile(profile); - req.setPointHints(new ArrayList<>(asList("Laufamholzstraße, 90482, Nürnberg, Deutschland", ""))); GHResponse rsp = hopper.route(req); assertFalse(rsp.hasErrors(), rsp.getErrors().toString()); @@ -544,7 +539,8 @@ public void testForwardBackwardDestination() { GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(BAUTZEN). - setProfiles(new Profile(profile).setVehicle("car")); + setEncodedValuesString("car_access, car_average_speed"). + setProfiles(TestProfiles.accessAndSpeed(profile, "car")); hopper.setMinNetworkSize(0); hopper.importOrLoad(); @@ -554,6 +550,7 @@ public void testForwardBackwardDestination() { assertFalse(rsp.hasErrors(), rsp.getErrors().toString()); assertEquals("keep right and take B 96 toward Bautzen-West, Hoyerswerda", rsp.getBest().getInstructions().get(1).getTurnDescription(tr)); + assertEquals("Bautzen-West", rsp.getBest().getInstructions().get(1).getExtraInfoJSON().get("motorway_junction")); assertEquals("turn left onto Hoyerswerdaer Straße and drive toward Hoyerswerda, Kleinwelka", rsp.getBest().getInstructions().get(2).getTurnDescription(tr)); @@ -565,14 +562,15 @@ public void testForwardBackwardDestination() { @Test public void testNorthBayreuthAccessDestination() { final String profile = "profile"; - final String vehicle = "car"; + + Profile p = TestProfiles.accessAndSpeed(profile, "car"); + p.getCustomModel().addToPriority(If("road_access == DESTINATION", MULTIPLY, ".1")); GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(BAYREUTH). - setProfiles(new Profile(profile). - setCustomModel(new CustomModel().addToPriority(If("road_access == DESTINATION", MULTIPLY, ".1"))). - setVehicle(vehicle)); + setEncodedValuesString("car_access, road_access, car_average_speed"). + setProfiles(p); hopper.importOrLoad(); GHRequest req = new GHRequest(49.985307, 11.50628, 49.985731, 11.507465). @@ -586,12 +584,12 @@ public void testNorthBayreuthAccessDestination() { @Test public void testNorthBayreuthBlockedEdges() { final String profile = "profile"; - final String vehicle = "car"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(BAYREUTH). - setProfiles(new Profile(profile).setVehicle(vehicle)); + setEncodedValuesString("car_access, car_average_speed"). + setProfiles(TestProfiles.accessAndSpeed(profile, "car")); hopper.importOrLoad(); GHRequest req = new GHRequest(49.985272, 11.506151, 49.986107, 11.507202). @@ -699,18 +697,16 @@ public void testNorthBayreuthBlockedEdges() { @Test public void testCustomModel() { - final String vehicle = "car"; final String customCar = "custom_car"; final String emptyCar = "empty_car"; - CustomModel customModel = new CustomModel(); - customModel.addToSpeed(If("road_class == TERTIARY || road_class == TRACK", MULTIPLY, "0.1")); + Profile p1 = TestProfiles.accessAndSpeed(customCar, "car"); + p1.getCustomModel().addToSpeed(If("road_class == TERTIARY || road_class == TRACK", MULTIPLY, "0.1")); + Profile p2 = TestProfiles.accessAndSpeed(emptyCar, "car"); GraphHopper hopper = new GraphHopper(). + setEncodedValuesString("car_average_speed,car_access,road_class"). setGraphHopperLocation(GH_LOCATION). setOSMFile(BAYREUTH). - setProfiles( - new Profile(emptyCar).setCustomModel(new CustomModel()).setVehicle(vehicle), - new Profile(customCar).setCustomModel(customModel).setVehicle(vehicle) - ). + setProfiles(p1, p2). importOrLoad(); // standard car route @@ -718,7 +714,7 @@ public void testCustomModel() { // the custom car takes a detour in the north to avoid tertiary roads assertDistance(hopper, customCar, null, 13223); // we can achieve the same by using the empty profile and using a client-side model, we just need to copy the model because of the internal flag - assertDistance(hopper, emptyCar, new CustomModel(customModel), 13223); + assertDistance(hopper, emptyCar, new CustomModel(p1.getCustomModel()), 13223); // now we prevent using unclassified roads as well and the route goes even further north CustomModel strictCustomModel = new CustomModel().addToSpeed( If("road_class == TERTIARY || road_class == TRACK || road_class == UNCLASSIFIED", MULTIPLY, "0.1")); @@ -746,12 +742,14 @@ private void assertDistance(GraphHopper hopper, String profile, CustomModel cust @Test public void testMonacoVia() { final String profile = "profile"; - final String vehicle = "foot"; + Profile p = TestProfiles.accessSpeedAndPriority(profile, "foot"); + p.getCustomModel().setDistanceInfluence(10_000d); GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setProfiles(new Profile(profile).setCustomModel(new CustomModel().setDistanceInfluence(10_000d)).setVehicle(vehicle)). + setEncodedValuesString("foot_access, foot_priority, foot_average_speed"). + setProfiles(p). setStoreOnFlush(true). importOrLoad(); @@ -763,13 +761,13 @@ public void testMonacoVia() { setAlgorithm(ASTAR).setProfile(profile)); ResponsePath res = rsp.getBest(); - assertEquals(6874.2, res.getDistance(), .1); - assertEquals(170, res.getPoints().size()); + assertEquals(6874, res.getDistance(), 1); + assertEquals(197, res.getPoints().size()); InstructionList il = res.getInstructions(); assertEquals(30, il.size()); assertEquals("continue onto Avenue des Guelfes", il.get(0).getTurnDescription(tr)); - assertEquals("turn slight left onto Avenue des Papalins", il.get(1).getTurnDescription(tr)); + assertEquals("continue onto Avenue des Papalins", il.get(1).getTurnDescription(tr)); assertEquals("turn sharp right onto Quai Jean-Charles Rey", il.get(4).getTurnDescription(tr)); assertEquals("turn left", il.get(5).getTurnDescription(tr)); assertEquals("turn right onto Avenue Albert II", il.get(6).getTurnDescription(tr)); @@ -781,7 +779,7 @@ public void testMonacoVia() { assertEquals("turn left", il.get(24).getTurnDescription(tr)); assertEquals("turn right onto Quai Jean-Charles Rey", il.get(25).getTurnDescription(tr)); assertEquals("turn sharp left onto Avenue des Papalins", il.get(26).getTurnDescription(tr)); - assertEquals("turn slight right onto Avenue des Guelfes", il.get(28).getTurnDescription(tr)); + assertEquals("continue onto Avenue des Guelfes", il.get(28).getTurnDescription(tr)); assertEquals("arrive at destination", il.get(29).getTurnDescription(tr)); assertEquals(11, il.get(0).getDistance(), 1); @@ -832,12 +830,12 @@ public void testMonacoVia() { @Test public void testMonacoPathDetails() { final String profile = "profile"; - final String vehicle = "foot"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setProfiles(new Profile(profile).setVehicle(vehicle)). + setEncodedValuesString("foot_access, foot_priority, foot_average_speed"). + setProfiles(TestProfiles.accessSpeedAndPriority(profile, "foot")). setStoreOnFlush(true). importOrLoad(); @@ -864,12 +862,12 @@ public void testMonacoPathDetails() { @Test public void testMonacoEnforcedDirection() { final String profile = "profile"; - final String vehicle = "foot"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setProfiles(new Profile(profile).setVehicle(vehicle)). + setEncodedValuesString("foot_access, foot_priority, foot_average_speed"). + setProfiles(TestProfiles.accessSpeedAndPriority(profile, "foot")). setStoreOnFlush(true). importOrLoad(); @@ -882,8 +880,8 @@ public void testMonacoEnforcedDirection() { GHResponse rsp = hopper.route(req); ResponsePath res = rsp.getBest(); - assertEquals(921, res.getDistance(), 10.); - assertEquals(36, res.getPoints().size()); + assertEquals(575, res.getDistance(), 10.); + assertEquals(26, res.getPoints().size()); // headings must be in [0, 360) req = new GHRequest(). @@ -912,11 +910,11 @@ public void testMonacoEnforcedDirection() { @Test public void testHeading() { final String profile = "profile"; - final String vehicle = "car"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(BAYREUTH). - setProfiles(new Profile(profile).setVehicle(vehicle)). + setEncodedValuesString("car_access, car_average_speed"). + setProfiles(TestProfiles.accessAndSpeed(profile, "car")). setStoreOnFlush(true). importOrLoad(); @@ -942,12 +940,12 @@ public void testHeading() { @Test public void testMonacoMaxVisitedNodes() { final String profile = "profile"; - final String vehicle = "foot"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setProfiles(new Profile(profile).setVehicle(vehicle)). + setEncodedValuesString("foot_access, foot_priority, foot_average_speed"). + setProfiles(TestProfiles.accessSpeedAndPriority(profile, "foot")). setStoreOnFlush(true). importOrLoad(); @@ -959,7 +957,7 @@ public void testMonacoMaxVisitedNodes() { assertTrue(rsp.hasErrors()); Throwable throwable = rsp.getErrors().get(0); - assertTrue(throwable instanceof MaximumNodesExceededException); + assertInstanceOf(MaximumNodesExceededException.class, throwable); Object nodesDetail = ((MaximumNodesExceededException) throwable).getDetails().get(MaximumNodesExceededException.NODES_KEY); assertEquals(5, nodesDetail); @@ -972,12 +970,11 @@ public void testMonacoMaxVisitedNodes() { @Test public void testMonacoNonChMaxWaypointDistance() { final String profile = "profile"; - final String vehicle = "foot"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setProfiles(new Profile(profile).setVehicle(vehicle)). + setProfiles(TestProfiles.constantSpeed(profile)). setStoreOnFlush(true). importOrLoad(); @@ -1004,12 +1001,11 @@ public void testMonacoNonChMaxWaypointDistance() { @Test public void testMonacoNonChMaxWaypointDistanceMultiplePoints() { final String profile = "profile"; - final String vehicle = "foot"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setProfiles(new Profile(profile).setVehicle(vehicle)). + setProfiles(TestProfiles.constantSpeed(profile)). setStoreOnFlush(true). importOrLoad(); @@ -1043,12 +1039,12 @@ public void testMonacoNonChMaxWaypointDistanceMultiplePoints() { @Test public void testMonacoStraightVia() { final String profile = "profile"; - final String vehicle = "foot"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setProfiles(new Profile(profile).setVehicle(vehicle)). + setEncodedValuesString("foot_access, foot_priority, foot_average_speed"). + setProfiles(TestProfiles.accessSpeedAndPriority(profile, "foot")). setStoreOnFlush(true). importOrLoad(); @@ -1062,7 +1058,7 @@ public void testMonacoStraightVia() { ResponsePath res = rsp.getBest(); assertEquals(297, res.getDistance(), 5.); - assertEquals(23, res.getPoints().size()); + assertEquals(25, res.getPoints().size()); // test if start and first point are identical leading to an empty path, #788 rq = new GHRequest(). @@ -1078,12 +1074,12 @@ public void testMonacoStraightVia() { @Test public void testSRTMWithInstructions() { final String profile = "profile"; - final String vehicle = "foot"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setProfiles(new Profile(profile).setVehicle(vehicle)). + setEncodedValuesString("foot_access, foot_priority, foot_average_speed"). + setProfiles(TestProfiles.accessSpeedAndPriority(profile, "foot")). setStoreOnFlush(true); hopper.setElevationProvider(new SRTMProvider(DIR)); @@ -1093,8 +1089,8 @@ public void testSRTMWithInstructions() { setAlgorithm(ASTAR).setProfile(profile)); ResponsePath res = rsp.getBest(); - assertEquals(1614.3, res.getDistance(), .1); - assertEquals(55, res.getPoints().size()); + assertEquals(1617.5, res.getDistance(), .1); + assertEquals(68, res.getPoints().size()); assertTrue(res.getPoints().is3D()); InstructionList il = res.getInstructions(); @@ -1103,31 +1099,27 @@ public void testSRTMWithInstructions() { String str = res.getPoints().toString(); - assertEquals("(43.730684662577524,7.421283725164733,62.0), (43.7306797,7.4213823,66.0), " + - "(43.731098,7.4215463,45.0), (43.7312991,7.42159,45.0), (43.7313271,7.4214147,45.0), " + - "(43.7312506,7.4213664,45.0), (43.7312822,7.4211156,52.0), (43.7313624,7.4211455,52.0), " + - "(43.7313714,7.4211233,52.0), (43.7314858,7.4211734,52.0), (43.7315753,7.4208688,52.0), " + - "(43.7316061,7.4208249,52.0), (43.7316404,7.4208503,52.0), (43.7316741,7.4210502,52.0), " + - "(43.7316276,7.4214636,45.0), (43.7316391,7.4215065,45.0), (43.7316664,7.4214904,45.0), " + - "(43.7317185,7.4211861,52.0), (43.7319676,7.4206159,19.0), (43.732038,7.4203936,20.0), " + - "(43.7322266,7.4196414,26.0), (43.7323236,7.4192656,26.0), (43.7323374,7.4190461,26.0), " + - "(43.7323875,7.4189195,26.0), (43.731974,7.4181688,29.0), (43.7316421,7.4173042,23.0), " + - "(43.7315686,7.4170356,31.0), (43.7314269,7.4166815,31.0), (43.7312401,7.4163184,49.0), " + - "(43.7308286,7.4157613,29.399999618530273), (43.730662,7.4155599,22.0), " + - "(43.7303643,7.4151193,51.0), (43.729579,7.4137274,40.0), (43.7295167,7.4137244,40.0), " + - "(43.7294669,7.4137725,40.0), (43.7285987,7.4149068,23.0), (43.7285167,7.4149272,22.0), " + - "(43.7283974,7.4148646,22.0), (43.7285619,7.4151365,23.0), (43.7285774,7.4152444,23.0), " + - "(43.7285763,7.4159759,21.0), (43.7285238,7.4161982,20.0), (43.7284592,7.4163655,18.0), " + - "(43.7281669,7.4168192,18.0), (43.7281442,7.4169449,18.0), (43.7281684,7.4172435,14.0), " + - "(43.7282784,7.4179606,14.0), (43.7282757,7.418175,11.0), (43.7282319,7.4183683,11.0), " + - "(43.7281482,7.4185473,11.0), (43.7280654,7.4186535,11.0), (43.7279259,7.418748,11.0), " + - "(43.727779,7.4187731,11.0), (43.7276825,7.4190072,11.0), " + - "(43.72767974015672,7.419198523220426,11.0)", str); + assertEquals("(43.730684662577524,7.421283725164733,62.0), (43.7306797,7.4213823,66.0), (43.730949,7.4214948,66.0), " + + "(43.731098,7.4215463,45.0), (43.7312269,7.4215824,45.0), (43.7312991,7.42159,45.0), (43.7313271,7.4214147,45.0), " + + "(43.7312506,7.4213664,45.0), (43.7312546,7.4212741,52.0), (43.7312822,7.4211156,52.0), (43.7313624,7.4211455,52.0), " + + "(43.7313714,7.4211233,52.0), (43.7314858,7.4211734,52.0), (43.7315522,7.4209778,52.0), (43.7315753,7.4208688,52.0), " + + "(43.7316061,7.4208249,52.0), (43.7316404,7.4208503,52.0), (43.7316741,7.4210502,52.0), (43.7316276,7.4214636,45.0), " + + "(43.7316391,7.4215065,45.0), (43.7316664,7.4214904,45.0), (43.7316981,7.4212652,52.0), (43.7317185,7.4211861,52.0), " + + "(43.7319676,7.4206159,19.0), (43.732038,7.4203936,20.0), (43.732173,7.4198886,20.0), (43.7322266,7.4196414,26.0), " + + "(43.732266,7.4194654,26.0), (43.7323236,7.4192656,26.0), (43.7323374,7.4191503,26.0), (43.7323374,7.4190461,26.0), " + + "(43.7323875,7.4189195,26.0), (43.7323444,7.4188579,26.0), (43.731974,7.4181688,29.0), (43.7316421,7.4173042,23.0), " + + "(43.7315686,7.4170356,31.0), (43.7314269,7.4166815,31.0), (43.7312401,7.4163184,49.0), (43.7308286,7.4157613,29.399999618530273), " + + "(43.730662,7.4155599,22.0), (43.7303643,7.4151193,51.0), (43.729579,7.4137274,40.0), (43.7295167,7.4137244,40.0), (43.7294669,7.4137725,40.0), " + + "(43.7285987,7.4149068,23.0), (43.7285167,7.4149272,22.0), (43.7283974,7.4148646,22.0), (43.7285619,7.4151365,23.0), (43.7285774,7.4152444,23.0), " + + "(43.7285863,7.4157656,21.0), (43.7285763,7.4159759,21.0), (43.7285238,7.4161982,20.0), (43.7284592,7.4163655,18.0), (43.72838,7.4165003,18.0), " + + "(43.7281669,7.4168192,18.0), (43.7281442,7.4169449,18.0), (43.7281477,7.4170695,18.0), (43.7281684,7.4172435,14.0), (43.7282784,7.4179606,14.0), " + + "(43.7282757,7.418175,11.0), (43.7282319,7.4183683,11.0), (43.7281482,7.4185473,11.0), (43.7280654,7.4186535,11.0), (43.7279259,7.418748,11.0), " + + "(43.7278398,7.4187697,11.0), (43.727779,7.4187731,11.0), (43.7276825,7.4190072,11.0), (43.72767974015672,7.419198523220426,11.0)", str); assertEquals(84, res.getAscend(), 1e-1); assertEquals(135, res.getDescend(), 1e-1); - assertEquals(55, res.getPoints().size()); + assertEquals(68, res.getPoints().size()); assertEquals(new GHPoint3D(43.73068455771767, 7.421283689825812, 62.0), res.getPoints().get(0)); assertEquals(new GHPoint3D(43.727679637988224, 7.419198521975086, 11.0), res.getPoints().get(res.getPoints().size() - 1)); @@ -1140,30 +1132,30 @@ public void testSRTMWithInstructions() { @ValueSource(booleans = {true, false}) public void testSRTMWithTunnelInterpolation(boolean withTunnelInterpolation) { final String profile = "profile"; - final String vehicle = "foot"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setProfiles(new Profile(profile).setVehicle(vehicle)). + setEncodedValuesString("foot_access, foot_priority, foot_average_speed"). + setProfiles(TestProfiles.accessSpeedAndPriority(profile, "foot")). setStoreOnFlush(true); if (!withTunnelInterpolation) { - hopper.setTagParserFactory(new DefaultTagParserFactory() { + hopper.setImportRegistry(new DefaultImportRegistry() { @Override - public TagParser create(EncodedValueLookup lookup, String name, PMap properties) { - TagParser parser = super.create(lookup, name, properties); - if (name.equals("road_environment")) - parser = new OSMRoadEnvironmentParser(lookup.getEnumEncodedValue(RoadEnvironment.KEY, RoadEnvironment.class)) { - @Override - public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay readerWay, IntsRef relationFlags) { - // do not change RoadEnvironment to avoid triggering tunnel interpolation - } - }; - return parser; + public ImportUnit createImportUnit(String name) { + ImportUnit importUnit = super.createImportUnit(name); + if ("road_environment".equals(name)) + importUnit = ImportUnit.create(name, props -> RoadEnvironment.create(), + (lookup, props) -> new OSMRoadEnvironmentParser(lookup.getEnumEncodedValue(RoadEnvironment.KEY, RoadEnvironment.class)) { + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay readerWay, IntsRef relationFlags) { + // do not change RoadEnvironment to avoid triggering tunnel interpolation + } + }); + return importUnit; } }); - hopper.setEncodedValuesString("road_environment"); } hopper.setElevationProvider(new SRTMProvider(DIR)); @@ -1205,13 +1197,13 @@ public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay rea @Test public void testSRTMWithLongEdgeSampling() { final String profile = "profile"; - final String vehicle = "foot"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). setStoreOnFlush(true). - setProfiles(new Profile("profile").setVehicle(vehicle)); + setEncodedValuesString("foot_access, foot_priority, foot_average_speed"). + setProfiles(TestProfiles.accessSpeedAndPriority("profile", "foot")); hopper.getRouterConfig().setElevationWayPointMaxDistance(1.); hopper.getReaderConfig(). setElevationMaxWayPointDistance(1.). @@ -1226,38 +1218,35 @@ public void testSRTMWithLongEdgeSampling() { setAlgorithm(ASTAR).setProfile(profile)); ResponsePath arsp = rsp.getBest(); - assertEquals(1569.7, arsp.getDistance(), .1); - assertEquals(60, arsp.getPoints().size()); + assertEquals(1570, arsp.getDistance(), 1); + assertEquals(74, arsp.getPoints().size()); assertTrue(arsp.getPoints().is3D()); InstructionList il = arsp.getInstructions(); assertEquals(12, il.size()); assertTrue(il.get(0).getPoints().is3D()); - String str = arsp.getPoints().toString(); - assertEquals(23.8, arsp.getAscend(), 1e-1); assertEquals(67.4, arsp.getDescend(), 1e-1); - assertEquals(60, arsp.getPoints().size()); + assertEquals(74, arsp.getPoints().size()); assertEquals(new GHPoint3D(43.73068455771767, 7.421283689825812, 55.82900047302246), arsp.getPoints().get(0)); assertEquals(new GHPoint3D(43.727679637988224, 7.419198521975086, 12.274499893188477), arsp.getPoints().get(arsp.getPoints().size() - 1)); assertEquals(55.83, arsp.getPoints().get(0).getEle(), 1e-2); assertEquals(57.78, arsp.getPoints().get(1).getEle(), 1e-2); - assertEquals(52.43, arsp.getPoints().get(10).getEle(), 1e-2); + assertEquals(53.62, arsp.getPoints().get(10).getEle(), 1e-2); } @Disabled @Test public void testSkadiElevationProvider() { final String profile = "profile"; - final String vehicle = "foot"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setProfiles(new Profile(profile).setVehicle(vehicle)). + setProfiles(TestProfiles.accessSpeedAndPriority(profile, "foot")). setStoreOnFlush(true); hopper.setElevationProvider(new SkadiProvider(DIR)); @@ -1283,9 +1272,10 @@ public void testKremsCyclewayInstructionsWithWayTypeInfo() { GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(KREMS). + setEncodedValuesString("foot_access, foot_priority, foot_average_speed, bike_access, bike_priority, bike_average_speed"). setProfiles( - new Profile(footProfile).setVehicle("foot"), - new Profile(bikeProfile).setVehicle("bike")). + TestProfiles.accessSpeedAndPriority(footProfile, "foot"), + TestProfiles.accessSpeedAndPriority(bikeProfile, "bike")). setStoreOnFlush(true). importOrLoad(); @@ -1294,8 +1284,8 @@ public void testKremsCyclewayInstructionsWithWayTypeInfo() { setProfile(bikeProfile)); assertFalse(rsp.hasErrors()); ResponsePath res = rsp.getBest(); - assertEquals(6931.8, res.getDistance(), .1); - assertEquals(103, res.getPoints().size()); + assertEquals(6932.2, res.getDistance(), .1); + assertEquals(117, res.getPoints().size()); InstructionList il = res.getInstructions(); assertEquals(19, il.size()); @@ -1327,15 +1317,14 @@ public void testKremsCyclewayInstructionsWithWayTypeInfo() { public void testRoundaboutInstructionsWithCH() { final String profile1 = "my_profile"; final String profile2 = "your_profile"; - final String vehicle1 = "car"; - final String vehicle2 = "bike"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). + setEncodedValuesString("car_access, car_average_speed, bike_access, bike_priority, bike_average_speed"). setProfiles(Arrays.asList( - new Profile(profile1).setVehicle(vehicle1), - new Profile(profile2).setVehicle(vehicle2)) + TestProfiles.accessAndSpeed(profile1, "car"), + TestProfiles.accessSpeedAndPriority(profile2, "bike")) ). setStoreOnFlush(true); hopper.getCHPreparationHandler().setCHProfiles( @@ -1373,15 +1362,14 @@ public void testRoundaboutInstructionsWithCH() { public void testCircularJunctionInstructionsWithCH() { String profile1 = "profile1"; String profile2 = "profile2"; - String vehicle1 = "car"; - String vehicle2 = "bike"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(BERLIN). + setEncodedValuesString("car_access, car_average_speed, bike_access, bike_priority, bike_average_speed"). setProfiles( - new Profile(profile1).setVehicle(vehicle1), - new Profile(profile2).setVehicle(vehicle2) + TestProfiles.accessAndSpeed(profile1, "car"), + TestProfiles.accessSpeedAndPriority(profile2, "bike") ). setStoreOnFlush(true); hopper.getCHPreparationHandler().setCHProfiles( @@ -1406,12 +1394,13 @@ public void testMultipleVehiclesWithCH() { final String bikeProfile = "bike_profile"; final String carProfile = "car_profile"; List profiles = asList( - new Profile(bikeProfile).setVehicle("bike"), - new Profile(carProfile).setVehicle("car") + TestProfiles.accessSpeedAndPriority(bikeProfile, "bike"), + TestProfiles.accessAndSpeed(carProfile, "car") ); GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). + setEncodedValuesString("bike_access, bike_priority, bike_average_speed, car_access, car_average_speed"). setProfiles(profiles). setStoreOnFlush(true); hopper.getCHPreparationHandler().setCHProfiles( @@ -1431,7 +1420,7 @@ public void testMultipleVehiclesWithCH() { res = rsp.getBest(); assertFalse(rsp.hasErrors(), rsp.getErrors().toString()); assertEquals(536, res.getTime() / 1000f, 1); - assertEquals(2521, res.getDistance(), 1); + assertEquals(2522, res.getDistance(), 1); rsp = hopper.route(new GHRequest(43.73005, 7.415707, 43.741522, 7.42826) .setProfile("profile3")); @@ -1450,22 +1439,21 @@ public void testMultipleVehiclesWithCH() { @Test public void testIfCHIsUsed() { // route directly after import - executeCHFootRoute(false); + executeCHFootRoute(); // now only load is called - executeCHFootRoute(false); + executeCHFootRoute(); } - private void executeCHFootRoute(boolean sort) { + private void executeCHFootRoute() { final String profile = "profile"; - final String vehicle = "foot"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setProfiles(new Profile(profile).setVehicle(vehicle)). - setStoreOnFlush(true). - setSortGraph(sort); + setEncodedValuesString("car_access, car_average_speed, foot_access, foot_priority, foot_average_speed"). + setProfiles(TestProfiles.accessSpeedAndPriority(profile, "foot")). + setStoreOnFlush(true); hopper.getCHPreparationHandler().setCHProfiles(new CHProfile(profile)); hopper.importOrLoad(); @@ -1478,30 +1466,21 @@ private void executeCHFootRoute(boolean sort) { long sum = rsp.getHints().getLong("visited_nodes.sum", 0); assertNotEquals(sum, 0); assertTrue(sum < 147, "Too many nodes visited " + sum); - assertEquals(3535, bestPath.getDistance(), 1); - assertEquals(115, bestPath.getPoints().size()); + assertEquals(3536, bestPath.getDistance(), 1); + assertEquals(131, bestPath.getPoints().size()); hopper.close(); } - @Test - public void testSortWhileImporting() { - // route after importing a sorted graph - executeCHFootRoute(true); - - // route after loading a sorted graph - executeCHFootRoute(false); - } - @Test public void testRoundTour() { final String profile = "profile"; - final String vehicle = "foot"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setProfiles(new Profile(profile).setVehicle(vehicle)). + setEncodedValuesString("foot_access, foot_priority, foot_average_speed"). + setProfiles(TestProfiles.accessSpeedAndPriority(profile, "foot")). setStoreOnFlush(true). importOrLoad(); @@ -1517,21 +1496,20 @@ public void testRoundTour() { assertEquals(1, rsp.getAll().size()); ResponsePath res = rsp.getBest(); - assertEquals(1.49, rsp.getBest().getDistance() / 1000f, .01); + assertEquals(1.47, rsp.getBest().getDistance() / 1000f, .01); assertEquals(19, rsp.getBest().getTime() / 1000f / 60, 1); - assertEquals(66, res.getPoints().size()); + assertEquals(68, res.getPoints().size()); } @Test public void testPathDetails1216() { final String profile = "profile"; - final String vehicle = "car"; - GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(BAYREUTH). - setProfiles(new Profile(profile).setVehicle(vehicle)); + setEncodedValuesString("car_access, car_average_speed"). + setProfiles(TestProfiles.accessAndSpeed(profile, "car")); hopper.importOrLoad(); GHRequest req = new GHRequest(). @@ -1551,12 +1529,11 @@ public void testPathDetails1216() { @Test public void testPathDetailsSamePoint() { final String profile = "profile"; - final String vehicle = "car"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(BAYREUTH). - setProfiles(new Profile(profile).setVehicle(vehicle)); + setProfiles(TestProfiles.constantSpeed(profile)); hopper.importOrLoad(); GHRequest req = new GHRequest(). @@ -1573,12 +1550,12 @@ public void testPathDetailsSamePoint() { @Test public void testFlexMode_631() { final String profile = "car_profile"; - final String vehicle = "car"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setProfiles(new Profile(profile).setVehicle(vehicle)). + setEncodedValuesString("car_access, car_average_speed"). + setProfiles(TestProfiles.accessAndSpeed(profile, "car")). setStoreOnFlush(true); hopper.getCHPreparationHandler(). @@ -1600,7 +1577,7 @@ public void testFlexMode_631() { assertTrue(chSum < 70, "Too many visited nodes for ch mode " + chSum); ResponsePath bestPath = rsp.getBest(); assertEquals(3587, bestPath.getDistance(), 1); - assertEquals(91, bestPath.getPoints().size()); + assertEquals(105, bestPath.getPoints().size()); // request flex mode req.setAlgorithm(Parameters.Algorithms.ASTAR_BI); @@ -1612,7 +1589,7 @@ public void testFlexMode_631() { bestPath = rsp.getBest(); assertEquals(3587, bestPath.getDistance(), 1); - assertEquals(91, bestPath.getPoints().size()); + assertEquals(105, bestPath.getPoints().size()); // request hybrid mode req.putHint(Landmark.DISABLE, false); @@ -1626,7 +1603,7 @@ public void testFlexMode_631() { bestPath = rsp.getBest(); assertEquals(3587, bestPath.getDistance(), 1); - assertEquals(91, bestPath.getPoints().size()); + assertEquals(105, bestPath.getPoints().size()); // note: combining hybrid & speed mode is currently not possible and should be avoided: #1082 } @@ -1636,14 +1613,17 @@ public void testCrossQuery() { final String profile1 = "p1"; final String profile2 = "p2"; final String profile3 = "p3"; + Profile p1 = TestProfiles.accessAndSpeed(profile1, "car"); + Profile p2 = TestProfiles.accessAndSpeed(profile2, "car"); + Profile p3 = TestProfiles.accessAndSpeed(profile3, "car"); + p1.getCustomModel().setDistanceInfluence(70d); + p2.getCustomModel().setDistanceInfluence(100d); + p3.getCustomModel().setDistanceInfluence(150d); GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setProfiles( - new Profile(profile1).setCustomModel(new CustomModel().setDistanceInfluence(70d)).setVehicle("car"), - new Profile(profile2).setCustomModel(new CustomModel().setDistanceInfluence(100d)).setVehicle("car"), - new Profile(profile3).setCustomModel(new CustomModel().setDistanceInfluence(150d)).setVehicle("car") - ). + setEncodedValuesString("car_access, car_average_speed"). + setProfiles(p1, p2, p3). setStoreOnFlush(true); hopper.getLMPreparationHandler(). @@ -1659,13 +1639,13 @@ public void testCrossQuery() { // flex testCrossQueryAssert(profile1, hopper, 525.3, 196, true); - testCrossQueryAssert(profile2, hopper, 632.9, 198, true); - testCrossQueryAssert(profile3, hopper, 812.2, 198, true); + testCrossQueryAssert(profile2, hopper, 633.0, 198, true); + testCrossQueryAssert(profile3, hopper, 812.4, 198, true); // LM (should be the same as flex, but with less visited nodes!) testCrossQueryAssert(profile1, hopper, 525.3, 108, false); - testCrossQueryAssert(profile2, hopper, 632.9, 126, false); - testCrossQueryAssert(profile3, hopper, 812.2, 192, false); + testCrossQueryAssert(profile2, hopper, 633.0, 126, false); + testCrossQueryAssert(profile3, hopper, 812.4, 192, false); } private void testCrossQueryAssert(String profile, GraphHopper hopper, double expectedWeight, int expectedVisitedNodes, boolean disableLM) { @@ -1678,12 +1658,15 @@ private void testCrossQueryAssert(String profile, GraphHopper hopper, double exp @Test public void testLMConstraints() { + Profile p1 = TestProfiles.accessAndSpeed("p1", "car"); + Profile p2 = TestProfiles.accessAndSpeed("p2", "car"); + p1.getCustomModel().setDistanceInfluence(100d); + p2.getCustomModel().setDistanceInfluence(100d); GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setProfiles( - new Profile("p1").setCustomModel(new CustomModel().setDistanceInfluence(100d)).setVehicle("car"), - new Profile("p2").setCustomModel(new CustomModel().setDistanceInfluence(100d)).setVehicle("car")). + setEncodedValuesString("car_access, car_average_speed"). + setProfiles(p1, p2). setStoreOnFlush(true); hopper.getLMPreparationHandler().setLMProfiles(new LMProfile("p1")); @@ -1725,20 +1708,18 @@ public void testLMConstraints() { @Test public void testCreateWeightingHintsMerging() { - final String profile = "profile"; - final String vehicle = "mtb"; - GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setProfiles(new Profile(profile).setVehicle(vehicle).setTurnCosts(true).putHint(U_TURN_COSTS, 123)); + setEncodedValuesString("car_access, car_average_speed, mtb_access, mtb_priority, mtb_average_speed"). + setProfiles(TestProfiles.accessSpeedAndPriority("profile", "mtb").setTurnCostsConfig(new TurnCostsConfig(List.of("bicycle"), 123))); hopper.importOrLoad(); // if we do not pass u_turn_costs with the request hints we get those from the profile Weighting w = hopper.createWeighting(hopper.getProfiles().get(0), new PMap()); assertEquals(123.0, w.calcTurnWeight(5, 6, 5)); - // we can overwrite the u_turn_costs given in the profile + // we can no longer overwrite the u_turn_costs w = hopper.createWeighting(hopper.getProfiles().get(0), new PMap().putObject(U_TURN_COSTS, 46)); assertEquals(46.0, w.calcTurnWeight(5, 6, 5)); } @@ -1747,14 +1728,14 @@ public void testCreateWeightingHintsMerging() { public void testPreparedProfileNotAvailable() { final String profile1 = "fast_profile"; final String profile2 = "short_fast_profile"; - final String vehicle = "car"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). + setEncodedValuesString("car_access, car_average_speed"). setProfiles( - new Profile(profile1).setVehicle(vehicle), - new Profile(profile2).setVehicle(vehicle) + TestProfiles.accessAndSpeed(profile1, "car"), + TestProfiles.accessAndSpeed(profile2, "car") ). setStoreOnFlush(true); @@ -1794,23 +1775,21 @@ public void testPreparedProfileNotAvailable() { @Test public void testDisablingLM() { // setup GH with LM preparation but no CH preparation - final String profile = "profile"; - final String vehicle = "car"; + // note that the pure presence of the bike profile leads to 'ghost' junctions with the bike network even for // cars such that the number of visited nodes depends on the bike profile added here or not, #1910 GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setProfiles(new Profile(profile).setVehicle(vehicle), - new Profile("bike").setVehicle("bike")). + setProfiles(TestProfiles.constantSpeed("car")). setStoreOnFlush(true); hopper.getLMPreparationHandler(). - setLMProfiles(new LMProfile(profile).setMaximumLMWeight(2000)); + setLMProfiles(new LMProfile("car").setMaximumLMWeight(2000)); hopper.importOrLoad(); // we can switch LM on/off GHRequest req = new GHRequest(43.727687, 7.418737, 43.74958, 7.436566). - setProfile(profile); + setProfile("car"); req.putHint(Landmark.DISABLE, false); GHResponse res = hopper.route(req); @@ -1824,15 +1803,13 @@ public void testDisablingLM() { @ParameterizedTest @ValueSource(booleans = {true, false}) public void testCompareAlgos(boolean turnCosts) { - final String profile = "car"; - final String vehicle = "car"; - GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MOSCOW). - setProfiles(new Profile(profile).setVehicle(vehicle).setTurnCosts(turnCosts)); - hopper.getCHPreparationHandler().setCHProfiles(new CHProfile(profile)); - hopper.getLMPreparationHandler().setLMProfiles(new LMProfile(profile)); + setEncodedValuesString("car_access, car_average_speed"). + setProfiles(TestProfiles.accessAndSpeed("car").setTurnCostsConfig(turnCosts ? TurnCostsConfig.car() : null)); + hopper.getCHPreparationHandler().setCHProfiles(new CHProfile("car")); + hopper.getLMPreparationHandler().setLMProfiles(new LMProfile("car")); hopper.importOrLoad(); long seed = System.nanoTime(); @@ -1844,7 +1821,7 @@ public void testCompareAlgos(boolean turnCosts) { double lon1 = bounds.minLon + rnd.nextDouble() * (bounds.maxLon - bounds.minLon); double lon2 = bounds.minLon + rnd.nextDouble() * (bounds.maxLon - bounds.minLon); GHRequest req = new GHRequest(lat1, lon1, lat2, lon2); - req.setProfile(profile); + req.setProfile("car"); req.getHints().putObject(CH.DISABLE, false).putObject(Landmark.DISABLE, true); ResponsePath pathCH = hopper.route(req).getBest(); req.getHints().putObject(CH.DISABLE, true).putObject(Landmark.DISABLE, false); @@ -1869,18 +1846,16 @@ public void testCompareAlgos(boolean turnCosts) { @ParameterizedTest @ValueSource(booleans = {true, false}) public void testAStarCHBug(boolean turnCosts) { - final String profile = "car"; - final String vehicle = "car"; - GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MOSCOW). - setProfiles(new Profile(profile).setVehicle(vehicle).setTurnCosts(turnCosts)); - hopper.getCHPreparationHandler().setCHProfiles(new CHProfile(profile)); + setEncodedValuesString("car_access, car_average_speed"). + setProfiles(TestProfiles.accessAndSpeed("car").setTurnCostsConfig(turnCosts ? TurnCostsConfig.car() : null)); + hopper.getCHPreparationHandler().setCHProfiles(new CHProfile("car")); hopper.importOrLoad(); GHRequest req = new GHRequest(55.821744463888116, 37.60380604129401, 55.82608197039734, 37.62055856655137); - req.setProfile(profile); + req.setProfile("car"); req.getHints().putObject(CH.DISABLE, false); ResponsePath pathCH = hopper.route(req).getBest(); req.getHints().putObject(CH.DISABLE, true); @@ -1894,15 +1869,13 @@ public void testAStarCHBug(boolean turnCosts) { @Test public void testIssue1960() { - final String profile = "car"; - final String vehicle = "car"; - GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MOSCOW). - setProfiles(new Profile(profile).setVehicle(vehicle).setTurnCosts(true)); - hopper.getCHPreparationHandler().setCHProfiles(new CHProfile(profile)); - hopper.getLMPreparationHandler().setLMProfiles(new LMProfile(profile)); + setEncodedValuesString("car_access, car_average_speed"). + setProfiles(TestProfiles.accessAndSpeed("car").setTurnCostsConfig(TurnCostsConfig.car())); + hopper.getCHPreparationHandler().setCHProfiles(new CHProfile("car")); + hopper.getLMPreparationHandler().setLMProfiles(new LMProfile("car")); hopper.importOrLoad(); @@ -1915,28 +1888,29 @@ public void testIssue1960() { req.getHints().putObject(CH.DISABLE, true).putObject(Landmark.DISABLE, true); ResponsePath path = hopper.route(req).getBest(); - assertEquals(1995.38, pathCH.getDistance(), 0.1); - assertEquals(1995.38, pathLM.getDistance(), 0.1); - assertEquals(1995.38, path.getDistance(), 0.1); + assertEquals(1995.18, pathCH.getDistance(), 0.1); + assertEquals(1995.18, pathLM.getDistance(), 0.1); + assertEquals(1995.18, path.getDistance(), 0.1); - assertEquals(149504, pathCH.getTime()); - assertEquals(149504, pathLM.getTime()); - assertEquals(149504, path.getTime()); + assertEquals(149481, pathCH.getTime()); + assertEquals(149481, pathLM.getTime()); + assertEquals(149481, path.getTime()); } @Test public void testTurnCostsOnOff() { final String profile1 = "profile_no_turn_costs"; final String profile2 = "profile_turn_costs"; - final String vehicle = "car"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MOSCOW). + setEncodedValuesString("car_access, car_average_speed"). // add profile with turn costs first when no flag encoder is explicitly added setProfiles( - new Profile(profile2).setVehicle(vehicle).setTurnCosts(true).putHint(U_TURN_COSTS, 30), - new Profile(profile1).setVehicle(vehicle).setTurnCosts(false) + TestProfiles.accessAndSpeed(profile2, "car"). + setTurnCostsConfig(new TurnCostsConfig(List.of("motorcar", "motor_vehicle"), 30)), + TestProfiles.accessAndSpeed(profile1, "car") ). setStoreOnFlush(true); hopper.importOrLoad(); @@ -1958,14 +1932,14 @@ public void testTurnCostsOnOff() { public void testTurnCostsOnOffCH() { final String profile1 = "profile_turn_costs"; final String profile2 = "profile_no_turn_costs"; - final String vehicle = "car"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MOSCOW). - setProfiles(Arrays.asList( - new Profile(profile1).setVehicle(vehicle).setTurnCosts(true), - new Profile(profile2).setVehicle(vehicle).setTurnCosts(false) + setEncodedValuesString("car_access, car_average_speed"). + setProfiles(List.of( + TestProfiles.accessAndSpeed(profile1, "car").setTurnCostsConfig(TurnCostsConfig.car()), + TestProfiles.accessAndSpeed(profile2, "car") )). setStoreOnFlush(true); hopper.getCHPreparationHandler().setCHProfiles( @@ -1984,27 +1958,27 @@ public void testTurnCostsOnOffCH() { @Test public void testCHOnOffWithTurnCosts() { final String profile = "my_car"; - final String vehicle = "car"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MOSCOW). - setProfiles(new Profile(profile).setVehicle(vehicle).setTurnCosts(true)). + setEncodedValuesString("car_access, car_average_speed"). + setProfiles(TestProfiles.accessAndSpeed(profile, "car").setTurnCostsConfig(TurnCostsConfig.car())). setStoreOnFlush(true); hopper.getCHPreparationHandler() .setCHProfiles(new CHProfile(profile)); hopper.importOrLoad(); - GHRequest req = new GHRequest(55.813357, 37.5958585, 55.811042, 37.594689); + GHRequest req = new GHRequest(55.81358, 37.598616, 55.809915, 37.5947); req.setProfile("my_car"); // with CH req.putHint(CH.DISABLE, true); GHResponse rsp1 = hopper.route(req); - assertEquals(1044, rsp1.getBest().getDistance(), 1); + assertEquals(1350, rsp1.getBest().getDistance(), 1); // without CH req.putHint(CH.DISABLE, false); GHResponse rsp2 = hopper.route(req); - assertEquals(1044, rsp2.getBest().getDistance(), 1); + assertEquals(1350, rsp2.getBest().getDistance(), 1); // just a quick check that we did not run the same algorithm twice assertNotEquals(rsp1.getHints().getInt("visited_nodes.sum", -1), rsp2.getHints().getInt("visited_nodes.sum", -1)); } @@ -2018,9 +1992,11 @@ public void testNodeBasedCHOnlyButTurnCostForNonCH() { GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MOSCOW). - setProfiles(Arrays.asList( - new Profile(profile1).setVehicle("car").setTurnCosts(true), - new Profile(profile2).setVehicle("car").setTurnCosts(false))). + setEncodedValuesString("car_access, car_average_speed"). + setProfiles(List.of( + TestProfiles.accessAndSpeed(profile1, "car").setTurnCostsConfig(TurnCostsConfig.car()), + TestProfiles.accessAndSpeed(profile2, "car") + )). setStoreOnFlush(true); hopper.getCHPreparationHandler() // we only do the CH preparation for the profile without turn costs @@ -2050,22 +2026,22 @@ public void testNodeBasedCHOnlyButTurnCostForNonCH() { } @Test - public void testEncoderWithTurnCostSupport_stillAllows_nodeBasedRouting() { + public void testProfileWithTurnCostSupport_stillAllows_nodeBasedRouting() { // see #1698 - final String profile = "profile"; - final String vehicle = "foot"; - GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MOSCOW). - setProfiles(new Profile(profile).setVehicle(vehicle), - new Profile("car").setVehicle("car").setTurnCosts(true)); + setEncodedValuesString("foot_access, foot_priority, foot_average_speed, car_access, car_average_speed"). + setProfiles( + TestProfiles.accessSpeedAndPriority("foot"), + TestProfiles.accessAndSpeed("car").setTurnCostsConfig(TurnCostsConfig.car()) + ); hopper.importOrLoad(); GHPoint p = new GHPoint(55.813357, 37.5958585); GHPoint q = new GHPoint(55.811042, 37.594689); GHRequest req = new GHRequest(p, q); - req.setProfile(profile); + req.setProfile("foot"); GHResponse rsp = hopper.route(req); assertEquals(0, rsp.getErrors().size(), "there should not be an error, but was: " + rsp.getErrors()); } @@ -2080,9 +2056,10 @@ public void testOneWaySubnetwork_issue1807() { setGraphHopperLocation(GH_LOCATION). setOSMFile(ESSEN). setMinNetworkSize(50). + setEncodedValuesString("foot_access, foot_priority, foot_average_speed, car_access, car_average_speed"). setProfiles( - new Profile("foot").setVehicle("foot"), - new Profile("car").setVehicle("car").setTurnCosts(true) + TestProfiles.accessSpeedAndPriority("foot"), + TestProfiles.accessAndSpeed("car").setTurnCostsConfig(TurnCostsConfig.car()) ); hopper.importOrLoad(); @@ -2112,7 +2089,8 @@ public void testTagParserProcessingOrder() { setGraphHopperLocation(GH_LOCATION). setOSMFile(BAYREUTH). setMinNetworkSize(0). - setProfiles(new Profile("bike").setVehicle("bike")); + setEncodedValuesString("bike_access, bike_priority, bike_average_speed"). + setProfiles(TestProfiles.accessSpeedAndPriority("bike")); hopper.importOrLoad(); GHRequest req = new GHRequest(new GHPoint(49.98021, 11.50730), new GHPoint(49.98026, 11.50795)); @@ -2136,7 +2114,7 @@ public void testEdgeCount() { setGraphHopperLocation(GH_LOCATION). setOSMFile(BAYREUTH). setMinNetworkSize(50). - setProfiles(new Profile("car").setVehicle("car")); + setProfiles(TestProfiles.constantSpeed("bike")); hopper.importOrLoad(); int count = 0; AllEdgesIterator iter = hopper.getBaseGraph().getAllEdges(); @@ -2150,9 +2128,10 @@ public void testCurbsides() { GraphHopper h = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(BAYREUTH). - setProfiles(new Profile("my_profile").setVehicle("car").setTurnCosts(true)); + setEncodedValuesString("car_access, car_average_speed"). + setProfiles(TestProfiles.accessAndSpeed("car").setTurnCostsConfig(TurnCostsConfig.car())); h.getCHPreparationHandler() - .setCHProfiles(new CHProfile("my_profile")); + .setCHProfiles(new CHProfile("car")); h.importOrLoad(); // depending on the curbside parameters we take very different routes @@ -2194,15 +2173,13 @@ public void testCurbsides() { @Test public void testForceCurbsides() { - final String profile = "my_profile"; - final String vehicle = "car"; - GraphHopper h = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setProfiles(new Profile(profile).setVehicle(vehicle).setTurnCosts(true)); + setEncodedValuesString("car_access, car_average_speed"). + setProfiles(TestProfiles.accessAndSpeed("car").setTurnCostsConfig(TurnCostsConfig.car())); h.getCHPreparationHandler() - .setCHProfiles(new CHProfile(profile)); + .setCHProfiles(new CHProfile("car")); h.importOrLoad(); // depending on the curbside parameters we take very different routes @@ -2255,7 +2232,7 @@ private void assertCurbsidesPathError(GraphHopper hopper, GHPoint source, GHPoin private GHResponse calcCurbsidePath(GraphHopper hopper, GHPoint source, GHPoint target, List curbsides, boolean force) { GHRequest req = new GHRequest(source, target); req.putHint(Routing.FORCE_CURBSIDE, force); - req.setProfile("my_profile"); + req.setProfile("car"); req.setCurbsides(curbsides); return hopper.route(req); } @@ -2265,8 +2242,8 @@ public void testCHWithFiniteUTurnCosts() { GraphHopper h = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setProfiles(new Profile("my_profile").setVehicle("car"). - setTurnCosts(true).putHint(U_TURN_COSTS, 40)); + setEncodedValuesString("car_access, car_average_speed"). + setProfiles(TestProfiles.accessAndSpeed("my_profile", "car").setTurnCostsConfig(new TurnCostsConfig(List.of("motorcar", "motor_vehicle"), 40))); h.getCHPreparationHandler() .setCHProfiles(new CHProfile("my_profile")); h.importOrLoad(); @@ -2279,7 +2256,7 @@ public void testCHWithFiniteUTurnCosts() { // we reach the target. at the start location we do a u-turn at the crossing with the *steps* ('ghost junction') req.setCurbsides(Arrays.asList("right", "right")); GHResponse res = h.route(req); - assertFalse(res.hasErrors(), "routing should not fail"); + assertFalse(res.hasErrors(), "routing should not fail but had errors: " + res.getErrors()); assertEquals(242.5, res.getBest().getRouteWeight(), 0.1); assertEquals(1917, res.getBest().getDistance(), 1); assertEquals(243000, res.getBest().getTime(), 1000); @@ -2290,7 +2267,8 @@ public void simplifyWithInstructionsAndPathDetails() { final String profile = "profile"; GraphHopper hopper = new GraphHopper(); hopper.setOSMFile(BAYREUTH). - setProfiles(new Profile(profile).setVehicle("car")). + setEncodedValuesString("car_access, car_average_speed"). + setProfiles(TestProfiles.accessAndSpeed(profile, "car")). setGraphHopperLocation(GH_LOCATION); hopper.importOrLoad(); @@ -2308,7 +2286,7 @@ public void simplifyWithInstructionsAndPathDetails() { ResponsePath path = rsp.getBest(); // check path was simplified (without it would be more like 58) - assertEquals(41, path.getPoints().size()); + assertEquals(55, path.getPoints().size()); // check instructions InstructionList instructions = path.getInstructions(); @@ -2316,39 +2294,39 @@ public void simplifyWithInstructionsAndPathDetails() { for (Instruction instruction : instructions) { totalLength += instruction.getLength(); } - assertEquals(40, totalLength); + assertEquals(54, totalLength); assertInstruction(instructions.get(0), "KU 11", "[0, 4[", 4, 4); - assertInstruction(instructions.get(1), "B 85", "[4, 16[", 12, 12); + assertInstruction(instructions.get(1), "B 85", "[4, 24[", 20, 20); // via instructions have length = 0, but the point list must not be empty! - assertInstruction(instructions.get(2), null, "[16, 17[", 0, 1); - assertInstruction(instructions.get(3), "B 85", "[16, 32[", 16, 16); - assertInstruction(instructions.get(4), null, "[32, 34[", 2, 2); - assertInstruction(instructions.get(5), "KU 18", "[34, 37[", 3, 3); - assertInstruction(instructions.get(6), "St 2189", "[37, 38[", 1, 1); - assertInstruction(instructions.get(7), null, "[38, 40[", 2, 2); + assertInstruction(instructions.get(2), null, "[24, 25[", 0, 1); + assertInstruction(instructions.get(3), "B 85", "[24, 45[", 21, 21); + assertInstruction(instructions.get(4), null, "[45, 48[", 3, 3); + assertInstruction(instructions.get(5), "KU 18", "[48, 51[", 3, 3); + assertInstruction(instructions.get(6), "St 2189", "[51, 52[", 1, 1); + assertInstruction(instructions.get(7), null, "[52, 54[", 2, 2); // finish instructions have length = 0, but the point list must not be empty! - assertInstruction(instructions.get(8), null, "[40, 41[", 0, 1); + assertInstruction(instructions.get(8), null, "[54, 55[", 0, 1); // check max speeds List speeds = path.getPathDetails().get("max_speed"); assertDetail(speeds.get(0), "null [0, 4]"); - assertDetail(speeds.get(1), "70.0 [4, 6]"); - assertDetail(speeds.get(2), "100.0 [6, 16]"); - assertDetail(speeds.get(3), "100.0 [16, 31]"); // we do not merge path details at via points - assertDetail(speeds.get(4), "80.0 [31, 32]"); - assertDetail(speeds.get(5), "null [32, 37]"); - assertDetail(speeds.get(6), "50.0 [37, 38]"); - assertDetail(speeds.get(7), "null [38, 40]"); + assertDetail(speeds.get(1), "70.0 [4, 8]"); + assertDetail(speeds.get(2), "100.0 [8, 24]"); + assertDetail(speeds.get(3), "100.0 [24, 42]"); // we do not merge path details at via points + assertDetail(speeds.get(4), "80.0 [42, 45]"); + assertDetail(speeds.get(5), "null [45, 51]"); + assertDetail(speeds.get(6), "50.0 [51, 52]"); + assertDetail(speeds.get(7), "null [52, 54]"); // check street names List streetNames = path.getPathDetails().get(KVStorage.KeyValue.STREET_REF); assertDetail(streetNames.get(0), "KU 11 [0, 4]"); - assertDetail(streetNames.get(1), "B 85 [4, 16]"); - assertDetail(streetNames.get(2), "B 85 [16, 32]"); - assertDetail(streetNames.get(3), "null [32, 34]"); - assertDetail(streetNames.get(4), "KU 18 [34, 37]"); - assertDetail(streetNames.get(5), "St 2189 [37, 38]"); - assertDetail(streetNames.get(6), "null [38, 40]"); + assertDetail(streetNames.get(1), "B 85 [4, 24]"); + assertDetail(streetNames.get(2), "B 85 [24, 45]"); + assertDetail(streetNames.get(3), "null [45, 48]"); + assertDetail(streetNames.get(4), "KU 18 [48, 51]"); + assertDetail(streetNames.get(5), "St 2189 [51, 52]"); + assertDetail(streetNames.get(6), "null [52, 54]"); } private void assertInstruction(Instruction instruction, String expectedRef, String expectedInterval, int expectedLength, int expectedPoints) { @@ -2368,7 +2346,8 @@ public void simplifyKeepsWaypoints(boolean elevation, boolean instructions) { GraphHopper h = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setProfiles(new Profile("car").setVehicle("car")); + setEncodedValuesString("car_access, car_average_speed"). + setProfiles(TestProfiles.accessAndSpeed("car")); if (elevation) h.setElevationProvider(new SRTMProvider(DIR)); h.importOrLoad(); @@ -2388,7 +2367,7 @@ public void simplifyKeepsWaypoints(boolean elevation, boolean instructions) { req.putHint("instructions", instructions); GHResponse res = h.route(req); assertFalse(res.hasErrors()); - assertEquals(elevation ? 1828 : 1793, res.getBest().getDistance(), 1); + assertEquals(elevation ? 1829 : 1794, res.getBest().getDistance(), 1); PointList points = res.getBest().getPoints(); PointList wayPoints = res.getBest().getWaypoints(); assertEquals(reqPoints.size(), wayPoints.size()); @@ -2411,9 +2390,7 @@ private static void assertPointlistContainsSublist(PointList pointList, PointLis @Test public void testNoLoad() { String profile = "profile"; - String vehicle = "car"; - final GraphHopper hopper = new GraphHopper(). - setProfiles(new Profile(profile).setVehicle(vehicle)); + final GraphHopper hopper = new GraphHopper().setProfiles(TestProfiles.constantSpeed(profile)); IllegalStateException e = assertThrows(IllegalStateException.class, () -> hopper.route(new GHRequest(42, 10.4, 42, 10).setProfile(profile))); assertTrue(e.getMessage().startsWith("Do a successful call to load or importOrLoad before routing"), e.getMessage()); } @@ -2421,15 +2398,14 @@ public void testNoLoad() { @Test public void connectionNotFound() { final String profile = "profile"; - final String vehicle = "car"; - GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(BAYREUTH). - setProfiles(new Profile("profile").setVehicle(vehicle)). + setEncodedValuesString("car_access, car_average_speed"). + setProfiles(TestProfiles.accessAndSpeed(profile, "car")). setStoreOnFlush(true); hopper.getCHPreparationHandler() - .setCHProfiles(new CHProfile("profile")); + .setCHProfiles(new CHProfile(profile)); hopper.setMinNetworkSize(0); hopper.importOrLoad(); // here from and to both snap to small subnetworks that are disconnected from the main graph and @@ -2455,7 +2431,7 @@ void interpolateBridgesTunnelsAndFerries() { }. setGraphHopperLocation(GH_LOCATION). setOSMFile(BAYREUTH). - setProfiles(new Profile("profile")). + setProfiles(new Profile("profile").setCustomModel(new CustomModel().addToSpeed(If("true", LIMIT, "100")))). setElevation(true). setStoreOnFlush(true); hopper.importOrLoad(); @@ -2471,7 +2447,7 @@ void interpolateBridgesTunnelsAndFerries() { super.interpolateBridgesTunnelsAndFerries(); } }. - setProfiles(new Profile("profile")). + setProfiles(new Profile("profile").setCustomModel(new CustomModel().addToSpeed(If("true", LIMIT, "100")))). setElevation(true). setGraphHopperLocation(GH_LOCATION); hopper.load(); @@ -2482,11 +2458,11 @@ void interpolateBridgesTunnelsAndFerries() { @Test public void issue2306_1() { final String profile = "profile"; - final String vehicle = "car"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile("../map-matching/files/leipzig_germany.osm.pbf"). - setProfiles(new Profile("profile").setVehicle(vehicle)). + setEncodedValuesString("car_access, car_average_speed"). + setProfiles(TestProfiles.accessAndSpeed(profile, "car")). setMinNetworkSize(200); hopper.importOrLoad(); Weighting weighting = hopper.createWeighting(hopper.getProfile(profile), new PMap()); @@ -2505,11 +2481,11 @@ public void issue2306_2() { // is a meta-parameter that could go away at some point, I say that _if_ we find a match, // it should be a close one. (And not a far away one, as happened in issue2306.) final String profile = "profile"; - final String vehicle = "car"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile("../map-matching/files/leipzig_germany.osm.pbf"). - setProfiles(new Profile("profile").setVehicle(vehicle)). + setEncodedValuesString("car_access, car_average_speed"). + setProfiles(TestProfiles.accessAndSpeed(profile, "car")). setMinNetworkSize(200); hopper.importOrLoad(); Weighting weighting = hopper.createWeighting(hopper.getProfile(profile), new PMap()); @@ -2525,11 +2501,11 @@ public void testBarriers() { GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile("../map-matching/files/leipzig_germany.osm.pbf"). - setVehiclesString("car|block_private=false"). + setEncodedValuesString("car_access|block_private=false,road_access,car_average_speed, bike_access, bike_priority, bike_average_speed, foot_access, foot_priority, foot_average_speed"). setProfiles( - new Profile("car").setVehicle("car"), - new Profile("bike").setVehicle("bike"), - new Profile("foot").setVehicle("foot") + TestProfiles.accessAndSpeed("car"), + TestProfiles.accessSpeedAndPriority("bike"), + TestProfiles.accessSpeedAndPriority("foot") ). setMinNetworkSize(0); hopper.importOrLoad(); @@ -2610,7 +2586,7 @@ public void testBarriers() { rsp = hopper.route(new GHRequest(51.327411, 12.429598, 51.32723, 12.429979). setCustomModel(new CustomModel().addToPriority(If("road_access == PRIVATE", MULTIPLY, "0"))). setProfile("car")); - assertFalse(rsp.hasErrors()); + assertFalse(rsp.hasErrors(), rsp.getErrors().toString()); assertEquals(20, rsp.getBest().getDistance(), 1); } } @@ -2618,12 +2594,13 @@ public void testBarriers() { @Test public void germanyCountryRuleAvoidsTracks() { final String profile = "profile"; + Profile p = TestProfiles.accessAndSpeed(profile, "car"); + p.getCustomModel().addToPriority(If("road_access == DESTINATION", MULTIPLY, ".1")); // first we try without country rules (the default) GraphHopper hopper = new GraphHopper() - .setProfiles(new Profile(profile) - .setCustomModel(new CustomModel().addToPriority(If("road_access == DESTINATION", MULTIPLY, ".1"))) - .setVehicle("car")) + .setEncodedValuesString("car_access, car_average_speed, road_access") + .setProfiles(p) .setCountryRuleFactory(null) .setGraphHopperLocation(GH_LOCATION) .setOSMFile(BAYREUTH); @@ -2639,9 +2616,8 @@ public void germanyCountryRuleAvoidsTracks() { // this time we enable country rules hopper.clean(); hopper = new GraphHopper() - .setProfiles(new Profile(profile) - .setCustomModel(new CustomModel().addToPriority(If("road_access == DESTINATION", MULTIPLY, ".1"))) - .setVehicle("car")) + .setEncodedValuesString("car_access, car_average_speed, road_access") + .setProfiles(p) .setGraphHopperLocation(GH_LOCATION) .setCountryRuleFactory(new CountryRuleFactory()) .setOSMFile(BAYREUTH); @@ -2657,10 +2633,9 @@ public void germanyCountryRuleAvoidsTracks() { @Test void curbsideWithSubnetwork_issue2502() { - final String profile = "profile"; GraphHopper hopper = new GraphHopper() - .setProfiles(new Profile(profile).setVehicle("car").setTurnCosts(true) - .setTurnCosts(true).putHint(U_TURN_COSTS, 80)) + .setEncodedValuesString("car_access, car_average_speed") + .setProfiles(TestProfiles.accessAndSpeed("car").setTurnCostsConfig(TurnCostsConfig.car())) .setGraphHopperLocation(GH_LOCATION) .setMinNetworkSize(200) .setOSMFile(DIR + "/one_way_dead_end.osm.pbf"); @@ -2670,7 +2645,7 @@ void curbsideWithSubnetwork_issue2502() { { // A->B GHRequest request = new GHRequest(pointA, pointB); - request.setProfile(profile); + request.setProfile("car"); request.setCurbsides(Arrays.asList("right", "right")); GHResponse response = hopper.route(request); assertFalse(response.hasErrors(), response.getErrors().toString()); @@ -2683,7 +2658,7 @@ void curbsideWithSubnetwork_issue2502() { // when the curbside constraints are evaluated. this should make the snap a tower snap such that the curbside // constraint won't result in a connection not found error GHRequest request = new GHRequest(pointB, pointA); - request.setProfile(profile); + request.setProfile("car"); request.setCurbsides(Arrays.asList("right", "right")); GHResponse response = hopper.route(request); assertFalse(response.hasErrors(), response.getErrors().toString()); @@ -2694,9 +2669,9 @@ void curbsideWithSubnetwork_issue2502() { @Test void averageSpeedPathDetailBug() { - final String profile = "profile"; GraphHopper hopper = new GraphHopper() - .setProfiles(new Profile(profile).setVehicle("car").setTurnCosts(true).putHint(U_TURN_COSTS, 80)) + .setEncodedValuesString("car_access, car_average_speed") + .setProfiles(TestProfiles.accessAndSpeed("car").setTurnCostsConfig(TurnCostsConfig.car())) .setGraphHopperLocation(GH_LOCATION) .setMinNetworkSize(200) .setOSMFile(BAYREUTH); @@ -2705,7 +2680,7 @@ void averageSpeedPathDetailBug() { GHPoint pointB = new GHPoint(50.019935, 11.500567); GHPoint pointC = new GHPoint(50.022027, 11.498255); GHRequest request = new GHRequest(Arrays.asList(pointA, pointB, pointC)); - request.setProfile(profile); + request.setProfile("car"); request.setPathDetails(Collections.singletonList("average_speed")); // this used to fail, because we did not wrap the weighting for query graph and so we tried calculating turn costs for virtual nodes GHResponse response = hopper.route(request); @@ -2716,9 +2691,9 @@ void averageSpeedPathDetailBug() { @Test void timeDetailBug() { - final String profile = "profile"; GraphHopper hopper = new GraphHopper() - .setProfiles(new Profile(profile).setVehicle("car").setTurnCosts(true).putHint(U_TURN_COSTS, 80)) + .setEncodedValuesString("car_access, car_average_speed") + .setProfiles(TestProfiles.accessAndSpeed("car").setTurnCostsConfig(TurnCostsConfig.car())) .setGraphHopperLocation(GH_LOCATION) .setMinNetworkSize(200) .setOSMFile(BAYREUTH); @@ -2727,7 +2702,7 @@ void timeDetailBug() { new GHPoint(50.020838, 11.494918), new GHPoint(50.024795, 11.498973), new GHPoint(50.023141, 11.496441))); - request.setProfile(profile); + request.setProfile("car"); request.getHints().putObject("instructions", true); request.setPathDetails(Arrays.asList("distance", "time")); GHResponse response = hopper.route(request); @@ -2767,8 +2742,8 @@ private void consistenceCheck(ResponsePath path) { public void testLoadGraph_implicitEncodedValues_issue1862() { GraphHopper hopper = new GraphHopper() .setProfiles( - new Profile("p_car").setVehicle("car"), - new Profile("p_bike").setVehicle("bike") + TestProfiles.constantSpeed("p_car"), + TestProfiles.constantSpeed("p_bike") ) .setGraphHopperLocation(GH_LOCATION) .setOSMFile(BAYREUTH); @@ -2776,26 +2751,23 @@ public void testLoadGraph_implicitEncodedValues_issue1862() { int nodes = hopper.getBaseGraph().getNodes(); hopper.close(); - // load without configured graph.vehicles - hopper = new GraphHopper(); - hopper.setProfiles(Arrays.asList( - new Profile("p_car").setVehicle("car"), - new Profile("p_bike").setVehicle("bike")) - ); - hopper.setGraphHopperLocation(GH_LOCATION); + hopper = new GraphHopper() + .setProfiles( + TestProfiles.constantSpeed("p_car"), + TestProfiles.constantSpeed("p_bike") + ) + .setGraphHopperLocation(GH_LOCATION); assertTrue(hopper.load()); hopper.getBaseGraph(); assertEquals(nodes, hopper.getBaseGraph().getNodes()); hopper.close(); - // load via explicitly configured graph.vehicles - hopper = new GraphHopper(); - hopper.setVehiclesString("car,bike"); - hopper.setProfiles(Arrays.asList( - new Profile("p_car").setVehicle("car"), - new Profile("p_bike").setVehicle("bike")) - ); - hopper.setGraphHopperLocation(GH_LOCATION); + hopper = new GraphHopper() + .setProfiles( + TestProfiles.constantSpeed("p_car"), + TestProfiles.constantSpeed("p_bike") + ) + .setGraphHopperLocation(GH_LOCATION); assertTrue(hopper.load()); assertEquals(nodes, hopper.getBaseGraph().getNodes()); hopper.close(); @@ -2805,20 +2777,20 @@ public void testLoadGraph_implicitEncodedValues_issue1862() { void testLoadingWithAnotherSpeedFactorWorks() { { GraphHopper hopper = new GraphHopper() - .setVehiclesString("car|speed_factor=3") - .setProfiles(new Profile("car").setVehicle("car")) + .setEncodedValuesString("car_average_speed|speed_factor=3, car_access") + .setProfiles(TestProfiles.accessAndSpeed("car")) .setGraphHopperLocation(GH_LOCATION) .setOSMFile(BAYREUTH); hopper.importOrLoad(); } { - // now we use another speed_factor, but changing the flag encoder string has no effect when we are loading + // now we use another speed_factor, but changing the encoded value string has no effect when we are loading // a graph. This API is a bit confusing, but we have been mixing configuration options that only matter // during import with those that only matter when routing for some time already. At some point we should // separate the 'import' from the 'routing' config (and split the GraphHopper class). GraphHopper hopper = new GraphHopper() - .setVehiclesString("car|speed_factor=9") - .setProfiles(new Profile("car").setVehicle("car")) + .setEncodedValuesString("car_average_speed|speed_factor=9") + .setProfiles(TestProfiles.accessAndSpeed("car")) .setGraphHopperLocation(GH_LOCATION); hopper.load(); assertEquals(2969, hopper.getBaseGraph().getNodes()); @@ -2826,4 +2798,3 @@ void testLoadingWithAnotherSpeedFactorWorks() { } } - diff --git a/core/src/test/java/com/graphhopper/isochrone/algorithm/ShortestPathTreeTest.java b/core/src/test/java/com/graphhopper/isochrone/algorithm/ShortestPathTreeTest.java index dd4187a8f3c..91c872dc88e 100644 --- a/core/src/test/java/com/graphhopper/isochrone/algorithm/ShortestPathTreeTest.java +++ b/core/src/test/java/com/graphhopper/isochrone/algorithm/ShortestPathTreeTest.java @@ -1,5 +1,6 @@ package com.graphhopper.isochrone.algorithm; +import com.graphhopper.json.Statement; import com.graphhopper.routing.ev.BooleanEncodedValue; import com.graphhopper.routing.ev.DecimalEncodedValue; import com.graphhopper.routing.ev.DecimalEncodedValueImpl; @@ -13,10 +14,7 @@ import com.graphhopper.routing.weighting.custom.CustomWeighting; import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.Graph; -import com.graphhopper.util.CustomModel; -import com.graphhopper.util.EdgeIterator; -import com.graphhopper.util.EdgeIteratorState; -import com.graphhopper.util.GHUtility; +import com.graphhopper.util.*; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -73,6 +71,20 @@ public long calcTurnMillis(int inEdge, int viaNode, int outEdge) { private final EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).add(ferryEnc).build(); private BaseGraph graph; + private Weighting createWeighting() { + return createWeighting(TurnCostProvider.NO_TURN_COST_PROVIDER); + } + + private Weighting createWeighting(TurnCostProvider turnCostProvider) { + return CustomModelParser.createWeighting(encodingManager, turnCostProvider, createBaseCustomModel()); + } + + private CustomModel createBaseCustomModel() { + CustomModel customModel = new CustomModel(); + customModel.addToPriority(Statement.If("!" + accessEnc.getName(), Statement.Op.MULTIPLY, "0")); + customModel.addToSpeed(Statement.If("true", Statement.Op.LIMIT, speedEnc.getName())); + return customModel; + } @BeforeEach public void setUp() { @@ -127,7 +139,7 @@ public void tearDown() { @Test public void testSPTAndIsochrone25Seconds() { List result = new ArrayList<>(); - ShortestPathTree instance = new ShortestPathTree(graph, CustomModelParser.createFastestWeighting(accessEnc, speedEnc, encodingManager), false, TraversalMode.NODE_BASED); + ShortestPathTree instance = new ShortestPathTree(graph, createWeighting(), false, TraversalMode.NODE_BASED); instance.setTimeLimit(25_000); instance.search(0, result::add); assertEquals(3, result.size()); @@ -143,7 +155,7 @@ public void testSPTAndIsochrone25Seconds() { @Test public void testSPT26Seconds() { List result = new ArrayList<>(); - ShortestPathTree instance = new ShortestPathTree(graph, CustomModelParser.createFastestWeighting(accessEnc, speedEnc, encodingManager), false, TraversalMode.NODE_BASED); + ShortestPathTree instance = new ShortestPathTree(graph, createWeighting(), false, TraversalMode.NODE_BASED); instance.setTimeLimit(26_000); instance.search(0, result::add); assertEquals(4, result.size()); @@ -158,7 +170,7 @@ public void testSPT26Seconds() { @Test public void testNoTimeLimit() { List result = new ArrayList<>(); - ShortestPathTree instance = new ShortestPathTree(graph, CustomModelParser.createFastestWeighting(accessEnc, speedEnc, encodingManager), false, TraversalMode.NODE_BASED); + ShortestPathTree instance = new ShortestPathTree(graph, createWeighting(), false, TraversalMode.NODE_BASED); instance.setTimeLimit(Double.MAX_VALUE); instance.search(0, result::add); assertEquals(9, result.size()); @@ -185,9 +197,9 @@ public void testFerry() { edge.set(ferryEnc, true); List result = new ArrayList<>(); - CustomModel cm = new CustomModel(); - cm.addToPriority(If("ferry", MULTIPLY, "0.005")); - CustomWeighting weighting = CustomModelParser.createWeighting(accessEnc, speedEnc, null, encodingManager, TurnCostProvider.NO_TURN_COST_PROVIDER, cm); + CustomModel customModel = createBaseCustomModel(); + customModel.addToPriority(If("ferry", MULTIPLY, "0.005")); + CustomWeighting weighting = CustomModelParser.createWeighting(encodingManager, TurnCostProvider.NO_TURN_COST_PROVIDER, customModel); ShortestPathTree instance = new ShortestPathTree(graph, weighting, false, TraversalMode.NODE_BASED); instance.setTimeLimit(30_000); instance.search(0, result::add); @@ -205,7 +217,7 @@ public void testFerry() { @Test public void testEdgeBasedWithFreeUTurns() { List result = new ArrayList<>(); - ShortestPathTree instance = new ShortestPathTree(graph, CustomModelParser.createFastestWeighting(accessEnc, speedEnc, encodingManager), false, TraversalMode.EDGE_BASED); + ShortestPathTree instance = new ShortestPathTree(graph, createWeighting(), false, TraversalMode.EDGE_BASED); instance.setTimeLimit(Double.MAX_VALUE); instance.search(0, result::add); // The origin, and every end of every directed edge, are traversed. @@ -237,7 +249,7 @@ public void testEdgeBasedWithFreeUTurns() { @Test public void testEdgeBasedWithForbiddenUTurns() { - Weighting fastestWeighting = CustomModelParser.createWeighting(accessEnc, speedEnc, null, encodingManager, FORBIDDEN_UTURNS, new CustomModel()); + Weighting fastestWeighting = createWeighting(FORBIDDEN_UTURNS); List result = new ArrayList<>(); ShortestPathTree instance = new ShortestPathTree(graph, fastestWeighting, false, TraversalMode.EDGE_BASED); instance.setTimeLimit(Double.MAX_VALUE); @@ -271,7 +283,7 @@ public void testEdgeBasedWithForbiddenUTurns() { @Test public void testEdgeBasedWithFinitePositiveUTurnCost() { TimeBasedUTurnCost turnCost = new TimeBasedUTurnCost(80000); - Weighting fastestWeighting = CustomModelParser.createWeighting(accessEnc, speedEnc, null, encodingManager, turnCost, new CustomModel()); + Weighting fastestWeighting = createWeighting(turnCost); List result = new ArrayList<>(); ShortestPathTree instance = new ShortestPathTree(graph, fastestWeighting, false, TraversalMode.EDGE_BASED); instance.setTimeLimit(Double.MAX_VALUE); @@ -306,9 +318,8 @@ public void testEdgeBasedWithFinitePositiveUTurnCost() { @Test public void testEdgeBasedWithSmallerUTurnCost() { TimeBasedUTurnCost turnCost = new TimeBasedUTurnCost(20000); - Weighting fastestWeighting = CustomModelParser.createWeighting(accessEnc, speedEnc, null, encodingManager, turnCost, new CustomModel()); List result = new ArrayList<>(); - ShortestPathTree instance = new ShortestPathTree(graph, fastestWeighting, false, TraversalMode.EDGE_BASED); + ShortestPathTree instance = new ShortestPathTree(graph, createWeighting(turnCost), false, TraversalMode.EDGE_BASED); instance.setTimeLimit(Double.MAX_VALUE); instance.search(0, result::add); // Something in between @@ -341,7 +352,7 @@ public void testEdgeBasedWithSmallerUTurnCost() { @Test public void testSearchByDistance() { List result = new ArrayList<>(); - ShortestPathTree instance = new ShortestPathTree(graph, CustomModelParser.createFastestWeighting(accessEnc, speedEnc, encodingManager), false, TraversalMode.NODE_BASED); + ShortestPathTree instance = new ShortestPathTree(graph, createWeighting(), false, TraversalMode.NODE_BASED); instance.setDistanceLimit(110.0); instance.search(5, result::add); assertEquals(6, result.size()); diff --git a/core/src/test/java/com/graphhopper/reader/dem/EdgeElevationInterpolatorTest.java b/core/src/test/java/com/graphhopper/reader/dem/EdgeElevationInterpolatorTest.java index fc7302f40d4..751e25c19df 100644 --- a/core/src/test/java/com/graphhopper/reader/dem/EdgeElevationInterpolatorTest.java +++ b/core/src/test/java/com/graphhopper/reader/dem/EdgeElevationInterpolatorTest.java @@ -45,7 +45,7 @@ public abstract class EdgeElevationInterpolatorTest { public void setUp() { accessEnc = new SimpleBooleanEncodedValue("access", true); speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); - encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).add(RoadEnvironment.create()).build(); graph = new BaseGraph.Builder(encodingManager).set3D(true).create(); roadEnvEnc = encodingManager.getEnumEncodedValue(RoadEnvironment.KEY, RoadEnvironment.class); edgeElevationInterpolator = createEdgeElevationInterpolator(); diff --git a/core/src/test/java/com/graphhopper/reader/osm/GraphHopperOSMTest.java b/core/src/test/java/com/graphhopper/reader/osm/GraphHopperOSMTest.java index 7356a0a429d..f5e5029a90f 100644 --- a/core/src/test/java/com/graphhopper/reader/osm/GraphHopperOSMTest.java +++ b/core/src/test/java/com/graphhopper/reader/osm/GraphHopperOSMTest.java @@ -23,6 +23,7 @@ import com.graphhopper.config.CHProfile; import com.graphhopper.config.LMProfile; import com.graphhopper.config.Profile; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.routing.ev.EncodedValue; import com.graphhopper.routing.lm.LandmarkStorage; import com.graphhopper.routing.util.EdgeFilter; @@ -70,10 +71,9 @@ public void tearDown() { @Test public void testLoadOSM() { String profile = "car_profile"; - String vehicle = "car"; GraphHopper hopper = new GraphHopper(). setStoreOnFlush(true). - setProfiles(new Profile(profile).setVehicle(vehicle)). + setProfiles(TestProfiles.constantSpeed(profile)). setGraphHopperLocation(ghLoc). setOSMFile(testOsm); hopper.getCHPreparationHandler().setCHProfiles(new CHProfile(profile)); @@ -87,7 +87,7 @@ public void testLoadOSM() { // no encoding manager necessary hopper = new GraphHopper(). - setProfiles(new Profile(profile).setVehicle(vehicle)). + setProfiles(TestProfiles.constantSpeed(profile)). setStoreOnFlush(true); hopper.getCHPreparationHandler().setCHProfiles(new CHProfile(profile)); hopper.setGraphHopperLocation(ghLoc); @@ -117,9 +117,8 @@ public void testLoadOSM() { @Test public void testLoadOSMNoCH() { final String profile = "profile"; - final String vehicle = "car"; GraphHopper gh = new GraphHopper(). - setProfiles(new Profile(profile).setVehicle(vehicle)). + setProfiles(TestProfiles.constantSpeed(profile)). setStoreOnFlush(true). setGraphHopperLocation(ghLoc). setOSMFile(testOsm); @@ -134,7 +133,7 @@ public void testLoadOSMNoCH() { gh.close(); gh = new GraphHopper(). - setProfiles(new Profile(profile).setVehicle(vehicle)). + setProfiles(TestProfiles.constantSpeed(profile)). setStoreOnFlush(true). setGraphHopperLocation(ghLoc); assertTrue(gh.load()); @@ -146,7 +145,7 @@ public void testLoadOSMNoCH() { gh.close(); gh = new GraphHopper(). - setProfiles(new Profile(profile).setVehicle(vehicle)). + setProfiles(TestProfiles.constantSpeed(profile)). setGraphHopperLocation(ghLoc). setOSMFile(testOsm); @@ -157,7 +156,7 @@ public void testLoadOSMNoCH() { @Test public void testQueryLocationIndexWithBBox() { final GraphHopper gh = new GraphHopper(). - setProfiles(new Profile("car").setVehicle("car")). + setProfiles(TestProfiles.constantSpeed("car")). setStoreOnFlush(true). setGraphHopperLocation(ghLoc). setOSMFile("../core/files/monaco.osm.gz"); @@ -211,23 +210,23 @@ protected boolean goFurther(int nodeId) { public void testLoadingWithDifferentCHConfig_issue471_pr1488() { // when there is a single CH profile we can also load GraphHopper without it // in #471 this was forbidden, but later it was allowed again, see #1488 - final String profile = "profile"; - final String vehicle = "car"; + Profile profile = TestProfiles.constantSpeed("car"); + GraphHopper gh = new GraphHopper(). setStoreOnFlush(true). - setProfiles(new Profile(profile).setVehicle(vehicle)). + setProfiles(profile). setGraphHopperLocation(ghLoc). setOSMFile(testOsm); - gh.getCHPreparationHandler().setCHProfiles(new CHProfile(profile)); + gh.getCHPreparationHandler().setCHProfiles(new CHProfile(profile.getName())); gh.importOrLoad(); - GHResponse rsp = gh.route(new GHRequest(51.2492152, 9.4317166, 51.2, 9.4).setProfile(profile)); + GHResponse rsp = gh.route(new GHRequest(51.2492152, 9.4317166, 51.2, 9.4).setProfile("car")); assertFalse(rsp.hasErrors()); assertEquals(3, rsp.getBest().getPoints().size()); gh.close(); // now load GH without CH profile gh = new GraphHopper(). - setProfiles(new Profile(profile).setVehicle(vehicle)). + setProfiles(profile). setStoreOnFlush(true). setGraphHopperLocation(ghLoc); gh.load(); @@ -238,22 +237,22 @@ public void testLoadingWithDifferentCHConfig_issue471_pr1488() { // when there is no CH preparation yet it will be added (CH delta import) gh = new GraphHopper(). setStoreOnFlush(true). - setProfiles(new Profile(profile).setVehicle(vehicle)). + setProfiles(profile). setGraphHopperLocation(ghLoc). setOSMFile(testOsm); gh.importOrLoad(); - rsp = gh.route(new GHRequest(51.2492152, 9.4317166, 51.2, 9.4).setProfile(profile)); + rsp = gh.route(new GHRequest(51.2492152, 9.4317166, 51.2, 9.4).setProfile("car")); assertFalse(rsp.hasErrors()); assertEquals(3, rsp.getBest().getPoints().size()); gh.close(); gh = new GraphHopper(). - setProfiles(new Profile(profile).setVehicle(vehicle)). + setProfiles(new Profile(profile)). setStoreOnFlush(true); - gh.getCHPreparationHandler().setCHProfiles(new CHProfile("profile")); + gh.getCHPreparationHandler().setCHProfiles(new CHProfile("car")); gh.setGraphHopperLocation(ghLoc); gh.importOrLoad(); - rsp = gh.route(new GHRequest(51.2492152, 9.4317166, 51.2, 9.4).setProfile(profile)); + rsp = gh.route(new GHRequest(51.2492152, 9.4317166, 51.2, 9.4).setProfile("car")); assertFalse(rsp.hasErrors()); assertEquals(3, rsp.getBest().getPoints().size()); // no error @@ -261,23 +260,22 @@ public void testLoadingWithDifferentCHConfig_issue471_pr1488() { @Test public void testAllowMultipleReadingInstances() { - String vehicle = "car"; GraphHopper instance1 = new GraphHopper(). - setProfiles(new Profile(vehicle).setVehicle(vehicle)). + setProfiles(TestProfiles.constantSpeed("car")). setStoreOnFlush(true). setGraphHopperLocation(ghLoc). setOSMFile(testOsm); instance1.importOrLoad(); GraphHopper instance2 = new GraphHopper(). - setProfiles(new Profile(vehicle).setVehicle(vehicle)). + setProfiles(TestProfiles.constantSpeed("car")). setStoreOnFlush(true). setOSMFile(testOsm). setGraphHopperLocation(ghLoc); instance2.load(); GraphHopper instance3 = new GraphHopper(). - setProfiles(new Profile(vehicle).setVehicle(vehicle)). + setProfiles(TestProfiles.constantSpeed("car")). setStoreOnFlush(true). setOSMFile(testOsm). setGraphHopperLocation(ghLoc); @@ -305,7 +303,7 @@ protected void importOSM() { } }.setStoreOnFlush(true). setGraphHopperLocation(ghLoc). - setProfiles(new Profile("car").setVehicle("car")). + setProfiles(TestProfiles.constantSpeed("car")). setOSMFile(testOsm); final AtomicReference ar = new AtomicReference<>(); Thread thread = new Thread() { @@ -321,7 +319,7 @@ public void run() { thread.start(); GraphHopper instance2 = new GraphHopper(). - setProfiles(new Profile("car").setVehicle("car")). + setProfiles(TestProfiles.constantSpeed("car")). setStoreOnFlush(true). setOSMFile(testOsm). setGraphHopperLocation(ghLoc); @@ -349,11 +347,10 @@ public void run() { @Test public void testPrepare() { final String profile = "profile"; - final String vehicle = "car"; instance = new GraphHopper(). setStoreOnFlush(false). - setProfiles(new Profile(profile).setVehicle(vehicle)). + setProfiles(TestProfiles.constantSpeed("profile")). setGraphHopperLocation(ghLoc). setOSMFile(testOsm); instance.getCHPreparationHandler().setCHProfiles(new CHProfile(profile)); @@ -366,39 +363,17 @@ public void testPrepare() { assertEquals(3, rsp.getBest().getPoints().size()); } - @Test - public void testSortedGraph_noCH() { - final String profile = "profile"; - final String vehicle = "car"; - instance = new GraphHopper(). - setProfiles(new Profile(profile).setVehicle(vehicle)). - setStoreOnFlush(false). - setSortGraph(true). - setGraphHopperLocation(ghLoc). - setOSMFile(testOsm); - instance.importOrLoad(); - ResponsePath rsp = instance.route(new GHRequest(51.2492152, 9.4317166, 51.2, 9.4). - setProfile(profile). - setAlgorithm(DIJKSTRA_BI)).getBest(); - assertFalse(rsp.hasErrors()); - assertEquals(3, rsp.getPoints().size()); - assertEquals(new GHPoint(51.24921503475044, 9.431716451757769), rsp.getPoints().get(0)); - assertEquals(new GHPoint(52.0, 9.0), rsp.getPoints().get(1)); - assertEquals(new GHPoint(51.199999850988384, 9.39999970197677), rsp.getPoints().get(2)); - } - @Test public void testFootAndCar() { final String profile1 = "profile1"; final String profile2 = "profile2"; - final String vehicle1 = "car"; - final String vehicle2 = "foot"; // now all ways are imported instance = new GraphHopper(). + setEncodedValuesString("car_access, car_average_speed, foot_access, foot_average_speed"). setProfiles( - new Profile(profile1).setVehicle(vehicle1), - new Profile(profile2).setVehicle(vehicle2) + TestProfiles.accessAndSpeed(profile1, "car"), + TestProfiles.accessAndSpeed(profile2, "foot") ). setStoreOnFlush(false). setGraphHopperLocation(ghLoc). @@ -431,7 +406,7 @@ public void testFootAndCar() { assertFalse(grsp.hasErrors()); rsp = grsp.getBest(); assertEquals(2, rsp.getPoints().size()); - // => found a point on edge A-B + // => found a point on edge A-B assertEquals(11.680, rsp.getPoints().getLat(1), 1e-3); assertEquals(50.644, rsp.getPoints().getLon(1), 1e-3); @@ -449,69 +424,51 @@ public void testFootAndCar() { } @Test - public void testNothingHappensWhenFlagEncodersAreChangedForLoad() { + public void testNothingHappensWhenProfilesAreChangedForLoad() { instance = new GraphHopper().init( new GraphHopperConfig(). putObject("datareader.file", testOsm3). putObject("datareader.dataaccess", "RAM"). - putObject("graph.vehicles", "foot,car"). putObject("import.osm.ignored_highways", ""). - setProfiles(Arrays.asList( - new Profile("foot").setVehicle("foot"), - new Profile("car").setVehicle("car") + setProfiles(List.of( + TestProfiles.constantSpeed("foot"), + TestProfiles.constantSpeed("car") ))). setGraphHopperLocation(ghLoc); instance.importOrLoad(); assertEquals(5, instance.getBaseGraph().getNodes()); instance.close(); - // different flagEncoder list has no effect when loading, so it does not matter, but the profiles must be the same + // the profiles must be the same GraphHopper tmpGH = new GraphHopper().init( new GraphHopperConfig(). putObject("datareader.file", testOsm3). putObject("datareader.dataaccess", "RAM"). - putObject("graph.vehicles", "foot"). putObject("import.osm.ignored_highways", ""). - setProfiles(Collections.singletonList( - new Profile("foot").setVehicle("foot") + setProfiles(List.of( + TestProfiles.constantSpeed("foot") ))). setOSMFile(testOsm3). setGraphHopperLocation(ghLoc); IllegalStateException e = assertThrows(IllegalStateException.class, tmpGH::load); assertTrue(e.getMessage().contains("Profiles do not match"), e.getMessage()); - // different order of graph.vehicles is also fine, but profiles must be in same order - tmpGH = new GraphHopper().init(new GraphHopperConfig(). - putObject("datareader.file", testOsm3). - putObject("datareader.dataaccess", "RAM"). - putObject("graph.vehicles", "car,foot"). - putObject("import.osm.ignored_highways", ""). - setProfiles(Arrays.asList( - new Profile("car").setVehicle("car"), - new Profile("foot").setVehicle("foot") - ))). - setOSMFile(testOsm3) - .setGraphHopperLocation(ghLoc); - e = assertThrows(IllegalStateException.class, tmpGH::load); - assertTrue(e.getMessage().contains("Profiles do not match"), e.getMessage()); - - // different encoded values do not matter either + // different encoded values do not matter, since they are ignored when loading the graph anyway instance = new GraphHopper().init( new GraphHopperConfig(). putObject("datareader.file", testOsm3). putObject("datareader.dataaccess", "RAM"). putObject("graph.encoded_values", "road_class"). - putObject("graph.vehicles", "foot,car"). putObject("import.osm.ignored_highways", ""). - setProfiles(Arrays.asList( - new Profile("foot").setVehicle("foot"), - new Profile("car").setVehicle("car") + setProfiles(List.of( + TestProfiles.constantSpeed("foot"), + TestProfiles.constantSpeed("car") ))). setOSMFile(testOsm3). setGraphHopperLocation(ghLoc); instance.load(); assertEquals(5, instance.getBaseGraph().getNodes()); - assertEquals("foot_access,foot_average_speed,foot_priority,car_access,car_average_speed,foot_subnetwork,car_subnetwork,roundabout,road_class,road_class_link,road_environment,max_speed,road_access,ferry_speed,foot_network", + assertEquals("road_class,road_environment,roundabout,road_class_link,max_speed,foot_subnetwork,car_subnetwork", instance.getEncodingManager().getEncodedValues().stream().map(EncodedValue::getName).collect(Collectors.joining(","))); } @@ -521,12 +478,8 @@ public void testFailsForWrongEVConfig() { new GraphHopperConfig(). putObject("datareader.file", testOsm3). putObject("datareader.dataaccess", "RAM"). - putObject("graph.vehicles", "foot,car"). putObject("import.osm.ignored_highways", ""). - setProfiles(Arrays.asList( - new Profile("foot").setVehicle("foot"), - new Profile("car").setVehicle("car") - ))). + setProfiles(List.of(TestProfiles.constantSpeed("car")))). setGraphHopperLocation(ghLoc); instance.importOrLoad(); // older versions <= 0.12 did not store this property, ensure that we fail to load it @@ -542,24 +495,19 @@ public void testFailsForWrongEVConfig() { putObject("datareader.dataaccess", "RAM"). putObject("graph.location", ghLoc). putObject("graph.encoded_values", "road_environment,road_class"). - putObject("graph.vehicles", "foot,car"). putObject("import.osm.ignored_highways", ""). - setProfiles(Arrays.asList( - new Profile("foot").setVehicle("foot"), - new Profile("car").setVehicle("car") - ))). + setProfiles(List.of(TestProfiles.constantSpeed("car")))). setOSMFile(testOsm3); instance.load(); assertEquals(5, instance.getBaseGraph().getNodes()); - assertEquals("foot_access,foot_average_speed,foot_priority,car_access,car_average_speed,foot_subnetwork,car_subnetwork,roundabout,road_class,road_class_link,road_environment,max_speed,road_access,ferry_speed,foot_network", instance.getEncodingManager().getEncodedValues().stream().map(EncodedValue::getName).collect(Collectors.joining(","))); + assertEquals("road_class,road_environment,roundabout,road_class_link,max_speed,car_subnetwork", instance.getEncodingManager().getEncodedValues().stream().map(EncodedValue::getName).collect(Collectors.joining(","))); } @Test public void testNoNPE_ifLoadNotSuccessful() { String profile = "profile"; - String vehicle = "car"; instance = new GraphHopper(). - setProfiles(new Profile(profile).setVehicle(vehicle)). + setProfiles(new Profile(profile)). setStoreOnFlush(true). setGraphHopperLocation(ghLoc); try { @@ -576,7 +524,7 @@ public void testNoNPE_ifLoadNotSuccessful() { @Test public void testDoesNotCreateEmptyFolderIfLoadingFromNonExistingPath() { instance = new GraphHopper(); - instance.setProfiles(new Profile("car").setVehicle("car")); + instance.setProfiles(new Profile("car")); instance.setGraphHopperLocation(ghLoc); assertFalse(instance.load()); assertFalse(new File(ghLoc).exists()); @@ -597,7 +545,7 @@ public void testFailsForMissingParameters() { // missing OSM file to import instance = new GraphHopper(). - setProfiles(new Profile("car").setVehicle("car")). + setProfiles(TestProfiles.constantSpeed("car")). setStoreOnFlush(true). setGraphHopperLocation(ghLoc); ex = assertThrows(IllegalStateException.class, instance::importOrLoad); @@ -614,7 +562,7 @@ public void testFailsForMissingParameters() { // Import is possible even if no storeOnFlush is specified BUT here we miss the OSM file instance = new GraphHopper(). - setProfiles(new Profile("car").setVehicle("car")). + setProfiles(TestProfiles.constantSpeed("car")). setStoreOnFlush(false). setGraphHopperLocation(ghLoc); ex = assertThrows(IllegalStateException.class, instance::importOrLoad); @@ -626,14 +574,14 @@ public void testFailsForMissingParameters() { public void testFootOnly() { // now only footable ways are imported => no A D C and B D E => the other both ways have pillar nodes! final String profile = "foot_profile"; - final String vehicle = "foot"; instance = new GraphHopper(). setStoreOnFlush(false). - setProfiles(new Profile(profile).setVehicle(vehicle)). + setEncodedValuesString("foot_access, foot_priority, foot_average_speed"). + setProfiles(TestProfiles.accessSpeedAndPriority(profile, "foot")). setGraphHopperLocation(ghLoc). setOSMFile(testOsm3); // exclude motorways which aren't accessible for foot - instance.getReaderConfig().setIgnoredHighways(Arrays.asList("motorway")); + instance.getReaderConfig().setIgnoredHighways(List.of("motorway")); instance.getCHPreparationHandler().setCHProfiles(new CHProfile(profile)); instance.importOrLoad(); @@ -653,14 +601,14 @@ public void testFootOnly() { @Test public void testVia() { final String profile = "profile"; - final String vehicle = "car"; + instance = new GraphHopper().setStoreOnFlush(true). init(new GraphHopperConfig(). putObject("datareader.file", testOsm3). putObject("prepare.min_network_size", 0). - putObject("graph.vehicles", vehicle). + putObject("graph.encoded_values", "car_access, car_average_speed"). putObject("import.osm.ignored_highways", ""). - setProfiles(Collections.singletonList(new Profile(profile).setVehicle(vehicle))). + setProfiles(List.of(TestProfiles.accessAndSpeed(profile, "car"))). setCHProfiles(Collections.singletonList(new CHProfile(profile))) ). setGraphHopperLocation(ghLoc); @@ -694,19 +642,19 @@ public void testMultipleCHPreparationsInParallel() { GraphHopper hopper = new GraphHopper(). setStoreOnFlush(false). setProfiles( - new Profile("car_profile").setVehicle("car"), - new Profile("mtb_profile").setVehicle("mtb"), - new Profile("bike_profile").setVehicle("racingbike"), - new Profile("foot_profile").setVehicle("foot") + TestProfiles.constantSpeed("p1", 60), + TestProfiles.constantSpeed("p2", 70), + TestProfiles.constantSpeed("p3", 80), + TestProfiles.constantSpeed("p4", 90) ). setGraphHopperLocation(ghLoc). setOSMFile(testOsm); hopper.getCHPreparationHandler() .setCHProfiles( - new CHProfile("car_profile"), - new CHProfile("mtb_profile"), - new CHProfile("bike_profile"), - new CHProfile("foot_profile") + new CHProfile("p1"), + new CHProfile("p2"), + new CHProfile("p3"), + new CHProfile("p4") ) .setPreparationThreads(threadCount); @@ -741,19 +689,19 @@ public void testMultipleLMPreparationsInParallel() { GraphHopper hopper = new GraphHopper(). setStoreOnFlush(false). setProfiles(Arrays.asList( - new Profile("car_profile").setVehicle("car"), - new Profile("mtb_profile").setVehicle("mtb"), - new Profile("bike_profile").setVehicle("racingbike"), - new Profile("foot_profile").setVehicle("foot") + TestProfiles.constantSpeed("p1", 60), + TestProfiles.constantSpeed("p2", 70), + TestProfiles.constantSpeed("p3", 80), + TestProfiles.constantSpeed("p4", 90) )). setGraphHopperLocation(ghLoc). setOSMFile(testOsm); hopper.getLMPreparationHandler(). setLMProfiles( - new LMProfile("car_profile"), - new LMProfile("mtb_profile"), - new LMProfile("bike_profile"), - new LMProfile("foot_profile") + new LMProfile("p1"), + new LMProfile("p2"), + new LMProfile("p3"), + new LMProfile("p4") ). setPreparationThreads(threadCount); @@ -781,11 +729,11 @@ public void testMultipleLMPreparationsInParallel() { } @Test - public void testGetMultipleWeightingsForCH() { + public void testMultipleProfilesForCH() { GraphHopper hopper = new GraphHopper(). setProfiles( - new Profile("profile1").setVehicle("car"), - new Profile("profile2").setCustomModel(new CustomModel().setDistanceInfluence(1000d)).setVehicle("car") + TestProfiles.constantSpeed("profile1", 60), + TestProfiles.constantSpeed("profile2", 100) ). setStoreOnFlush(false). setGraphHopperLocation(ghLoc). @@ -800,48 +748,38 @@ public void testGetMultipleWeightingsForCH() { @Test public void testProfilesMustNotBeChanged() { { - GraphHopper hopper = createHopperWithProfiles(Arrays.asList( - new Profile("car").setVehicle("car"), - new Profile("custom").setCustomModel(new CustomModel().setDistanceInfluence(3d)).setVehicle("car") + GraphHopper hopper = createHopperWithProfiles(List.of( + TestProfiles.constantSpeed("bike1", 60), + TestProfiles.constantSpeed("bike2", 120) )); hopper.importOrLoad(); hopper.close(); } { // load without problem - GraphHopper hopper = createHopperWithProfiles(Arrays.asList( - new Profile("car").setVehicle("car"), - new Profile("custom").setCustomModel(new CustomModel().setDistanceInfluence(3d)).setVehicle("car") + GraphHopper hopper = createHopperWithProfiles(List.of( + TestProfiles.constantSpeed("bike1", 60), + TestProfiles.constantSpeed("bike2", 120) )); hopper.importOrLoad(); hopper.close(); } - { - // problem: the vehicle was changed. this is not allowed. - GraphHopper hopper = createHopperWithProfiles(Arrays.asList( - new Profile("car").setVehicle("bike"), - new Profile("custom").setCustomModel(new CustomModel().setDistanceInfluence(3d)).setVehicle("car") - )); - IllegalStateException e = assertThrows(IllegalStateException.class, hopper::importOrLoad); - assertTrue(e.getMessage().contains("Profiles do not match"), e.getMessage()); - hopper.close(); - } { // problem: the profile changed (slightly). we do not allow this because we would potentially need to re-calculate the subnetworks - GraphHopper hopper = createHopperWithProfiles(Arrays.asList( - new Profile("car").setVehicle("car"), - new Profile("custom").setCustomModel(new CustomModel().setDistanceInfluence(80d)).setVehicle("car") + GraphHopper hopper = createHopperWithProfiles(List.of( + TestProfiles.constantSpeed("bike1", 60), + TestProfiles.constantSpeed("bike2", 110) )); IllegalStateException e = assertThrows(IllegalStateException.class, hopper::importOrLoad); assertTrue(e.getMessage().contains("Profiles do not match"), e.getMessage()); hopper.close(); } { - // problem: we add another profile, which is not allowed, because there would be no subnetwork ev for it - GraphHopper hopper = createHopperWithProfiles(Arrays.asList( - new Profile("car").setVehicle("car"), - new Profile("custom").setCustomModel(new CustomModel().setDistanceInfluence(3d)).setVehicle("car"), - new Profile("car2").setVehicle("car") + // problem: we add another profile, which is not allowed either, because there would be no subnetwork ev for it + GraphHopper hopper = createHopperWithProfiles(List.of( + TestProfiles.constantSpeed("bike1", 60), + TestProfiles.constantSpeed("bike2", 120), + TestProfiles.constantSpeed("bike3", 110) )); IllegalStateException e = assertThrows(IllegalStateException.class, hopper::importOrLoad); assertTrue(e.getMessage().contains("Profiles do not match"), e.getMessage()); @@ -850,8 +788,8 @@ public void testProfilesMustNotBeChanged() { { // problem: we remove a profile, which would technically be possible, but does not save memory either. it // could be useful to disable a profile, but currently we just force a new import. - GraphHopper hopper = createHopperWithProfiles(Arrays.asList( - new Profile("car").setVehicle("car") + GraphHopper hopper = createHopperWithProfiles(List.of( + TestProfiles.constantSpeed("bike1", 60) )); IllegalStateException e = assertThrows(IllegalStateException.class, hopper::importOrLoad); assertTrue(e.getMessage().contains("Profiles do not match"), e.getMessage()); @@ -872,18 +810,18 @@ private GraphHopper createHopperWithProfiles(List profiles) { @Test public void testLoadingLMAndCHProfiles() { + Profile profile = TestProfiles.constantSpeed("car"); GraphHopper hopper = new GraphHopper() .setGraphHopperLocation(ghLoc) .setOSMFile(testOsm) - .setProfiles(new Profile("car").setVehicle("car")); + .setProfiles(profile); hopper.getLMPreparationHandler().setLMProfiles(new LMProfile("car")); hopper.getCHPreparationHandler().setCHProfiles(new CHProfile("car")); hopper.importOrLoad(); hopper.close(); // load without problem - hopper = new GraphHopper() - .setProfiles(new Profile("car").setVehicle("car")); + hopper = new GraphHopper().setProfiles(profile); hopper.getLMPreparationHandler().setLMProfiles(new LMProfile("car")); hopper.getCHPreparationHandler().setCHProfiles(new CHProfile("car")); hopper.setGraphHopperLocation(ghLoc); @@ -899,8 +837,7 @@ public void testLoadingLMAndCHProfiles() { props.flush(); // problem: LM version does not match the actual profile - hopper = new GraphHopper() - .setProfiles(new Profile("car").setVehicle("car")); + hopper = new GraphHopper().setProfiles(profile); hopper.getLMPreparationHandler().setLMProfiles(new LMProfile("car")); hopper.setGraphHopperLocation(ghLoc); IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, hopper::load); @@ -908,8 +845,7 @@ public void testLoadingLMAndCHProfiles() { hopper.close(); // problem: CH version does not match the actual profile - hopper = new GraphHopper() - .setProfiles(new Profile("car").setVehicle("car")); + hopper = new GraphHopper().setProfiles(profile); hopper.getCHPreparationHandler().setCHProfiles(new CHProfile("car")); hopper.setGraphHopperLocation(ghLoc); ex = assertThrows(IllegalArgumentException.class, hopper::load); diff --git a/core/src/test/java/com/graphhopper/reader/osm/OSMReaderTest.java b/core/src/test/java/com/graphhopper/reader/osm/OSMReaderTest.java index 5e8d2a402c0..10237c77591 100644 --- a/core/src/test/java/com/graphhopper/reader/osm/OSMReaderTest.java +++ b/core/src/test/java/com/graphhopper/reader/osm/OSMReaderTest.java @@ -21,18 +21,21 @@ import com.graphhopper.GHResponse; import com.graphhopper.GraphHopper; import com.graphhopper.GraphHopperTest; -import com.graphhopper.config.Profile; +import com.graphhopper.config.TurnCostsConfig; import com.graphhopper.reader.ReaderElement; import com.graphhopper.reader.ReaderRelation; import com.graphhopper.reader.ReaderWay; import com.graphhopper.reader.dem.ElevationProvider; import com.graphhopper.reader.dem.SRTMProvider; -import com.graphhopper.reader.osm.conditional.DateRangeParser; import com.graphhopper.routing.OSMReaderConfig; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.*; import com.graphhopper.routing.util.countryrules.CountryRuleFactory; -import com.graphhopper.routing.util.parsers.*; +import com.graphhopper.routing.util.parsers.CountryParser; +import com.graphhopper.routing.util.parsers.OSMBikeNetworkTagParser; +import com.graphhopper.routing.util.parsers.OSMMtbNetworkTagParser; +import com.graphhopper.routing.util.parsers.OSMRoadAccessParser; import com.graphhopper.storage.*; import com.graphhopper.storage.index.LocationIndex; import com.graphhopper.storage.index.Snap; @@ -49,6 +52,7 @@ import java.util.HashMap; import java.util.List; +import static com.graphhopper.routing.util.TransportationMode.CAR; import static com.graphhopper.util.GHUtility.readCountries; import static org.junit.jupiter.api.Assertions.*; @@ -142,14 +146,6 @@ protected int findID(LocationIndex index, double lat, double lon) { return index.findClosest(lat, lon, EdgeFilter.ALL_EDGES).getClosestNode(); } - @Test - public void testSort() { - GraphHopper hopper = new GraphHopperFacade(file1).setSortGraph(true).importOrLoad(); - NodeAccess na = hopper.getBaseGraph().getNodeAccess(); - assertEquals(10, na.getLon(findID(hopper.getLocationIndex(), 49, 10)), 1e-3); - assertEquals(51.249, na.getLat(findID(hopper.getLocationIndex(), 51.2492152, 9.4317166)), 1e-3); - } - @Test public void testOneWay() { GraphHopper hopper = new GraphHopperFacade(file2) @@ -387,10 +383,10 @@ public void testBarrierBetweenWays() { @Test public void testFords() { GraphHopper hopper = new GraphHopper(); - hopper.setVehiclesString("car|block_fords=true"); + hopper.setEncodedValuesString("car_access|block_fords=true,car_average_speed"); hopper.setOSMFile(getClass().getResource("test-barriers3.xml").getFile()). setGraphHopperLocation(dir). - setProfiles(new Profile("car").setVehicle("car")). + setProfiles(TestProfiles.accessAndSpeed("car")). setMinNetworkSize(0). importOrLoad(); Graph graph = hopper.getBaseGraph(); @@ -478,11 +474,14 @@ public void testBarriersOnTowerNodes() { } @Test - public void testRelation() { + public void testBikeAndMtbRelation() { EnumEncodedValue bikeNetworkEnc = new EnumEncodedValue<>(BikeNetwork.KEY, RouteNetwork.class); - EncodingManager manager = new EncodingManager.Builder().add(bikeNetworkEnc).build(); + EnumEncodedValue mtbNetworkEnc = new EnumEncodedValue<>(MtbNetwork.KEY, RouteNetwork.class); + EncodingManager manager = new EncodingManager.Builder().add(mtbNetworkEnc).add(bikeNetworkEnc).build(); OSMParsers osmParsers = new OSMParsers() - .addRelationTagParser(relConf -> new OSMBikeNetworkTagParser(bikeNetworkEnc, relConf)); + .addRelationTagParser(relConf -> new OSMBikeNetworkTagParser(bikeNetworkEnc, relConf)) + .addRelationTagParser(relConf -> new OSMMtbNetworkTagParser(mtbNetworkEnc, relConf)); + ReaderRelation osmRel = new ReaderRelation(1); osmRel.add(new ReaderRelation.Member(ReaderElement.Type.WAY, 1, "")); osmRel.add(new ReaderRelation.Member(ReaderElement.Type.WAY, 2, "")); @@ -494,6 +493,7 @@ public void testRelation() { osmRel.setTag("route", "bicycle"); osmRel.setTag("network", "lcn"); + // bikeNetworkEnc and MtbNetworkEnc share the same instance of the relFlags IntsRef relFlags = manager.createRelationFlags(); IntsRefEdgeIntAccess intAccess = new IntsRefEdgeIntAccess(relFlags); int edgeId = 0; @@ -512,12 +512,36 @@ public void testRelation() { osmParsers.handleRelationTags(osmRel, relFlags); assertEquals(RouteNetwork.NATIONAL, transformEnc.getEnum(false, edgeId, intAccess)); assertNotEquals(before, relFlags); + + // The further tests below are for an edge which is part of a bike and another mountainbike relation + osmRel.clearTags(); + + // this is pretty ugly: the mtb network parser writes to the edge flags we pass into it, but at a location we + // don't know, so we need to get the internal enc to read the flags below + transformEnc = ((OSMMtbNetworkTagParser) osmParsers.getRelationTagParsers().get(1)).getTransformerRouteRelEnc(); + + osmRel.setTag("route", "mtb"); + osmRel.setTag("network", "lcn"); + + osmParsers.handleRelationTags(osmRel, relFlags); + assertEquals(RouteNetwork.LOCAL, transformEnc.getEnum(false, edgeId, intAccess)); + + // unchanged network + osmParsers.handleRelationTags(osmRel, relFlags); + assertEquals(RouteNetwork.LOCAL, transformEnc.getEnum(false, edgeId, intAccess)); + assertEquals(RouteNetwork.LOCAL, transformEnc.getEnum(false, edgeId, intAccess)); + + // overwrite network + osmRel.setTag("network", "ncn"); + osmParsers.handleRelationTags(osmRel, relFlags); + assertEquals(RouteNetwork.NATIONAL, transformEnc.getEnum(false, edgeId, intAccess)); + assertNotEquals(before, relFlags); } @Test public void testTurnRestrictionsFromXML() { String fileTurnRestrictions = "test-restrictions.xml"; - GraphHopper hopper = new GraphHopperFacade(fileTurnRestrictions, true, ""). + GraphHopper hopper = new GraphHopperFacade(fileTurnRestrictions, ""). importOrLoad(); Graph graph = hopper.getBaseGraph(); @@ -588,7 +612,7 @@ public void testTurnRestrictionsFromXML() { @Test public void testTurnRestrictionsViaHgvTransportationMode() { String fileTurnRestrictions = "test-restrictions.xml"; - GraphHopper hopper = new GraphHopperFacade(fileTurnRestrictions, true, ""). + GraphHopper hopper = new GraphHopperFacade(fileTurnRestrictions, ""). importOrLoad(); Graph graph = hopper.getBaseGraph(); @@ -604,17 +628,16 @@ public void testTurnRestrictionsViaHgvTransportationMode() { int edge3_8 = GHUtility.getEdge(graph, n3, n8).getEdge(); BooleanEncodedValue carTCEnc = hopper.getEncodingManager().getTurnBooleanEncodedValue(TurnRestriction.key("car")); - BooleanEncodedValue roadsTCEnc = hopper.getEncodingManager().getTurnBooleanEncodedValue(TurnRestriction.key("roads")); + BooleanEncodedValue truckTCEnc = hopper.getEncodingManager().getTurnBooleanEncodedValue(TurnRestriction.key("truck")); assertFalse(tcStorage.get(carTCEnc, edge9_3, n3, edge3_8)); - assertTrue(tcStorage.get(roadsTCEnc, edge9_3, n3, edge3_8)); + assertTrue(tcStorage.get(truckTCEnc, edge9_3, n3, edge3_8)); } @Test public void testRoadAttributes() { String fileRoadAttributes = "test-road-attributes.xml"; GraphHopper hopper = new GraphHopperFacade(fileRoadAttributes); - hopper.setEncodedValuesString("max_width,max_height,max_weight"); hopper.importOrLoad(); DecimalEncodedValue widthEnc = hopper.getEncodingManager().getDecimalEncodedValue(MaxWidth.KEY); @@ -684,30 +707,13 @@ public void testReadEleFromDataProvider() { @Test public void testTurnFlagCombination() { GraphHopper hopper = new GraphHopper(); - hopper.setVehicleEncodedValuesFactory((name, config) -> { - if (name.equals("truck")) { - return VehicleEncodedValues.car(new PMap(config).putObject("name", "truck")); - } else { - return new DefaultVehicleEncodedValuesFactory().createVehicleEncodedValues(name, config); - } - }); - hopper.setVehicleTagParserFactory((lookup, name, config) -> { - if (name.equals("truck")) { - return new VehicleTagParsers( - new CarAccessParser(lookup.getBooleanEncodedValue(VehicleAccess.key("truck")), lookup.getBooleanEncodedValue(Roundabout.KEY), config, TransportationMode.HGV) - .init(config.getObject("date_range_parser", new DateRangeParser())), - new CarAverageSpeedParser(lookup.getDecimalEncodedValue(VehicleSpeed.key("truck")), lookup.getDecimalEncodedValue(FerrySpeed.KEY)), - null - ); - } - return new DefaultVehicleTagParserFactory().createParsers(lookup, name, config); - }); + hopper.setEncodedValuesString("car_average_speed,car_access,bike_access,bike_average_speed,bike_priority"); hopper.setOSMFile(getClass().getResource("test-multi-profile-turn-restrictions.xml").getFile()). setGraphHopperLocation(dir). setProfiles( - new Profile("bike").setVehicle("bike").setTurnCosts(true), - new Profile("car").setVehicle("car").setTurnCosts(true), - new Profile("truck").setVehicle("truck").setTurnCosts(true) + TestProfiles.accessAndSpeed("bike").setTurnCostsConfig(new TurnCostsConfig(List.of("bicycle"))), + TestProfiles.accessAndSpeed("car").setTurnCostsConfig(new TurnCostsConfig(List.of("motorcar", "motor_vehicle"))), + TestProfiles.accessAndSpeed("truck", "car").setTurnCostsConfig(new TurnCostsConfig(List.of("hgv", "motor_vehicle"))) ). importOrLoad(); EncodingManager manager = hopper.getEncodingManager(); @@ -742,7 +748,7 @@ public void testTurnFlagCombination() { @Test public void testConditionalTurnRestriction() { String fileConditionalTurnRestrictions = "test-conditional-turn-restrictions.xml"; - GraphHopper hopper = new GraphHopperFacade(fileConditionalTurnRestrictions, true, ""). + GraphHopper hopper = new GraphHopperFacade(fileConditionalTurnRestrictions, ""). setMinNetworkSize(0). importOrLoad(); @@ -811,7 +817,7 @@ public void testConditionalTurnRestriction() { @Test public void testMultipleTurnRestrictions() { String fileMultipleConditionalTurnRestrictions = "test-multiple-conditional-turn-restrictions.xml"; - GraphHopper hopper = new GraphHopperFacade(fileMultipleConditionalTurnRestrictions, true, ""). + GraphHopper hopper = new GraphHopperFacade(fileMultipleConditionalTurnRestrictions, ""). importOrLoad(); Graph graph = hopper.getBaseGraph(); @@ -853,7 +859,7 @@ public void testMultipleTurnRestrictions() { @Test public void testPreferredLanguage() { - GraphHopper hopper = new GraphHopperFacade(file1, false, "de"). + GraphHopper hopper = new GraphHopperFacade(file1, "de"). importOrLoad(); BaseGraph graph = hopper.getBaseGraph(); int n20 = AbstractGraphStorageTester.getIdOf(graph, 52); @@ -861,7 +867,7 @@ public void testPreferredLanguage() { assertTrue(iter.next()); assertEquals("straße 123, B 122", iter.getName()); - hopper = new GraphHopperFacade(file1, false, "el"). + hopper = new GraphHopperFacade(file1, "el"). importOrLoad(); graph = hopper.getBaseGraph(); n20 = AbstractGraphStorageTester.getIdOf(graph, 52); @@ -904,7 +910,8 @@ protected File _getOSMFile() { return new File(getClass().getResource(file2).getFile()); } }.setOSMFile("dummy"). - setProfiles(new Profile("profile").setVehicle("car")). + setEncodedValuesString("car_access,car_average_speed"). + setProfiles(TestProfiles.accessAndSpeed("profile", "car")). setMinNetworkSize(0). setGraphHopperLocation(dir). importOrLoad(); @@ -926,10 +933,10 @@ protected File _getOSMFile() { @Test public void testCountries() throws IOException { - EncodingManager em = new EncodingManager.Builder().build(); - EnumEncodedValue roadAccessEnc = em.getEnumEncodedValue(RoadAccess.KEY, RoadAccess.class); + EnumEncodedValue roadAccessEnc = RoadAccess.create(); + EncodingManager em = new EncodingManager.Builder().add(roadAccessEnc).build(); OSMParsers osmParsers = new OSMParsers(); - osmParsers.addWayTagParser(new OSMRoadAccessParser(roadAccessEnc, OSMRoadAccessParser.toOSMRestrictions(TransportationMode.CAR))); + osmParsers.addWayTagParser(new OSMRoadAccessParser(roadAccessEnc, OSMRoadAccessParser.toOSMRestrictions(CAR))); BaseGraph graph = new BaseGraph.Builder(em).create(); OSMReader reader = new OSMReader(graph, osmParsers, new OSMReaderConfig()); reader.setCountryRuleFactory(new CountryRuleFactory()); @@ -958,7 +965,7 @@ public void testCurvedWayAlongBorder() throws IOException { // see https://discuss.graphhopper.com/t/country-of-way-is-wrong-on-road-near-border-with-curvature/6908/2 EnumEncodedValue countryEnc = Country.create(); EncodingManager em = EncodingManager.start() - .add(VehicleEncodedValues.car(new PMap())) + .add(VehicleSpeed.create("car", 5, 5, false)).add(VehicleAccess.create("car")) .add(countryEnc) .build(); OSMParsers osmParsers = new OSMParsers() @@ -987,19 +994,20 @@ private AreaIndex createCountryIndex() { class GraphHopperFacade extends GraphHopper { public GraphHopperFacade(String osmFile) { - this(osmFile, false, ""); + this(osmFile, ""); } - public GraphHopperFacade(String osmFile, boolean turnCosts, String prefLang) { + public GraphHopperFacade(String osmFile, String prefLang) { setStoreOnFlush(false); setOSMFile(osmFile); setGraphHopperLocation(dir); - if (turnCosts) setVehiclesString("roads|turn_costs=true|transportation_mode=HGV"); + String str = "max_width,max_height,max_weight,foot_access, foot_priority, foot_average_speed, car_access, car_average_speed, bike_access, bike_priority, bike_average_speed"; + setEncodedValuesString(str); setProfiles( - new Profile("foot").setVehicle("foot"), - new Profile("car").setVehicle("car").setTurnCosts(turnCosts), - new Profile("bike").setVehicle("bike").setTurnCosts(turnCosts), - new Profile("roads").setVehicle("roads").setTurnCosts(turnCosts) + TestProfiles.accessSpeedAndPriority("foot"), + TestProfiles.accessAndSpeed("car").setTurnCostsConfig(new TurnCostsConfig(List.of("motorcar", "motor_vehicle"))), + TestProfiles.accessSpeedAndPriority("bike").setTurnCostsConfig(new TurnCostsConfig(List.of("bicycle"))), + TestProfiles.constantSpeed("truck", 100).setTurnCostsConfig(new TurnCostsConfig(List.of("hgv", "motor_vehicle"))) ); getReaderConfig().setPreferredLanguage(prefLang); } diff --git a/core/src/test/java/com/graphhopper/reader/osm/conditional/ConditionalOSMTagInspectorTest.java b/core/src/test/java/com/graphhopper/reader/osm/conditional/ConditionalOSMTagInspectorTest.java deleted file mode 100644 index 5efca154e75..00000000000 --- a/core/src/test/java/com/graphhopper/reader/osm/conditional/ConditionalOSMTagInspectorTest.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * 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.reader.osm.conditional; - -import com.graphhopper.reader.ReaderWay; -import org.junit.jupiter.api.Test; - -import java.util.*; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * @author Robin Boldt - */ -public class ConditionalOSMTagInspectorTest extends CalendarBasedTest { - private static Set getSampleRestrictedValues() { - Set restrictedValues = new HashSet<>(); - restrictedValues.add("private"); - restrictedValues.add("agricultural"); - restrictedValues.add("forestry"); - restrictedValues.add("no"); - restrictedValues.add("restricted"); - restrictedValues.add("delivery"); - restrictedValues.add("military"); - restrictedValues.add("emergency"); - return restrictedValues; - } - - private static Set getSamplePermissiveValues() { - Set permissiveValues = new HashSet<>(); - permissiveValues.add("yes"); - permissiveValues.add("permissive"); - return permissiveValues; - } - - private static List getSampleConditionalTags() { - List conditionalTags = new ArrayList<>(); - conditionalTags.add("vehicle"); - conditionalTags.add("access"); - return conditionalTags; - } - - @Test - public void testConditionalAccept() { - Calendar cal = getCalendar(2014, Calendar.MARCH, 10); - ConditionalTagInspector acceptor = new ConditionalOSMTagInspector(cal, getSampleConditionalTags(), getSampleRestrictedValues(), getSamplePermissiveValues()); - ReaderWay way = new ReaderWay(1); - way.setTag("vehicle:conditional", "no @ (Aug 10-Aug 14)"); - assertFalse(acceptor.isPermittedWayConditionallyRestricted(way)); - } - - @Test - public void testConditionalAcceptNextYear() { - Calendar cal = getCalendar(2014, Calendar.MARCH, 10); - ConditionalTagInspector acceptor = new ConditionalOSMTagInspector(cal, getSampleConditionalTags(), getSampleRestrictedValues(), getSamplePermissiveValues()); - ReaderWay way = new ReaderWay(1); - way.setTag("vehicle:conditional", "no @ (2013 Mar 1-2013 Mar 31)"); - assertFalse(acceptor.isPermittedWayConditionallyRestricted(way)); - } - - @Test - public void testConditionalReject() { - Calendar cal = getCalendar(2014, Calendar.MARCH, 10); - ConditionalTagInspector acceptor = new ConditionalOSMTagInspector(cal, getSampleConditionalTags(), getSampleRestrictedValues(), getSamplePermissiveValues()); - ReaderWay way = new ReaderWay(1); - way.setTag("vehicle:conditional", "no @ (Mar 10-Aug 14)"); - assertTrue(acceptor.isPermittedWayConditionallyRestricted(way)); - } - - @Test - public void testConditionalAllowance() { - Calendar cal = getCalendar(2014, Calendar.MARCH, 10); - ConditionalTagInspector acceptor = new ConditionalOSMTagInspector(cal, getSampleConditionalTags(), getSampleRestrictedValues(), getSamplePermissiveValues()); - ReaderWay way = new ReaderWay(1); - way.setTag("vehicle:conditional", "yes @ (Mar 10-Aug 14)"); - assertTrue(acceptor.isRestrictedWayConditionallyPermitted(way)); - } - - @Test - public void testConditionalAllowanceReject() { - Calendar cal = getCalendar(2014, Calendar.MARCH, 10); - ConditionalTagInspector acceptor = new ConditionalOSMTagInspector(cal, getSampleConditionalTags(), getSampleRestrictedValues(), getSamplePermissiveValues()); - ReaderWay way = new ReaderWay(1); - way.setTag("vehicle:conditional", "no @ (Mar 10-Aug 14)"); - assertTrue(acceptor.isPermittedWayConditionallyRestricted(way)); - } - - @Test - public void testConditionalSingleDay() { - Calendar cal = getCalendar(2015, Calendar.DECEMBER, 27); - ConditionalTagInspector acceptor = new ConditionalOSMTagInspector(cal, getSampleConditionalTags(), getSampleRestrictedValues(), getSamplePermissiveValues()); - ReaderWay way = new ReaderWay(1); - way.setTag("vehicle:conditional", "no @ (Su)"); - assertTrue(acceptor.isPermittedWayConditionallyRestricted(way)); - } - - @Test - public void testConditionalAllowanceSingleDay() { - Calendar cal = getCalendar(2015, Calendar.DECEMBER, 27); - ConditionalTagInspector acceptor = new ConditionalOSMTagInspector(cal, getSampleConditionalTags(), getSampleRestrictedValues(), getSamplePermissiveValues()); - ReaderWay way = new ReaderWay(1); - way.setTag("vehicle:conditional", "yes @ (Su)"); - assertTrue(acceptor.isRestrictedWayConditionallyPermitted(way)); - } - -} diff --git a/core/src/test/java/com/graphhopper/reader/osm/conditional/ConditionalParserTest.java b/core/src/test/java/com/graphhopper/reader/osm/conditional/ConditionalParserTest.java deleted file mode 100644 index 4052554bf76..00000000000 --- a/core/src/test/java/com/graphhopper/reader/osm/conditional/ConditionalParserTest.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * 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.reader.osm.conditional; - -import org.junit.jupiter.api.Test; - -import java.text.ParseException; -import java.util.Calendar; -import java.util.HashSet; -import java.util.Set; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * @author Robin Boldt - */ -public class ConditionalParserTest extends CalendarBasedTest { - - private final HashSet restrictedValues = new HashSet<>(); - - public ConditionalParserTest() { - restrictedValues.add("private"); - restrictedValues.add("agricultural"); - restrictedValues.add("forestry"); - restrictedValues.add("no"); - restrictedValues.add("restricted"); - restrictedValues.add("delivery"); - restrictedValues.add("military"); - restrictedValues.add("emergency"); - } - - ConditionalParser createParser(Calendar date) { - return new ConditionalParser(restrictedValues).addConditionalValueParser(new DateRangeParser(date)); - } - - @Test - public void testParseConditional() throws ParseException { - String str = "no @ (2015 Sep 1-2015 Sep 30)"; - assertFalse(createParser(getCalendar(2015, Calendar.AUGUST, 31)).checkCondition(str)); - assertTrue(createParser(getCalendar(2015, Calendar.SEPTEMBER, 30)).checkCondition(str)); - } - - @Test - public void testParseAllowingCondition() throws ParseException { - assertFalse(createParser(getCalendar(2015, Calendar.JANUARY, 12)). - checkCondition("yes @ (2015 Sep 1-2015 Sep 30)")); - } - - @Test - public void testParsingOfLeading0() throws ParseException { - assertTrue(createParser(getCalendar(2015, Calendar.DECEMBER, 2)). - checkCondition("no @ (01.11. - 31.03.)")); - - assertTrue(createParser(getCalendar(2015, Calendar.DECEMBER, 2)). - checkCondition("no @ (01.11 - 31.03)")); - } - - @Test - public void testGetRange() throws Exception { - assertTrue(ConditionalParser.createNumberParser("weight", 11).checkCondition("weight > 10").isCheckPassed()); - assertFalse(ConditionalParser.createNumberParser("weight", 10).checkCondition("weight > 10").isCheckPassed()); - assertFalse(ConditionalParser.createNumberParser("weight", 9).checkCondition("weight > 10").isCheckPassed()); - assertFalse(ConditionalParser.createNumberParser("xy", 9).checkCondition("weight > 10").isValid()); - - Set set = new HashSet<>(); - set.add("no"); - ConditionalParser instance = new ConditionalParser(set). - setConditionalValueParser(ConditionalParser.createNumberParser("weight", 11)); - assertTrue(instance.checkCondition("no @weight>10")); - instance.setConditionalValueParser(ConditionalParser.createNumberParser("weight", 10)); - assertFalse(instance.checkCondition("no @weight>10")); - instance.setConditionalValueParser(ConditionalParser.createNumberParser("weight", 9)); - assertFalse(instance.checkCondition("no @weight>10")); - - instance.setConditionalValueParser(ConditionalParser.createNumberParser("weight", 11)); - assertFalse(instance.checkCondition("no @ weight < 10")); - instance.setConditionalValueParser(ConditionalParser.createNumberParser("weight", 10)); - assertFalse(instance.checkCondition("no @ weight < 10")); - instance.setConditionalValueParser(ConditionalParser.createNumberParser("weight", 9)); - assertTrue(instance.checkCondition("no @ weight < 10")); - - // equals is ignored for now (not that bad for weight) - instance.setConditionalValueParser(ConditionalParser.createNumberParser("weight", 11)); - assertFalse(instance.checkCondition("no @ weight <= 10")); - instance.setConditionalValueParser(ConditionalParser.createNumberParser("weight", 10)); - assertFalse(instance.checkCondition("no @ weight <= 10")); - instance.setConditionalValueParser(ConditionalParser.createNumberParser("weight", 9)); - assertTrue(instance.checkCondition("no @ weight <= 10")); - - instance.setConditionalValueParser(ConditionalParser.createNumberParser("weight", 11)); - assertFalse(instance.checkCondition("no @ weight<=10")); - instance.setConditionalValueParser(ConditionalParser.createNumberParser("weight", 10)); - assertFalse(instance.checkCondition("no @ weight<=10")); - instance.setConditionalValueParser(ConditionalParser.createNumberParser("weight", 9)); - assertTrue(instance.checkCondition("no @ weight<=10")); - - instance.setConditionalValueParser(ConditionalParser.createNumberParser("height", 1)); - assertFalse(instance.checkCondition("no @ height > 2")); - instance.setConditionalValueParser(ConditionalParser.createNumberParser("height", 2)); - assertFalse(instance.checkCondition("no @ height > 2")); - instance.setConditionalValueParser(ConditionalParser.createNumberParser("height", 3)); - assertTrue(instance.checkCondition("no @ height > 2")); - - // unit is allowed according to wiki - instance.setConditionalValueParser(ConditionalParser.createNumberParser("height", 1)); - assertFalse(instance.checkCondition("no @ height > 2t")); - instance.setConditionalValueParser(ConditionalParser.createNumberParser("height", 2)); - assertFalse(instance.checkCondition("no @ height > 2t")); - instance.setConditionalValueParser(ConditionalParser.createNumberParser("height", 3)); - assertTrue(instance.checkCondition("no @ height > 2t")); - } - - @Test - public void parseNumber() { - // TODO currently no unit conversation is done which can be required if a different one is passed in checkCondition - assertEquals(3, ConditionalParser.parseNumber("3t"), .1); - assertEquals(3.1, ConditionalParser.parseNumber("3.1 t"), .1); - assertEquals(3, ConditionalParser.parseNumber("3 meters"), .1); - } -} diff --git a/core/src/test/java/com/graphhopper/reader/osm/conditional/DateRangeParserTest.java b/core/src/test/java/com/graphhopper/reader/osm/conditional/DateRangeParserTest.java index 19226795cb6..ffb3a70afe8 100644 --- a/core/src/test/java/com/graphhopper/reader/osm/conditional/DateRangeParserTest.java +++ b/core/src/test/java/com/graphhopper/reader/osm/conditional/DateRangeParserTest.java @@ -27,7 +27,7 @@ /** * @author Robin Boldt */ -public class DateRangeParserTest extends CalendarBasedTest { +public class DateRangeParserTest { final DateRangeParser dateRangeParser = new DateRangeParser(); @Test @@ -218,4 +218,11 @@ private void assertSameDate(int year, int month, int day, String dateString) thr assertEquals(expected.get(Calendar.DAY_OF_MONTH), actual.get(Calendar.DAY_OF_MONTH)); } + protected Calendar getCalendar(int year, int month, int day) { + Calendar calendar = DateRangeParser.createCalendar(); + calendar.set(Calendar.YEAR, year); + calendar.set(Calendar.MONTH, month); + calendar.set(Calendar.DAY_OF_MONTH, day); + return calendar; + } } diff --git a/core/src/test/java/com/graphhopper/routing/AStarBidirectionTest.java b/core/src/test/java/com/graphhopper/routing/AStarBidirectionTest.java index e4815989a8e..6113af99561 100644 --- a/core/src/test/java/com/graphhopper/routing/AStarBidirectionTest.java +++ b/core/src/test/java/com/graphhopper/routing/AStarBidirectionTest.java @@ -19,17 +19,14 @@ package com.graphhopper.routing; import com.carrotsearch.hppc.IntArrayList; -import com.graphhopper.routing.ev.BooleanEncodedValue; import com.graphhopper.routing.ev.DecimalEncodedValue; import com.graphhopper.routing.ev.DecimalEncodedValueImpl; -import com.graphhopper.routing.ev.SimpleBooleanEncodedValue; import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.routing.util.TraversalMode; -import com.graphhopper.routing.weighting.ShortestWeighting; +import com.graphhopper.routing.weighting.SpeedWeighting; import com.graphhopper.routing.weighting.WeightApproximator; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.storage.BaseGraph; -import com.graphhopper.util.GHUtility; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -45,31 +42,30 @@ void infeasibleApproximator_noException() { // This means the resulting path contains the invalid search tree branch 2(old)-3-4 and is not the shortest path, // because the SPTEntry for node 3 still points to the outdated/deleted entry for node 2. // We do not expect an exception, though, because for an infeasible approximator we cannot expect optimal paths. - BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); - DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); - EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 2, 1, true); + EncodingManager em = EncodingManager.start().add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); // 0-1----2-3-4----5-6-7-8-9 // \ / // 10 - GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(100)); + graph.edge(0, 1).set(speedEnc, 1, 0).setDistance(100); // the distance 1-2 is longer than 1-10-2 // we deliberately use 2-1 as storage direction, even though the edge points from 1 to 2, because this way // we can reproduce the 'Calculating time should not require to read speed from edge in wrong direction' error // from #2600 - graph.edge(2, 1).setDistance(300).set(accessEnc, false, true).set(speedEnc, 60); - GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(100)); - GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(100)); + graph.edge(2, 1).setDistance(300).set(speedEnc, 0, 1); + graph.edge(2, 3).set(speedEnc, 1, 0).setDistance(100); + graph.edge(3, 4).set(speedEnc, 1, 0).setDistance(100); // distance 4-5 is very long - GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 5).setDistance(10_000)); - GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 6).setDistance(100)); - GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(6, 7).setDistance(100)); - GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(7, 8).setDistance(100)); - GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(8, 9).setDistance(100)); - GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 10).setDistance(100)); - GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(10, 2).setDistance(100)); + graph.edge(4, 5).set(speedEnc, 1, 0).setDistance(10_000); + graph.edge(5, 6).set(speedEnc, 1, 0).setDistance(100); + graph.edge(6, 7).set(speedEnc, 1, 0).setDistance(100); + graph.edge(7, 8).set(speedEnc, 1, 0).setDistance(100); + graph.edge(8, 9).set(speedEnc, 1, 0).setDistance(100); + graph.edge(1, 10).set(speedEnc, 1, 0).setDistance(100); + graph.edge(10, 2).set(speedEnc, 1, 0).setDistance(100); - Weighting weighting = new ShortestWeighting(accessEnc, speedEnc); + Weighting weighting = new SpeedWeighting(speedEnc); AStarBidirection algo = new AStarBidirection(graph, weighting, TraversalMode.NODE_BASED); algo.setApproximation(new InfeasibleApproximator()); Path path = algo.calcPath(0, 9); diff --git a/core/src/test/java/com/graphhopper/routing/AlternativeRouteCHTest.java b/core/src/test/java/com/graphhopper/routing/AlternativeRouteCHTest.java index 8b17101880c..212d9f4f5fb 100644 --- a/core/src/test/java/com/graphhopper/routing/AlternativeRouteCHTest.java +++ b/core/src/test/java/com/graphhopper/routing/AlternativeRouteCHTest.java @@ -19,17 +19,14 @@ import com.graphhopper.routing.ch.NodeOrderingProvider; import com.graphhopper.routing.ch.PrepareContractionHierarchies; -import com.graphhopper.routing.ev.BooleanEncodedValue; import com.graphhopper.routing.ev.DecimalEncodedValue; import com.graphhopper.routing.ev.DecimalEncodedValueImpl; -import com.graphhopper.routing.ev.SimpleBooleanEncodedValue; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.weighting.custom.CustomModelParser; +import com.graphhopper.routing.weighting.SpeedWeighting; import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.CHConfig; import com.graphhopper.storage.RoutingCHGraph; import com.graphhopper.storage.RoutingCHGraphImpl; -import com.graphhopper.util.GHUtility; import com.graphhopper.util.PMap; import org.junit.jupiter.api.Test; @@ -38,9 +35,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class AlternativeRouteCHTest { - private final BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); private final DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); - private final EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + private final EncodingManager em = EncodingManager.start().add(speedEnc).build(); public BaseGraph createTestGraph(EncodingManager tmpEM) { final BaseGraph graph = new BaseGraph.Builder(tmpEM).create(); @@ -59,21 +55,20 @@ public BaseGraph createTestGraph(EncodingManager tmpEM) { // has to be locally-shortest to be considered. // So we get all three alternatives. - GHUtility.setSpeed(60, 60, accessEnc, speedEnc, - graph.edge(5, 6).setDistance(10000), - graph.edge(6, 3).setDistance(10000), - graph.edge(3, 4).setDistance(10000), - graph.edge(4, 10).setDistance(10000), - graph.edge(6, 7).setDistance(10000), - graph.edge(7, 8).setDistance(10000), - graph.edge(8, 4).setDistance(10000), - graph.edge(5, 1).setDistance(10000), - graph.edge(1, 9).setDistance(10000), - graph.edge(9, 2).setDistance(10000), - graph.edge(2, 3).setDistance(10000), - graph.edge(4, 11).setDistance(9000), - graph.edge(11, 12).setDistance(9000), - graph.edge(12, 10).setDistance(10000)); + graph.edge(5, 6).setDistance(10000).set(speedEnc, 60); + graph.edge(6, 3).setDistance(10000).set(speedEnc, 60); + graph.edge(3, 4).setDistance(10000).set(speedEnc, 60); + graph.edge(4, 10).setDistance(10000).set(speedEnc, 60); + graph.edge(6, 7).setDistance(10000).set(speedEnc, 60); + graph.edge(7, 8).setDistance(10000).set(speedEnc, 60); + graph.edge(8, 4).setDistance(10000).set(speedEnc, 60); + graph.edge(5, 1).setDistance(10000).set(speedEnc, 60); + graph.edge(1, 9).setDistance(10000).set(speedEnc, 60); + graph.edge(9, 2).setDistance(10000).set(speedEnc, 60); + graph.edge(2, 3).setDistance(10000).set(speedEnc, 60); + graph.edge(4, 11).setDistance(9000).set(speedEnc, 60); + graph.edge(11, 12).setDistance(9000).set(speedEnc, 60); + graph.edge(12, 10).setDistance(10000).set(speedEnc, 60); graph.freeze(); return graph; @@ -84,7 +79,7 @@ private RoutingCHGraph prepareCH(BaseGraph graph) { // meet on all four possible paths from 5 to 10 // 5 ---> 11 will be reachable via shortcuts, as 11 is on shortest path 5 --> 12 final int[] nodeOrdering = new int[]{0, 10, 12, 4, 3, 2, 5, 1, 6, 7, 8, 9, 11}; - CHConfig chConfig = CHConfig.nodeBased("p", CustomModelParser.createFastestWeighting(accessEnc, speedEnc, em)); + CHConfig chConfig = CHConfig.nodeBased("p", new SpeedWeighting(speedEnc)); PrepareContractionHierarchies contractionHierarchies = PrepareContractionHierarchies.fromGraph(graph, chConfig); contractionHierarchies.useFixedNodeOrdering(NodeOrderingProvider.fromArray(nodeOrdering)); PrepareContractionHierarchies.Result res = contractionHierarchies.doWork(); diff --git a/core/src/test/java/com/graphhopper/routing/CustomizableConditionalRestrictionsTest.java b/core/src/test/java/com/graphhopper/routing/CustomizableConditionalRestrictionsTest.java new file mode 100644 index 00000000000..dfd70879e1c --- /dev/null +++ b/core/src/test/java/com/graphhopper/routing/CustomizableConditionalRestrictionsTest.java @@ -0,0 +1,68 @@ +package com.graphhopper.routing; + +import com.graphhopper.GHRequest; +import com.graphhopper.GHResponse; +import com.graphhopper.GraphHopper; +import com.graphhopper.GraphHopperConfig; +import com.graphhopper.json.Statement; +import com.graphhopper.routing.ev.FootTemporalAccess; +import com.graphhopper.util.CustomModel; +import com.graphhopper.util.Helper; +import com.graphhopper.util.details.PathDetail; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +public class CustomizableConditionalRestrictionsTest { + + private static final String GH_LOCATION = "target/routing-conditional-access-gh"; + + @BeforeEach + @AfterEach + public void setup() { + Helper.removeDir(new File(GH_LOCATION)); + } + + @Test + public void testConditionalAccess() { + GraphHopper hopper = new GraphHopper(). + setStoreOnFlush(false). + setEncodedValuesString(FootTemporalAccess.KEY); + + hopper.init(new GraphHopperConfig(). + setProfiles(List.of(TestProfiles.accessAndSpeed("foot", "foot"))). + putObject("graph.location", GH_LOCATION). + putObject("graph.encoded_values", "foot_temporal_access, foot_access, foot_average_speed"). + putObject("datareader.file", "../core/files/conditional-restrictions.osm.xml"). + putObject("prepare.min_network_size", "0"). + putObject("import.osm.ignored_highways", ""). + putObject("datareader.date_range_parser_day", "2023-08-01")); + hopper.importOrLoad(); + + String PD_KEY = "access_conditional"; + GHResponse rsp = hopper.route(new GHRequest(50.909136, 14.213924, 50.90918, 14.213549). + setProfile("foot"). + setPathDetails(Arrays.asList(PD_KEY))); + assertFalse(rsp.hasErrors(), rsp.getErrors().toString()); + List details = rsp.getBest().getPathDetails().get(PD_KEY); + assertEquals("no@(Jan15-Aug15)", details.get(0).getValue()); + assertEquals(2, details.size()); + assertEquals(32, rsp.getBest().getDistance(), 1); + + rsp = hopper.route(new GHRequest(50.909136, 14.213924, 50.90918, 14.213549). + setProfile("foot"). + setCustomModel(new CustomModel().addToPriority(Statement.If("foot_temporal_access == NO", Statement.Op.MULTIPLY, "0"))). + setPathDetails(Arrays.asList(PD_KEY))); + assertFalse(rsp.hasErrors(), rsp.getErrors().toString()); + assertEquals(16, rsp.getBest().getDistance(), 1); + details = rsp.getBest().getPathDetails().get(PD_KEY); + assertEquals(1, details.size()); + } +} diff --git a/core/src/test/java/com/graphhopper/routing/EdgeBasedRoutingAlgorithmTest.java b/core/src/test/java/com/graphhopper/routing/EdgeBasedRoutingAlgorithmTest.java index 06fa172e476..9ec43c85db3 100644 --- a/core/src/test/java/com/graphhopper/routing/EdgeBasedRoutingAlgorithmTest.java +++ b/core/src/test/java/com/graphhopper/routing/EdgeBasedRoutingAlgorithmTest.java @@ -196,7 +196,7 @@ public void testRandomGraph(String algoStr) { } if (strictViolations.size() > 0.05 * numQueries) { fail("Too many strict violations: " + strictViolations.size() + "/" + numQueries + "\n" + - Helper.join("\n", strictViolations)); + String.join("\n", strictViolations)); } } diff --git a/core/src/test/java/com/graphhopper/routing/HeadingRoutingTest.java b/core/src/test/java/com/graphhopper/routing/HeadingRoutingTest.java index 4640500d731..1c2ef11db6b 100644 --- a/core/src/test/java/com/graphhopper/routing/HeadingRoutingTest.java +++ b/core/src/test/java/com/graphhopper/routing/HeadingRoutingTest.java @@ -56,7 +56,13 @@ public void headingTest1() { // Test enforce start direction BooleanEncodedValue accessEnc = VehicleAccess.create("car"); DecimalEncodedValue speedEnc = VehicleSpeed.create("car", 5, 5, false); - EncodingManager encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc).add(Subnetwork.create("profile")).build(); + EncodingManager encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc) + .add(RoadClass.create()) + .add(RoadClassLink.create()) + .add(RoadEnvironment.create()) + .add(Roundabout.create()) + .add(MaxSpeed.create()) + .add(Subnetwork.create("profile")).build(); BaseGraph graph = createSquareGraph(encodingManager, accessEnc, speedEnc); Router router = createRouter(graph, encodingManager); @@ -80,7 +86,13 @@ public void headingTest2() { // Test enforce south start direction and east end direction BooleanEncodedValue accessEnc = VehicleAccess.create("car"); DecimalEncodedValue speedEnc = VehicleSpeed.create("car", 5, 5, false); - EncodingManager encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc).add(Subnetwork.create("profile")).build(); + EncodingManager encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc) + .add(RoadClass.create()) + .add(RoadClassLink.create()) + .add(RoadEnvironment.create()) + .add(Roundabout.create()) + .add(MaxSpeed.create()) + .add(Subnetwork.create("profile")).build(); BaseGraph graph = createSquareGraph(encodingManager, accessEnc, speedEnc); Router router = createRouter(graph, encodingManager); @@ -108,7 +120,13 @@ public void headingTest2() { public void headingTest3() { BooleanEncodedValue accessEnc = VehicleAccess.create("car"); DecimalEncodedValue speedEnc = VehicleSpeed.create("car", 5, 5, false); - EncodingManager encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc).add(Subnetwork.create("profile")).build(); + EncodingManager encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc) + .add(RoadClass.create()) + .add(RoadClassLink.create()) + .add(RoadEnvironment.create()) + .add(Roundabout.create()) + .add(MaxSpeed.create()) + .add(Subnetwork.create("profile")).build(); BaseGraph graph = createSquareGraph(encodingManager, accessEnc, speedEnc); Router router = createRouter(graph, encodingManager); @@ -134,7 +152,13 @@ public void headingTest4() { // Test straight via routing BooleanEncodedValue accessEnc = VehicleAccess.create("car"); DecimalEncodedValue speedEnc = VehicleSpeed.create("car", 5, 5, false); - EncodingManager encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc).add(Subnetwork.create("profile")).build(); + EncodingManager encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc) + .add(RoadClass.create()) + .add(RoadClassLink.create()) + .add(RoadEnvironment.create()) + .add(Roundabout.create()) + .add(MaxSpeed.create()) + .add(Subnetwork.create("profile")).build(); BaseGraph graph = createSquareGraph(encodingManager, accessEnc, speedEnc); Router router = createRouter(graph, encodingManager); @@ -160,7 +184,13 @@ public void headingTest5() { // Test independence of previous enforcement for subsequent paths BooleanEncodedValue accessEnc = VehicleAccess.create("car"); DecimalEncodedValue speedEnc = VehicleSpeed.create("car", 5, 5, false); - EncodingManager encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc).add(Subnetwork.create("profile")).build(); + EncodingManager encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc) + .add(RoadClass.create()) + .add(RoadClassLink.create()) + .add(RoadEnvironment.create()) + .add(Roundabout.create()) + .add(MaxSpeed.create()) + .add(Subnetwork.create("profile")).build(); BaseGraph graph = createSquareGraph(encodingManager, accessEnc, speedEnc); Router router = createRouter(graph, encodingManager); @@ -185,7 +215,13 @@ public void headingTest5() { public void testHeadingWithSnapFilter() { BooleanEncodedValue accessEnc = VehicleAccess.create("car"); DecimalEncodedValue speedEnc = VehicleSpeed.create("car", 5, 5, false); - EncodingManager encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc).add(Subnetwork.create("profile")).build(); + EncodingManager encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc) + .add(RoadClass.create()) + .add(RoadClassLink.create()) + .add(RoadEnvironment.create()) + .add(Roundabout.create()) + .add(MaxSpeed.create()) + .add(Subnetwork.create("profile")).build(); BaseGraph graph = createSquareGraphWithTunnel(encodingManager, accessEnc, speedEnc); Router router = createRouter(graph, encodingManager); // Start at 8 (slightly north to make it independent on some edge ordering and always use 8-3 or 3-8 as fallback) @@ -248,7 +284,13 @@ public void testHeadingWithSnapFilter() { public void testHeadingWithSnapFilter2() { BooleanEncodedValue accessEnc = VehicleAccess.create("car"); DecimalEncodedValue speedEnc = VehicleSpeed.create("car", 5, 5, false); - EncodingManager encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc).add(Subnetwork.create("profile")).build(); + EncodingManager encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc) + .add(RoadClass.create()) + .add(RoadClassLink.create()) + .add(RoadEnvironment.create()) + .add(Roundabout.create()) + .add(MaxSpeed.create()) + .add(Subnetwork.create("profile")).build(); BaseGraph graph = createSquareGraphWithTunnel(encodingManager, accessEnc, speedEnc); Router router = createRouter(graph, encodingManager); // Start at 8 (slightly east to snap to edge 1->5 per default) @@ -282,7 +324,13 @@ public void headingTest6() { // Test if snaps at tower nodes are ignored BooleanEncodedValue accessEnc = VehicleAccess.create("car"); DecimalEncodedValue speedEnc = VehicleSpeed.create("car", 5, 5, false); - EncodingManager encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc).add(Subnetwork.create("profile")).build(); + EncodingManager encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc) + .add(RoadClass.create()) + .add(RoadClassLink.create()) + .add(RoadEnvironment.create()) + .add(Roundabout.create()) + .add(MaxSpeed.create()) + .add(Subnetwork.create("profile")).build(); BaseGraph graph = createSquareGraph(encodingManager, accessEnc, speedEnc); Router router = createRouter(graph, encodingManager); @@ -305,7 +353,7 @@ private Router createRouter(BaseGraph graph, EncodingManager encodingManager) { LocationIndexTree locationIndex = new LocationIndexTree(graph, new RAMDirectory()); locationIndex.prepareIndex(); Map profilesByName = new HashMap<>(); - profilesByName.put("profile", new Profile("profile").setVehicle("car")); + profilesByName.put("profile", TestProfiles.accessAndSpeed("profile", "car")); return new Router(graph.getBaseGraph(), encodingManager, locationIndex, profilesByName, new PathDetailsBuilderFactory(), new TranslationMap().doImport(), new RouterConfig(), new DefaultWeightingFactory(graph.getBaseGraph(), encodingManager), Collections.emptyMap(), Collections.emptyMap()); } diff --git a/core/src/test/java/com/graphhopper/routing/PathTest.java b/core/src/test/java/com/graphhopper/routing/PathTest.java index 1ee48734d3c..7862e571e13 100644 --- a/core/src/test/java/com/graphhopper/routing/PathTest.java +++ b/core/src/test/java/com/graphhopper/routing/PathTest.java @@ -17,12 +17,12 @@ */ package com.graphhopper.routing; +import com.carrotsearch.hppc.IntArrayList; import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.routing.util.TraversalMode; -import com.graphhopper.routing.weighting.ShortestWeighting; +import com.graphhopper.routing.weighting.SpeedWeighting; import com.graphhopper.routing.weighting.Weighting; -import com.graphhopper.routing.weighting.custom.CustomModelParser; import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.Graph; import com.graphhopper.storage.NodeAccess; @@ -44,15 +44,17 @@ * @author Peter Karich */ public class PathTest { - private final BooleanEncodedValue carAccessEnc = new SimpleBooleanEncodedValue("access", true); - private final DecimalEncodedValue carAvSpeedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); - private final EncodingManager carManager = EncodingManager.start().add(carAccessEnc).add(carAvSpeedEnc).build(); - private final BooleanEncodedValue mixedCarAccessEnc = new SimpleBooleanEncodedValue("mixed_car_access", true); - private final DecimalEncodedValue mixedCarSpeedEnc = new DecimalEncodedValueImpl("mixed_car_speed", 5, 5, false); - private final BooleanEncodedValue mixedFootAccessEnc = new SimpleBooleanEncodedValue("mixed_foot_access", true); - private final DecimalEncodedValue mixedFootSpeedEnc = new DecimalEncodedValueImpl("mixed_foot_speed", 4, 1, false); - private final EncodingManager mixedEncodingManager = EncodingManager.start().add(mixedCarAccessEnc). - add(mixedCarSpeedEnc).add(mixedFootAccessEnc).add(mixedFootSpeedEnc).build(); + private final DecimalEncodedValue carAvSpeedEnc = new DecimalEncodedValueImpl("speed", 5, 5, true); + private final EncodingManager carManager = EncodingManager.start().add(carAvSpeedEnc) + .add(Roundabout.create()).add(RoadClass.create()).add(RoadClassLink.create()).add(MaxSpeed.create()).build(); + private final DecimalEncodedValue mixedCarSpeedEnc = new DecimalEncodedValueImpl("mixed_car_speed", 5, 5, true); + private final DecimalEncodedValue mixedFootSpeedEnc = new DecimalEncodedValueImpl("mixed_foot_speed", 4, 1, true); + private final EncodingManager mixedEncodingManager = EncodingManager.start(). + add(mixedCarSpeedEnc).add(mixedFootSpeedEnc). + add(RoadClass.create()). + add(RoadClassLink.create()). + add(MaxSpeed.create()). + add(Roundabout.create()).build(); private final TranslationMap trMap = TranslationMapTest.SINGLETON; private final Translation tr = trMap.getWithFallBack(Locale.US); private final RoundaboutGraph roundaboutGraph = new RoundaboutGraph(); @@ -75,21 +77,21 @@ public void testWayList() { na.setNode(1, 1.0, 0.1); na.setNode(2, 2.0, 0.1); - EdgeIteratorState edge1 = g.edge(0, 1).setDistance(1000).set(carAccessEnc, true, true).set(carAvSpeedEnc, 10.0); + EdgeIteratorState edge1 = g.edge(0, 1).setDistance(1000).set(carAvSpeedEnc, 10.0, 10.0); edge1.setWayGeometry(Helper.createPointList(8, 1, 9, 1)); - EdgeIteratorState edge2 = g.edge(2, 1).setDistance(2000).set(carAccessEnc, true, true).set(carAvSpeedEnc, 50.0); + EdgeIteratorState edge2 = g.edge(2, 1).setDistance(2000).set(carAvSpeedEnc, 50.0, 50.0); edge2.setWayGeometry(Helper.createPointList(11, 1, 10, 1)); SPTEntry e1 = new SPTEntry(edge2.getEdge(), 2, 1, new SPTEntry(edge1.getEdge(), 1, 1, new SPTEntry(0, 1))); - Weighting weighting = CustomModelParser.createFastestWeighting(carAccessEnc, carAvSpeedEnc, carManager); + Weighting weighting = new SpeedWeighting(carAvSpeedEnc); Path path = extractPath(g, weighting, e1); // 0-1-2 assertPList(Helper.createPointList(0, 0.1, 8, 1, 9, 1, 1, 0.1, 10, 1, 11, 1, 2, 0.1), path.calcPoints()); InstructionList instr = InstructionsFromEdges.calcInstructions(path, path.graph, weighting, carManager, tr); Instruction tmp = instr.get(0); assertEquals(3000.0, tmp.getDistance(), 0.0); - assertEquals(504000L, tmp.getTime()); + assertEquals(140000L, tmp.getTime()); assertEquals("continue", tmp.getTurnDescription(tr)); assertEquals(6, tmp.getLength()); @@ -108,7 +110,7 @@ public void testWayList() { // force minor change for instructions edge2.setKeyValues(createKV(STREET_NAME, "2")); na.setNode(3, 1.0, 1.0); - g.edge(1, 3).setDistance(1000).set(carAccessEnc, true, true).set(carAvSpeedEnc, 10.0); + g.edge(1, 3).setDistance(1000).set(carAvSpeedEnc, 10.0, 10.0); e1 = new SPTEntry(edge2.getEdge(), 2, 1, new SPTEntry(edge1.getEdge(), 1, 1, @@ -120,13 +122,13 @@ public void testWayList() { tmp = instr.get(0); assertEquals(1000.0, tmp.getDistance(), 0); - assertEquals(360000L, tmp.getTime()); + assertEquals(100000L, tmp.getTime()); assertEquals("continue", tmp.getTurnDescription(tr)); assertEquals(3, tmp.getLength()); tmp = instr.get(1); assertEquals(2000.0, tmp.getDistance(), 0); - assertEquals(144000L, tmp.getTime()); + assertEquals(40000L, tmp.getTime()); assertEquals("turn sharp right onto 2", tmp.getTurnDescription(tr)); assertEquals(3, tmp.getLength()); acc = 0; @@ -144,13 +146,13 @@ public void testWayList() { instr = InstructionsFromEdges.calcInstructions(path, path.graph, weighting, carManager, tr); tmp = instr.get(0); assertEquals(2000.0, tmp.getDistance(), 0); - assertEquals(144000L, tmp.getTime()); + assertEquals(40000L, tmp.getTime()); assertEquals("continue onto 2", tmp.getTurnDescription(tr)); assertEquals(3, tmp.getLength()); tmp = instr.get(1); assertEquals(1000.0, tmp.getDistance(), 0); - assertEquals(360000L, tmp.getTime()); + assertEquals(100000L, tmp.getTime()); assertEquals("turn sharp left", tmp.getTurnDescription(tr)); assertEquals(3, tmp.getLength()); acc = 0; @@ -171,22 +173,22 @@ public void testFindInstruction() { na.setNode(4, 7.5, 0.25); na.setNode(5, 5.0, 1.0); - EdgeIteratorState edge1 = g.edge(0, 1).setDistance(1000).set(carAccessEnc, true, true).set(carAvSpeedEnc, 50.0); + EdgeIteratorState edge1 = g.edge(0, 1).setDistance(1000).set(carAvSpeedEnc, 50.0, 50.0); edge1.setWayGeometry(Helper.createPointList()); edge1.setKeyValues(createKV(STREET_NAME, "Street 1")); - EdgeIteratorState edge2 = g.edge(1, 2).setDistance(1000).set(carAccessEnc, true, true).set(carAvSpeedEnc, 50.0); + EdgeIteratorState edge2 = g.edge(1, 2).setDistance(1000).set(carAvSpeedEnc, 50.0, 50.0); edge2.setWayGeometry(Helper.createPointList()); edge2.setKeyValues(createKV(STREET_NAME, "Street 2")); - EdgeIteratorState edge3 = g.edge(2, 3).setDistance(1000).set(carAccessEnc, true, true).set(carAvSpeedEnc, 50.0); + EdgeIteratorState edge3 = g.edge(2, 3).setDistance(1000).set(carAvSpeedEnc, 50.0, 50.0); edge3.setWayGeometry(Helper.createPointList()); edge3.setKeyValues(createKV(STREET_NAME, "Street 3")); - EdgeIteratorState edge4 = g.edge(3, 4).setDistance(500).set(carAccessEnc, true, true).set(carAvSpeedEnc, 50.0); + EdgeIteratorState edge4 = g.edge(3, 4).setDistance(500).set(carAvSpeedEnc, 50.0, 50.0); edge4.setWayGeometry(Helper.createPointList()); edge4.setKeyValues(createKV(STREET_NAME, "Street 4")); - g.edge(1, 5).setDistance(10000).set(carAccessEnc, true, true).set(carAvSpeedEnc, 50.0); - g.edge(2, 5).setDistance(10000).set(carAccessEnc, true, true).set(carAvSpeedEnc, 50.0); - g.edge(3, 5).setDistance(100000).set(carAccessEnc, true, true).set(carAvSpeedEnc, 50.0); + g.edge(1, 5).setDistance(10000).set(carAvSpeedEnc, 50.0, 50.0); + g.edge(2, 5).setDistance(10000).set(carAvSpeedEnc, 50.0, 50.0); + g.edge(3, 5).setDistance(100000).set(carAvSpeedEnc, 50.0, 50.0); SPTEntry e1 = new SPTEntry(edge4.getEdge(), 4, 1, @@ -195,7 +197,7 @@ public void testFindInstruction() { new SPTEntry(edge1.getEdge(), 1, 1, new SPTEntry(0, 1) )))); - Weighting weighting = CustomModelParser.createFastestWeighting(carAccessEnc, carAvSpeedEnc, carManager); + Weighting weighting = new SpeedWeighting(carAvSpeedEnc); Path path = extractPath(g, weighting, e1); InstructionList il = InstructionsFromEdges.calcInstructions(path, path.graph, weighting, carManager, tr); @@ -212,12 +214,12 @@ public void testFindInstruction() { */ @Test void testCalcInstructionsRoundabout() { - calcInstructionsRoundabout(mixedCarAccessEnc, mixedCarSpeedEnc); - calcInstructionsRoundabout(mixedFootAccessEnc, mixedFootSpeedEnc); + calcInstructionsRoundabout(mixedCarSpeedEnc); + calcInstructionsRoundabout(mixedFootSpeedEnc); } - public void calcInstructionsRoundabout(BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { - Weighting weighting = new ShortestWeighting(accessEnc, speedEnc); + public void calcInstructionsRoundabout(DecimalEncodedValue speedEnc) { + Weighting weighting = new SpeedWeighting(speedEnc); Path p = new Dijkstra(roundaboutGraph.g, weighting, TraversalMode.NODE_BASED) .calcPath(1, 8); assertTrue(p.isFound()); @@ -225,7 +227,7 @@ public void calcInstructionsRoundabout(BooleanEncodedValue accessEnc, DecimalEnc InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncodingManager, tr); // Test instructions List tmpList = getTurnDescriptions(wayList); - assertEquals(Arrays.asList("continue onto MainStreet 1 2", + assertEquals(List.of("continue onto MainStreet 1 2", "At roundabout, take exit 3 onto 5-8", "arrive at destination"), tmpList); @@ -239,7 +241,7 @@ public void calcInstructionsRoundabout(BooleanEncodedValue accessEnc, DecimalEnc calcPath(1, 7); wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncodingManager, tr); tmpList = getTurnDescriptions(wayList); - assertEquals(Arrays.asList("continue onto MainStreet 1 2", + assertEquals(List.of("continue onto MainStreet 1 2", "At roundabout, take exit 2 onto MainStreet 4 7", "arrive at destination"), tmpList); @@ -251,13 +253,13 @@ public void calcInstructionsRoundabout(BooleanEncodedValue accessEnc, DecimalEnc @Test public void testCalcInstructionsRoundaboutBegin() { - Weighting weighting = new ShortestWeighting(mixedCarAccessEnc, mixedCarSpeedEnc); + Weighting weighting = new SpeedWeighting(mixedCarSpeedEnc); Path p = new Dijkstra(roundaboutGraph.g, weighting, TraversalMode.NODE_BASED) .calcPath(2, 8); assertTrue(p.isFound()); InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncodingManager, tr); List tmpList = getTurnDescriptions(wayList); - assertEquals(Arrays.asList("At roundabout, take exit 3 onto 5-8", + assertEquals(List.of("At roundabout, take exit 3 onto 5-8", "arrive at destination"), tmpList); } @@ -265,13 +267,13 @@ public void testCalcInstructionsRoundaboutBegin() { @Test public void testCalcInstructionsRoundaboutDirectExit() { roundaboutGraph.inverse3to9(); - Weighting weighting = new ShortestWeighting(mixedCarAccessEnc, mixedCarSpeedEnc); + Weighting weighting = new SpeedWeighting(mixedCarSpeedEnc); Path p = new Dijkstra(roundaboutGraph.g, weighting, TraversalMode.NODE_BASED) .calcPath(6, 8); assertTrue(p.isFound()); InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncodingManager, tr); List tmpList = getTurnDescriptions(wayList); - assertEquals(Arrays.asList("continue onto 3-6", + assertEquals(List.of("continue onto 3-6", "At roundabout, take exit 3 onto 5-8", "arrive at destination"), tmpList); @@ -280,21 +282,21 @@ public void testCalcInstructionsRoundaboutDirectExit() { @Test public void testCalcAverageSpeedDetails() { - Weighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); + Weighting weighting = new SpeedWeighting(carAvSpeedEnc); Path p = new Dijkstra(pathDetailGraph, weighting, TraversalMode.NODE_BASED).calcPath(1, 5); assertTrue(p.isFound()); Map> details = PathDetailsFromEdges.calcDetails(p, carManager, weighting, - Arrays.asList(AVERAGE_SPEED), new PathDetailsBuilderFactory(), 0, pathDetailGraph); - assertTrue(details.size() == 1); + List.of(AVERAGE_SPEED), new PathDetailsBuilderFactory(), 0, pathDetailGraph); + assertEquals(1, details.size()); List averageSpeedDetails = details.get(AVERAGE_SPEED); assertEquals(4, averageSpeedDetails.size()); - assertEquals(45.0, averageSpeedDetails.get(0).getValue()); - assertEquals(90.0, averageSpeedDetails.get(1).getValue()); - assertEquals(10.0, averageSpeedDetails.get(2).getValue()); - assertEquals(45.0, averageSpeedDetails.get(3).getValue()); + assertEquals(162.2, (double) averageSpeedDetails.get(0).getValue(), 1.e-3); + assertEquals(327.3, (double) averageSpeedDetails.get(1).getValue(), 1.e-3); + assertEquals(36.0, (double) averageSpeedDetails.get(2).getValue(), 1.e-3); + assertEquals(162.2, (double) averageSpeedDetails.get(3).getValue(), 1.e-3); assertEquals(0, averageSpeedDetails.get(0).getFirst()); assertEquals(1, averageSpeedDetails.get(1).getFirst()); @@ -305,12 +307,12 @@ public void testCalcAverageSpeedDetails() { @Test public void testCalcAverageSpeedDetailsWithShortDistances_issue1848() { - Weighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); + Weighting weighting = new SpeedWeighting(carAvSpeedEnc); Path p = new Dijkstra(pathDetailGraph, weighting, TraversalMode.NODE_BASED).calcPath(1, 6); assertTrue(p.isFound()); Map> details = PathDetailsFromEdges.calcDetails(p, carManager, weighting, - Arrays.asList(AVERAGE_SPEED), new PathDetailsBuilderFactory(), 0, pathDetailGraph); - assertTrue(details.size() == 1); + List.of(AVERAGE_SPEED), new PathDetailsBuilderFactory(), 0, pathDetailGraph); + assertEquals(1, details.size()); List averageSpeedDetails = details.get(AVERAGE_SPEED); assertEquals(4, averageSpeedDetails.size()); @@ -318,8 +320,8 @@ public void testCalcAverageSpeedDetailsWithShortDistances_issue1848() { p = new Dijkstra(pathDetailGraph, weighting, TraversalMode.NODE_BASED).calcPath(6, 1); assertTrue(p.isFound()); details = PathDetailsFromEdges.calcDetails(p, carManager, weighting, - Arrays.asList(AVERAGE_SPEED), new PathDetailsBuilderFactory(), 0, pathDetailGraph); - assertTrue(details.size() == 1); + List.of(AVERAGE_SPEED), new PathDetailsBuilderFactory(), 0, pathDetailGraph); + assertEquals(1, details.size()); averageSpeedDetails = details.get(AVERAGE_SPEED); assertEquals(5, averageSpeedDetails.size()); assertNull(averageSpeedDetails.get(0).getValue()); @@ -327,16 +329,16 @@ public void testCalcAverageSpeedDetailsWithShortDistances_issue1848() { @Test public void testCalcStreetNameDetails() { - Weighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); + Weighting weighting = new SpeedWeighting(carAvSpeedEnc); Path p = new Dijkstra(pathDetailGraph, weighting, TraversalMode.NODE_BASED).calcPath(1, 5); assertTrue(p.isFound()); Map> details = PathDetailsFromEdges.calcDetails(p, carManager, weighting, - Arrays.asList(STREET_NAME), new PathDetailsBuilderFactory(), 0, pathDetailGraph); - assertTrue(details.size() == 1); + List.of(STREET_NAME), new PathDetailsBuilderFactory(), 0, pathDetailGraph); + assertEquals(1, details.size()); List streetNameDetails = details.get(STREET_NAME); - assertTrue(details.size() == 1); + assertEquals(1, details.size()); assertEquals(4, streetNameDetails.size()); assertEquals("1-2", streetNameDetails.get(0).getValue()); @@ -353,13 +355,13 @@ public void testCalcStreetNameDetails() { @Test public void testCalcEdgeIdDetails() { - Weighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); + Weighting weighting = new SpeedWeighting(carAvSpeedEnc); Path p = new Dijkstra(pathDetailGraph, weighting, TraversalMode.NODE_BASED).calcPath(1, 5); assertTrue(p.isFound()); Map> details = PathDetailsFromEdges.calcDetails(p, carManager, weighting, - Arrays.asList(EDGE_ID), new PathDetailsBuilderFactory(), 0, pathDetailGraph); - assertTrue(details.size() == 1); + List.of(EDGE_ID), new PathDetailsBuilderFactory(), 0, pathDetailGraph); + assertEquals(1, details.size()); List edgeIdDetails = details.get(EDGE_ID); assertEquals(4, edgeIdDetails.size()); @@ -378,12 +380,12 @@ public void testCalcEdgeIdDetails() { @Test public void testCalcEdgeKeyDetailsForward() { - Weighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); + Weighting weighting = new SpeedWeighting(carAvSpeedEnc); Path p = new Dijkstra(pathDetailGraph, weighting, TraversalMode.NODE_BASED).calcPath(1, 5); assertTrue(p.isFound()); Map> details = PathDetailsFromEdges.calcDetails(p, carManager, weighting, - Arrays.asList(EDGE_KEY), new PathDetailsBuilderFactory(), 0, pathDetailGraph); + List.of(EDGE_KEY), new PathDetailsBuilderFactory(), 0, pathDetailGraph); List edgeKeyDetails = details.get(EDGE_KEY); assertEquals(4, edgeKeyDetails.size()); @@ -395,12 +397,12 @@ public void testCalcEdgeKeyDetailsForward() { @Test public void testCalcEdgeKeyDetailsBackward() { - Weighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); + Weighting weighting = new SpeedWeighting(carAvSpeedEnc); Path p = new Dijkstra(pathDetailGraph, weighting, TraversalMode.NODE_BASED).calcPath(5, 1); assertTrue(p.isFound()); Map> details = PathDetailsFromEdges.calcDetails(p, carManager, weighting, - Arrays.asList(EDGE_KEY), new PathDetailsBuilderFactory(), 0, pathDetailGraph); + List.of(EDGE_KEY), new PathDetailsBuilderFactory(), 0, pathDetailGraph); List edgeKeyDetails = details.get(EDGE_KEY); assertEquals(4, edgeKeyDetails.size()); @@ -412,20 +414,21 @@ public void testCalcEdgeKeyDetailsBackward() { @Test public void testCalcTimeDetails() { - Weighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); + Weighting weighting = new SpeedWeighting(carAvSpeedEnc); Path p = new Dijkstra(pathDetailGraph, weighting, TraversalMode.NODE_BASED).calcPath(1, 5); assertTrue(p.isFound()); + assertEquals(IntArrayList.from(1, 2, 3, 4, 5), p.calcNodes()); Map> details = PathDetailsFromEdges.calcDetails(p, carManager, weighting, - Arrays.asList(TIME), new PathDetailsBuilderFactory(), 0, pathDetailGraph); - assertTrue(details.size() == 1); + List.of(TIME), new PathDetailsBuilderFactory(), 0, pathDetailGraph); + assertEquals(1, details.size()); List timeDetails = details.get(TIME); assertEquals(4, timeDetails.size()); - assertEquals(400L, timeDetails.get(0).getValue()); - assertEquals(200L, timeDetails.get(1).getValue()); - assertEquals(3600L, timeDetails.get(2).getValue()); - assertEquals(400L, timeDetails.get(3).getValue()); + assertEquals(111L, timeDetails.get(0).getValue()); + assertEquals(55L, timeDetails.get(1).getValue()); + assertEquals(1000L, timeDetails.get(2).getValue()); + assertEquals(111L, timeDetails.get(3).getValue()); assertEquals(0, timeDetails.get(0).getFirst()); assertEquals(1, timeDetails.get(1).getFirst()); @@ -436,13 +439,13 @@ public void testCalcTimeDetails() { @Test public void testCalcDistanceDetails() { - Weighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); + Weighting weighting = new SpeedWeighting(carAvSpeedEnc); Path p = new Dijkstra(pathDetailGraph, weighting, TraversalMode.NODE_BASED).calcPath(1, 5); assertTrue(p.isFound()); Map> details = PathDetailsFromEdges.calcDetails(p, carManager, weighting, - Arrays.asList(DISTANCE), new PathDetailsBuilderFactory(), 0, pathDetailGraph); - assertTrue(details.size() == 1); + List.of(DISTANCE), new PathDetailsBuilderFactory(), 0, pathDetailGraph); + assertEquals(1, details.size()); List distanceDetails = details.get(DISTANCE); assertEquals(5D, distanceDetails.get(0).getValue()); @@ -453,29 +456,29 @@ public void testCalcDistanceDetails() { @Test public void testCalcIntersectionDetails() { - Weighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); + Weighting weighting = new SpeedWeighting(carAvSpeedEnc); Path p = new Dijkstra(pathDetailGraph, weighting, TraversalMode.NODE_BASED).calcPath(1, 5); assertTrue(p.isFound()); Map> details = PathDetailsFromEdges.calcDetails(p, carManager, weighting, - Arrays.asList(INTERSECTION), new PathDetailsBuilderFactory(), 0, pathDetailGraph); - assertTrue(details.size() == 1); + List.of(INTERSECTION), new PathDetailsBuilderFactory(), 0, pathDetailGraph); + assertEquals(1, details.size()); List intersectionDetails = details.get(INTERSECTION); assertEquals(4, intersectionDetails.size()); Map intersectionMap = new HashMap<>(); intersectionMap.put("out", 0); - intersectionMap.put("entries", Arrays.asList(true)); - intersectionMap.put("bearings", Arrays.asList(90)); + intersectionMap.put("entries", List.of(true)); + intersectionMap.put("bearings", List.of(90)); assertEquals(intersectionMap, intersectionDetails.get(0).getValue()); intersectionMap.clear(); intersectionMap.put("out", 0); intersectionMap.put("in", 1); - intersectionMap.put("entries", Arrays.asList(true, false)); - intersectionMap.put("bearings", Arrays.asList(90, 270)); + intersectionMap.put("entries", List.of(true, false)); + intersectionMap.put("bearings", List.of(90, 270)); assertEquals(intersectionMap, intersectionDetails.get(1).getValue()); } @@ -486,13 +489,13 @@ public void testCalcIntersectionDetails() { @Test public void testCalcInstructionsRoundabout2() { roundaboutGraph.inverse3to6(); - Weighting weighting = new ShortestWeighting(mixedCarAccessEnc, mixedCarSpeedEnc); + Weighting weighting = new SpeedWeighting(mixedCarSpeedEnc); Path p = new Dijkstra(roundaboutGraph.g, weighting, TraversalMode.NODE_BASED) .calcPath(1, 8); assertTrue(p.isFound()); InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncodingManager, tr); List tmpList = getTurnDescriptions(wayList); - assertEquals(Arrays.asList("continue onto MainStreet 1 2", + assertEquals(List.of("continue onto MainStreet 1 2", "At roundabout, take exit 2 onto 5-8", "arrive at destination"), tmpList); @@ -531,40 +534,40 @@ public void testCalcInstructionsRoundaboutIssue353() { na.setNode(10, 52.5135, 13.348); na.setNode(11, 52.514, 13.347); - GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, graph.edge(2, 1).setDistance(5)).setKeyValues(createKV(STREET_NAME, "MainStreet 2 1")); - GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, graph.edge(1, 11).setDistance(5)).setKeyValues(createKV(STREET_NAME, "MainStreet 1 11")); + graph.edge(2, 1).set(carAvSpeedEnc, 60, 0).setDistance(5).setKeyValues(createKV(STREET_NAME, "MainStreet 2 1")); + graph.edge(1, 11).set(carAvSpeedEnc, 60, 0).setDistance(5).setKeyValues(createKV(STREET_NAME, "MainStreet 1 11")); // roundabout EdgeIteratorState tmpEdge; - tmpEdge = GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, graph.edge(3, 9).setDistance(2)).setKeyValues(createKV(STREET_NAME, "3-9")); + tmpEdge = graph.edge(3, 9).set(carAvSpeedEnc, 60, 0).setDistance(2).setKeyValues(createKV(STREET_NAME, "3-9")); BooleanEncodedValue carManagerRoundabout = carManager.getBooleanEncodedValue(Roundabout.KEY); tmpEdge.set(carManagerRoundabout, true); - tmpEdge = GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, graph.edge(9, 10).setDistance(2)).setKeyValues(createKV(STREET_NAME, "9-10")); + tmpEdge = graph.edge(9, 10).set(carAvSpeedEnc, 60, 0).setDistance(2).setKeyValues(createKV(STREET_NAME, "9-10")); tmpEdge.set(carManagerRoundabout, true); - tmpEdge = GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, graph.edge(6, 10).setDistance(2)).setKeyValues(createKV(STREET_NAME, "6-10")); + tmpEdge = graph.edge(6, 10).set(carAvSpeedEnc, 60, 0).setDistance(2).setKeyValues(createKV(STREET_NAME, "6-10")); tmpEdge.set(carManagerRoundabout, true); - tmpEdge = GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, graph.edge(10, 1).setDistance(2)).setKeyValues(createKV(STREET_NAME, "10-1")); + tmpEdge = graph.edge(10, 1).set(carAvSpeedEnc, 60, 0).setDistance(2).setKeyValues(createKV(STREET_NAME, "10-1")); tmpEdge.set(carManagerRoundabout, true); - tmpEdge = GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, graph.edge(3, 2).setDistance(5)).setKeyValues(createKV(STREET_NAME, "2-3")); + tmpEdge = graph.edge(3, 2).set(carAvSpeedEnc, 60, 0).setDistance(5).setKeyValues(createKV(STREET_NAME, "2-3")); tmpEdge.set(carManagerRoundabout, true); - tmpEdge = GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, graph.edge(4, 3).setDistance(5)).setKeyValues(createKV(STREET_NAME, "3-4")); + tmpEdge = graph.edge(4, 3).set(carAvSpeedEnc, 60, 0).setDistance(5).setKeyValues(createKV(STREET_NAME, "3-4")); tmpEdge.set(carManagerRoundabout, true); - tmpEdge = GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, graph.edge(5, 4).setDistance(5)).setKeyValues(createKV(STREET_NAME, "4-5")); + tmpEdge = graph.edge(5, 4).set(carAvSpeedEnc, 60, 0).setDistance(5).setKeyValues(createKV(STREET_NAME, "4-5")); tmpEdge.set(carManagerRoundabout, true); - tmpEdge = GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, graph.edge(2, 5).setDistance(5)).setKeyValues(createKV(STREET_NAME, "5-2")); + tmpEdge = graph.edge(2, 5).set(carAvSpeedEnc, 60, 0).setDistance(5).setKeyValues(createKV(STREET_NAME, "5-2")); tmpEdge.set(carManagerRoundabout, true); - GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(4, 7).setDistance(5)).setKeyValues(createKV(STREET_NAME, "MainStreet 4 7")); - GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(5, 8).setDistance(5)).setKeyValues(createKV(STREET_NAME, "5-8")); - GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(3, 6).setDistance(5)).setKeyValues(createKV(STREET_NAME, "3-6")); + graph.edge(4, 7).set(carAvSpeedEnc, 60, 60).setDistance(5).setKeyValues(createKV(STREET_NAME, "MainStreet 4 7")); + graph.edge(5, 8).set(carAvSpeedEnc, 60, 60).setDistance(5).setKeyValues(createKV(STREET_NAME, "5-8")); + graph.edge(3, 6).set(carAvSpeedEnc, 60, 60).setDistance(5).setKeyValues(createKV(STREET_NAME, "3-6")); - Weighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); + Weighting weighting = new SpeedWeighting(carAvSpeedEnc); Path p = new Dijkstra(graph, weighting, TraversalMode.NODE_BASED) .calcPath(6, 11); assertTrue(p.isFound()); InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, carManager, tr); List tmpList = getTurnDescriptions(wayList); - assertEquals(Arrays.asList("At roundabout, take exit 1 onto MainStreet 1 11", + assertEquals(List.of("At roundabout, take exit 1 onto MainStreet 1 11", "arrive at destination"), tmpList); } @@ -572,13 +575,13 @@ public void testCalcInstructionsRoundaboutIssue353() { @Test public void testCalcInstructionsRoundaboutClockwise() { roundaboutGraph.setRoundabout(true); - Weighting weighting = new ShortestWeighting(mixedCarAccessEnc, mixedCarSpeedEnc); + Weighting weighting = new SpeedWeighting(mixedCarSpeedEnc); Path p = new Dijkstra(roundaboutGraph.g, weighting, TraversalMode.NODE_BASED) .calcPath(1, 8); assertTrue(p.isFound()); InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncodingManager, tr); List tmpList = getTurnDescriptions(wayList); - assertEquals(Arrays.asList("continue onto MainStreet 1 2", + assertEquals(List.of("continue onto MainStreet 1 2", "At roundabout, take exit 1 onto 5-8", "arrive at destination"), tmpList); @@ -591,7 +594,7 @@ public void testCalcInstructionsRoundaboutClockwise() { @Test public void testCalcInstructionsIgnoreContinue() { // Follow a couple of straight edges, including a name change - Weighting weighting = new ShortestWeighting(mixedCarAccessEnc, mixedCarSpeedEnc); + Weighting weighting = new SpeedWeighting(mixedCarSpeedEnc); Path p = new Dijkstra(roundaboutGraph.g, weighting, TraversalMode.NODE_BASED) .calcPath(4, 11); assertTrue(p.isFound()); @@ -604,7 +607,7 @@ public void testCalcInstructionsIgnoreContinue() { @Test public void testCalcInstructionsIgnoreTurnIfNoAlternative() { // The street turns left, but there is not turn - Weighting weighting = new ShortestWeighting(mixedCarAccessEnc, mixedCarSpeedEnc); + Weighting weighting = new SpeedWeighting(mixedCarSpeedEnc); Path p = new Dijkstra(roundaboutGraph.g, weighting, TraversalMode.NODE_BASED) .calcPath(10, 12); assertTrue(p.isFound()); @@ -632,11 +635,11 @@ public void testCalcInstructionForForkWithSameName() { na.setNode(3, 48.982611, 13.121012); na.setNode(4, 48.982336, 13.121002); - GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(1, 2).setDistance(5)).setKeyValues(createKV(STREET_NAME, "Regener Weg")); - GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(2, 4).setDistance(5)).setKeyValues(createKV(STREET_NAME, "Regener Weg")); - GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(2, 3).setDistance(5)); + graph.edge(1, 2).set(carAvSpeedEnc, 60, 60).setDistance(5).setKeyValues(createKV(STREET_NAME, "Regener Weg")); + graph.edge(2, 4).set(carAvSpeedEnc, 60, 60).setDistance(5).setKeyValues(createKV(STREET_NAME, "Regener Weg")); + graph.edge(2, 3).set(carAvSpeedEnc, 60, 60).setDistance(5); - Weighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); + Weighting weighting = new SpeedWeighting(carAvSpeedEnc); Path p = new Dijkstra(graph, weighting, TraversalMode.NODE_BASED) .calcPath(1, 4); assertTrue(p.isFound()); @@ -665,11 +668,11 @@ public void testCalcInstructionForMotorwayFork() { EnumEncodedValue roadClassEnc = carManager.getEnumEncodedValue(RoadClass.KEY, RoadClass.class); BooleanEncodedValue roadClassLinkEnc = carManager.getBooleanEncodedValue(RoadClassLink.KEY); - GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(1, 2).setDistance(5)).setKeyValues(createKV(STREET_NAME, "A 8")).set(roadClassEnc, RoadClass.MOTORWAY).set(roadClassLinkEnc, false); - GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(2, 4).setDistance(5)).setKeyValues(createKV(STREET_NAME, "A 8")).set(roadClassEnc, RoadClass.MOTORWAY).set(roadClassLinkEnc, false); - GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(2, 3).setDistance(5)).set(roadClassEnc, RoadClass.MOTORWAY).set(roadClassLinkEnc, true); + graph.edge(1, 2).set(carAvSpeedEnc, 60, 60).setDistance(5).setKeyValues(createKV(STREET_NAME, "A 8")).set(roadClassEnc, RoadClass.MOTORWAY).set(roadClassLinkEnc, false); + graph.edge(2, 4).set(carAvSpeedEnc, 60, 60).setDistance(5).setKeyValues(createKV(STREET_NAME, "A 8")).set(roadClassEnc, RoadClass.MOTORWAY).set(roadClassLinkEnc, false); + graph.edge(2, 3).set(carAvSpeedEnc, 60, 60).setDistance(5).set(roadClassEnc, RoadClass.MOTORWAY).set(roadClassLinkEnc, true); - Weighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); + Weighting weighting = new SpeedWeighting(carAvSpeedEnc); Path p = new Dijkstra(graph, weighting, TraversalMode.NODE_BASED) .calcPath(1, 4); assertTrue(p.isFound()); @@ -693,11 +696,11 @@ public void testCalcInstructionsEnterMotorway() { na.setNode(3, 48.630558, 9.459851); na.setNode(4, 48.63054, 9.459406); - GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, graph.edge(1, 2).setDistance(5)).setKeyValues(createKV(STREET_NAME, "A 8")); - GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, graph.edge(2, 3).setDistance(5)).setKeyValues(createKV(STREET_NAME, "A 8")); - GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, graph.edge(4, 2).setDistance(5)).setKeyValues(createKV(STREET_NAME, "A 8")); + graph.edge(1, 2).set(carAvSpeedEnc, 60, 0).setDistance(5).setKeyValues(createKV(STREET_NAME, "A 8")); + graph.edge(2, 3).set(carAvSpeedEnc, 60, 0).setDistance(5).setKeyValues(createKV(STREET_NAME, "A 8")); + graph.edge(4, 2).set(carAvSpeedEnc, 60, 0).setDistance(5).setKeyValues(createKV(STREET_NAME, "A 8")); - Weighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); + Weighting weighting = new SpeedWeighting(carAvSpeedEnc); Path p = new Dijkstra(graph, weighting, TraversalMode.NODE_BASED) .calcPath(4, 3); assertTrue(p.isFound()); @@ -722,11 +725,11 @@ public void testCalcInstructionsMotorwayJunction() { na.setNode(3, 48.706805, 9.162995); na.setNode(4, 48.706705, 9.16329); - GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, g.edge(1, 2).setDistance(5)).setKeyValues(createKV(STREET_NAME, "A 8")); - GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, g.edge(2, 3).setDistance(5)).setKeyValues(createKV(STREET_NAME, "A 8")); - GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, g.edge(2, 4).setDistance(5)).setKeyValues(createKV(STREET_NAME, "A 8")); + g.edge(1, 2).setDistance(5).set(carAvSpeedEnc, 60, 0).setKeyValues(createKV(STREET_NAME, "A 8")); + g.edge(2, 3).setDistance(5).set(carAvSpeedEnc, 60, 0).setKeyValues(createKV(STREET_NAME, "A 8")); + g.edge(2, 4).setDistance(5).set(carAvSpeedEnc, 60, 0).setKeyValues(createKV(STREET_NAME, "A 8")); - Weighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); + Weighting weighting = new SpeedWeighting(carAvSpeedEnc); Path p = new Dijkstra(g, weighting, TraversalMode.NODE_BASED) .calcPath(1, 3); assertTrue(p.isFound()); @@ -752,11 +755,11 @@ public void testCalcInstructionsOntoOneway() { na.setNode(3, -33.824415, 151.188177); na.setNode(4, -33.824437, 151.187925); - GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, g.edge(1, 2).setDistance(5)).setKeyValues(createKV(STREET_NAME, "Pacific Highway")); - GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, g.edge(2, 3).setDistance(5)).setKeyValues(createKV(STREET_NAME, "Pacific Highway")); - GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, g.edge(4, 2).setDistance(5)).setKeyValues(createKV(STREET_NAME, "Greenwich Road")); + g.edge(1, 2).setDistance(5).set(carAvSpeedEnc, 60, 0).setKeyValues(createKV(STREET_NAME, "Pacific Highway")); + g.edge(2, 3).setDistance(5).set(carAvSpeedEnc, 60, 0).setKeyValues(createKV(STREET_NAME, "Pacific Highway")); + g.edge(4, 2).setDistance(5).set(carAvSpeedEnc, 60, 60).setKeyValues(createKV(STREET_NAME, "Greenwich Road")); - Weighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); + Weighting weighting = new SpeedWeighting(carAvSpeedEnc); Path p = new Dijkstra(g, weighting, TraversalMode.NODE_BASED) .calcPath(4, 3); assertTrue(p.isFound()); @@ -787,11 +790,11 @@ public void testCalcInstructionIssue1047() { EnumEncodedValue roadClassEnc = carManager.getEnumEncodedValue(RoadClass.KEY, RoadClass.class); BooleanEncodedValue roadClassLinkEnc = carManager.getBooleanEncodedValue(RoadClassLink.KEY); - GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, g.edge(1, 2).setDistance(5)).setKeyValues(createKV(STREET_NAME, "B 156")).set(roadClassEnc, RoadClass.PRIMARY).set(roadClassLinkEnc, false); - GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, g.edge(2, 4).setDistance(5)).setKeyValues(createKV(STREET_NAME, "S 108")).set(roadClassEnc, RoadClass.SECONDARY).set(roadClassLinkEnc, false); - GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, g.edge(2, 3).setDistance(5)).setKeyValues(createKV(STREET_NAME, "B 156")).set(roadClassEnc, RoadClass.PRIMARY).set(roadClassLinkEnc, false); + g.edge(1, 2).set(carAvSpeedEnc, 60, 60).setDistance(5).setKeyValues(createKV(STREET_NAME, "B 156")).set(roadClassEnc, RoadClass.PRIMARY).set(roadClassLinkEnc, false); + g.edge(2, 4).set(carAvSpeedEnc, 60, 60).setDistance(5).setKeyValues(createKV(STREET_NAME, "S 108")).set(roadClassEnc, RoadClass.SECONDARY).set(roadClassLinkEnc, false); + g.edge(2, 3).set(carAvSpeedEnc, 60, 60).setDistance(5).setKeyValues(createKV(STREET_NAME, "B 156")).set(roadClassEnc, RoadClass.PRIMARY).set(roadClassLinkEnc, false); - Weighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); + Weighting weighting = new SpeedWeighting(carAvSpeedEnc); Path p = new Dijkstra(g, weighting, TraversalMode.NODE_BASED) .calcPath(1, 4); assertTrue(p.isFound()); @@ -821,11 +824,11 @@ public void testCalcInstructionContinueLeavingStreet() { na.setNode(3, 48.982611, 13.121012); na.setNode(4, 48.982565, 13.121002); - GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, g.edge(1, 2).setDistance(5)).setKeyValues(createKV(STREET_NAME, "Regener Weg")); - GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, g.edge(2, 4).setDistance(5)); - GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, g.edge(2, 3).setDistance(5)).setKeyValues(createKV(STREET_NAME, "Regener Weg")); + g.edge(1, 2).set(carAvSpeedEnc, 60, 60).setDistance(5).setKeyValues(createKV(STREET_NAME, "Regener Weg")); + g.edge(2, 4).set(carAvSpeedEnc, 60, 60).setDistance(5); + g.edge(2, 3).set(carAvSpeedEnc, 60, 60).setDistance(5).setKeyValues(createKV(STREET_NAME, "Regener Weg")); - Weighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); + Weighting weighting = new SpeedWeighting(carAvSpeedEnc); Path p = new Dijkstra(g, weighting, TraversalMode.NODE_BASED) .calcPath(1, 4); assertTrue(p.isFound()); @@ -851,11 +854,11 @@ public void testCalcInstructionSlightTurn() { na.setNode(3, 48.412034, 15.599411); na.setNode(4, 48.411927, 15.599197); - GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, g.edge(1, 2).setDistance(5)).setKeyValues(createKV(STREET_NAME, "Stöhrgasse")); - GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, g.edge(2, 3).setDistance(5)); - GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, g.edge(2, 4).setDistance(5)).setKeyValues(createKV(STREET_NAME, "Stöhrgasse")); + g.edge(1, 2).set(carAvSpeedEnc, 60, 60).setDistance(5).setKeyValues(createKV(STREET_NAME, "Stöhrgasse")); + g.edge(2, 3).set(carAvSpeedEnc, 60, 60).setDistance(5); + g.edge(2, 4).set(carAvSpeedEnc, 60, 60).setDistance(5).setKeyValues(createKV(STREET_NAME, "Stöhrgasse")); - Weighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); + Weighting weighting = new SpeedWeighting(carAvSpeedEnc); Path p = new Dijkstra(g, weighting, TraversalMode.NODE_BASED) .calcPath(4, 1); assertTrue(p.isFound()); @@ -884,16 +887,14 @@ public void testUTurnLeft() { na.setNode(6, 48.402422, 9.996067); na.setNode(7, 48.402604, 9.994962); - GHUtility.setSpeed(60, 0, carAccessEnc, carAvSpeedEnc, - g.edge(1, 2).setDistance(5).setKeyValues(createKV(STREET_NAME, "Olgastraße")), - g.edge(2, 3).setDistance(5).setKeyValues(createKV(STREET_NAME, "Olgastraße")), - g.edge(6, 5).setDistance(5).setKeyValues(createKV(STREET_NAME, "Olgastraße")), - g.edge(5, 4).setDistance(5).setKeyValues(createKV(STREET_NAME, "Olgastraße"))); - GHUtility.setSpeed(60, 60, carAccessEnc, carAvSpeedEnc, - g.edge(2, 5).setDistance(5).setKeyValues(createKV(STREET_NAME, "Neithardtstraße")), - g.edge(5, 7).setDistance(5).setKeyValues(createKV(STREET_NAME, "Neithardtstraße"))); + g.edge(1, 2).set(carAvSpeedEnc, 60, 0).setDistance(5).setKeyValues(createKV(STREET_NAME, "Olgastraße")); + g.edge(2, 3).set(carAvSpeedEnc, 60, 0).setDistance(5).setKeyValues(createKV(STREET_NAME, "Olgastraße")); + g.edge(6, 5).set(carAvSpeedEnc, 60, 0).setDistance(5).setKeyValues(createKV(STREET_NAME, "Olgastraße")); + g.edge(5, 4).set(carAvSpeedEnc, 60, 0).setDistance(5).setKeyValues(createKV(STREET_NAME, "Olgastraße")); + g.edge(2, 5).set(carAvSpeedEnc, 60, 60).setDistance(5).setKeyValues(createKV(STREET_NAME, "Neithardtstraße")); + g.edge(5, 7).set(carAvSpeedEnc, 60, 60).setDistance(5).setKeyValues(createKV(STREET_NAME, "Neithardtstraße")); - Weighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); + Weighting weighting = new SpeedWeighting(carAvSpeedEnc); Path p = new Dijkstra(g, weighting, TraversalMode.NODE_BASED) .calcPath(1, 4); assertTrue(p.isFound()); @@ -922,21 +923,22 @@ public void testUTurnRight() { na.setNode(6, -33.885692, 151.181445); na.setNode(7, -33.885692, 151.181445); - GHUtility.setSpeed(60, 0, carAccessEnc, carAvSpeedEnc, - g.edge(1, 2).setDistance(5).setKeyValues(createKV(STREET_NAME, "Parramatta Road")), - g.edge(2, 3).setDistance(5).setKeyValues(createKV(STREET_NAME, "Parramatta Road")), - g.edge(4, 5).setDistance(5).setKeyValues(createKV(STREET_NAME, "Parramatta Road")), - g.edge(5, 6).setDistance(5).setKeyValues(createKV(STREET_NAME, "Parramatta Road"))); - GHUtility.setSpeed(60, 60, carAccessEnc, carAvSpeedEnc, - g.edge(2, 5).setDistance(5).setKeyValues(createKV(STREET_NAME, "Larkin Street")), - g.edge(5, 7).setDistance(5).setKeyValues(createKV(STREET_NAME, "Larkin Street"))); + g.edge(1, 2).set(carAvSpeedEnc, 60, 0).setDistance(5).setKeyValues(createKV(STREET_NAME, "Parramatta Road")); + g.edge(2, 3).set(carAvSpeedEnc, 60, 0).setDistance(5).setKeyValues(createKV(STREET_NAME, "Parramatta Road")); + g.edge(4, 5).set(carAvSpeedEnc, 60, 0).setDistance(5).setKeyValues(createKV(STREET_NAME, "Parramatta Road")); + g.edge(5, 6).set(carAvSpeedEnc, 60, 0).setDistance(5).setKeyValues(createKV(STREET_NAME, "Parramatta Road")); + g.edge(2, 5).set(carAvSpeedEnc, 60, 60).setDistance(5).setKeyValues(createKV(STREET_NAME, "Larkin Street")); + g.edge(5, 7).set(carAvSpeedEnc, 60, 60).setDistance(5).setKeyValues(createKV(STREET_NAME, "Larkin Street")); - Weighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); + Weighting weighting = new SpeedWeighting(carAvSpeedEnc); Path p = new Dijkstra(g, weighting, TraversalMode.NODE_BASED) .calcPath(1, 6); assertTrue(p.isFound()); + assertEquals(IntArrayList.from(1, 2, 5, 6), p.calcNodes()); InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, carManager, tr); + assertEquals(List.of("continue onto Parramatta Road", "make a U-turn onto Parramatta Road", "arrive at destination"), + getTurnDescriptions(wayList)); assertEquals(3, wayList.size()); assertEquals(Instruction.U_TURN_RIGHT, wayList.get(1).getSign()); } @@ -944,7 +946,7 @@ public void testUTurnRight() { @Test public void testCalcInstructionsForTurn() { // The street turns left, but there is not turn - Weighting weighting = new ShortestWeighting(mixedCarAccessEnc, mixedCarSpeedEnc); + Weighting weighting = new SpeedWeighting(mixedCarSpeedEnc); Path p = new Dijkstra(roundaboutGraph.g, weighting, TraversalMode.NODE_BASED) .calcPath(11, 13); assertTrue(p.isFound()); @@ -959,7 +961,7 @@ public void testCalcInstructionsForTurn() { @Test public void testCalcInstructionsForSlightTurnWithOtherSlightTurn() { // Test for a fork with two slight turns. Since there are two slight turns, show the turn instruction - Weighting weighting = new ShortestWeighting(mixedCarAccessEnc, mixedCarSpeedEnc); + Weighting weighting = new SpeedWeighting(mixedCarSpeedEnc); Path p = new Dijkstra(roundaboutGraph.g, weighting, TraversalMode.NODE_BASED) .calcPath(12, 16); assertTrue(p.isFound()); @@ -986,11 +988,11 @@ public void testCalcInstructionsForSlightTurnOntoDifferentStreet() { na.setNode(3, 48.764149, 8.678926); na.setNode(4, 48.764085, 8.679183); - GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, g.edge(1, 3).setDistance(5)).setKeyValues(createKV(STREET_NAME, "Talstraße, K 4313")); - GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, g.edge(2, 3).setDistance(5)).setKeyValues(createKV(STREET_NAME, "Calmbacher Straße, K 4312")); - GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, g.edge(3, 4).setDistance(5)).setKeyValues(createKV(STREET_NAME, "Calmbacher Straße, K 4312")); + g.edge(1, 3).setDistance(5).set(carAvSpeedEnc, 60, 60).setKeyValues(createKV(STREET_NAME, "Talstraße, K 4313")); + g.edge(2, 3).setDistance(5).set(carAvSpeedEnc, 60, 60).setKeyValues(createKV(STREET_NAME, "Calmbacher Straße, K 4312")); + g.edge(3, 4).setDistance(5).set(carAvSpeedEnc, 60, 60).setKeyValues(createKV(STREET_NAME, "Calmbacher Straße, K 4312")); - Weighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); + Weighting weighting = new SpeedWeighting(carAvSpeedEnc); Path p = new Dijkstra(g, weighting, TraversalMode.NODE_BASED) .calcPath(1, 2); assertTrue(p.isFound()); @@ -1002,8 +1004,8 @@ public void testCalcInstructionsForSlightTurnOntoDifferentStreet() { @Test public void testIgnoreInstructionsForSlightTurnWithOtherTurn() { - // Test for a fork with one sligh turn and one actual turn. We are going along the slight turn. No turn instruction needed in this case - Weighting weighting = new ShortestWeighting(mixedCarAccessEnc, mixedCarSpeedEnc); + // Test for a fork with one slight turn and one actual turn. We are going along the slight turn. No turn instruction needed in this case + Weighting weighting = new SpeedWeighting(mixedCarSpeedEnc); Path p = new Dijkstra(roundaboutGraph.g, weighting, TraversalMode.NODE_BASED) .calcPath(16, 19); assertTrue(p.isFound()); @@ -1032,11 +1034,11 @@ private Graph generatePathDetailsGraph() { na.setNode(5, 52.516, 13.3452); na.setNode(6, 52.516, 13.344); - GHUtility.setSpeed(45, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(1, 2).setDistance(5)).setKeyValues(createKV(STREET_NAME, "1-2")); - GHUtility.setSpeed(45, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(4, 5).setDistance(5)).setKeyValues(createKV(STREET_NAME, "4-5")); - GHUtility.setSpeed(90, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(2, 3).setDistance(5)).setKeyValues(createKV(STREET_NAME, "2-3")); - GHUtility.setSpeed(9, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(3, 4).setDistance(10)).setKeyValues(createKV(STREET_NAME, "3-4")); - GHUtility.setSpeed(9, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(5, 6).setDistance(0.001)).setKeyValues(createKV(STREET_NAME, "3-4")); + graph.edge(1, 2).set(carAvSpeedEnc, 45, 45).setDistance(5).setKeyValues(createKV(STREET_NAME, "1-2")); + graph.edge(4, 5).set(carAvSpeedEnc, 45, 45).setDistance(5).setKeyValues(createKV(STREET_NAME, "4-5")); + graph.edge(2, 3).set(carAvSpeedEnc, 90, 90).setDistance(5).setKeyValues(createKV(STREET_NAME, "2-3")); + graph.edge(3, 4).set(carAvSpeedEnc, 9, 9).setDistance(10).setKeyValues(createKV(STREET_NAME, "3-4")); + graph.edge(5, 6).set(carAvSpeedEnc, 9, 9).setDistance(0.001).setKeyValues(createKV(STREET_NAME, "3-4")); return graph; } @@ -1109,12 +1111,12 @@ private RoundaboutGraph() { bothDir.add(g.edge(17, 19).setDistance(5)); for (EdgeIteratorState edge : bothDir) { - GHUtility.setSpeed(70, 70, mixedCarAccessEnc, mixedCarSpeedEnc, edge); - GHUtility.setSpeed(7, 7, mixedFootAccessEnc, mixedFootSpeedEnc, edge); + edge.set(mixedCarSpeedEnc, 70, 70); + edge.set(mixedFootSpeedEnc, 7, 7); } for (EdgeIteratorState edge : oneDir) { - GHUtility.setSpeed(70, 0, mixedCarAccessEnc, mixedCarSpeedEnc, edge); - GHUtility.setSpeed(7, 0, mixedFootAccessEnc, mixedFootSpeedEnc, edge); + edge.set(mixedCarSpeedEnc, 70, 0); + edge.set(mixedFootSpeedEnc, 7, 0); } setRoundabout(clockwise); inverse3to9(); @@ -1123,21 +1125,21 @@ private RoundaboutGraph() { public void setRoundabout(boolean clockwise) { BooleanEncodedValue mixedRoundabout = mixedEncodingManager.getBooleanEncodedValue(Roundabout.KEY); for (EdgeIteratorState edge : roundaboutEdges) { - edge.set(mixedCarAccessEnc, clockwise).setReverse(mixedCarAccessEnc, !clockwise); - edge.set(mixedFootAccessEnc, clockwise).setReverse(mixedFootAccessEnc, !clockwise); + edge.set(mixedCarSpeedEnc, clockwise ? 70 : 0, clockwise ? 0 : 70); + edge.set(mixedFootSpeedEnc, clockwise ? 7 : 0, clockwise ? 0 : 7); edge.set(mixedRoundabout, true); } this.clockwise = clockwise; } public void inverse3to9() { - edge3to9.set(mixedCarAccessEnc, !edge3to9.get(mixedCarAccessEnc)).setReverse(mixedCarAccessEnc, false); - edge3to9.set(mixedFootAccessEnc, !edge3to9.get(mixedFootAccessEnc)).setReverse(mixedFootAccessEnc, false); + edge3to9.set(mixedCarSpeedEnc, edge3to9.get(mixedCarSpeedEnc) > 0 ? 0 : 70, 0); + edge3to9.set(mixedFootSpeedEnc, edge3to9.get(mixedFootSpeedEnc) > 0 ? 0 : 7, 0); } public void inverse3to6() { - edge3to6.set(mixedCarAccessEnc, !edge3to6.get(mixedCarAccessEnc)).setReverse(mixedCarAccessEnc, true); - edge3to6.set(mixedFootAccessEnc, !edge3to6.get(mixedFootAccessEnc)).setReverse(mixedFootAccessEnc, true); + edge3to6.set(mixedCarSpeedEnc, edge3to6.get(mixedCarSpeedEnc) > 0 ? 0 : 70, 70); + edge3to6.set(mixedFootSpeedEnc, edge3to6.get(mixedFootSpeedEnc) > 0 ? 0 : 7, 7); } private double getAngle(int n1, int n2, int n3, int n4) { diff --git a/core/src/test/java/com/graphhopper/routing/PriorityRoutingTest.java b/core/src/test/java/com/graphhopper/routing/PriorityRoutingTest.java index 5f1c519173d..ff8907f01fe 100644 --- a/core/src/test/java/com/graphhopper/routing/PriorityRoutingTest.java +++ b/core/src/test/java/com/graphhopper/routing/PriorityRoutingTest.java @@ -40,10 +40,9 @@ public class PriorityRoutingTest { @Test void testMaxPriority() { - BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", false); DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 4, 2, false); DecimalEncodedValue priorityEnc = new DecimalEncodedValueImpl("priority", 4, PriorityCode.getFactor(1), false); - EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).add(priorityEnc).build(); + EncodingManager em = EncodingManager.start().add(speedEnc).add(priorityEnc).add(RoadClass.create()).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); NodeAccess na = graph.getNodeAccess(); na.setNode(0, 48.0, 11.0); @@ -56,15 +55,15 @@ void testMaxPriority() { // \- 4 - 5 -/ double speed = speedEnc.getNextStorableValue(30); double dist1 = 0; - dist1 += addEdge(em, graph, 0, 1, 1.0, accessEnc, speedEnc, priorityEnc, speed).getDistance(); - dist1 += addEdge(em, graph, 1, 2, 1.0, accessEnc, speedEnc, priorityEnc, speed).getDistance(); - dist1 += addEdge(em, graph, 2, 3, 1.0, accessEnc, speedEnc, priorityEnc, speed).getDistance(); + dist1 += addEdge(em, graph, 0, 1, 1.0, speedEnc, priorityEnc, speed).getDistance(); + dist1 += addEdge(em, graph, 1, 2, 1.0, speedEnc, priorityEnc, speed).getDistance(); + dist1 += addEdge(em, graph, 2, 3, 1.0, speedEnc, priorityEnc, speed).getDistance(); final double maxPrio = PriorityCode.getFactor(PriorityCode.BEST.getValue()); double dist2 = 0; - dist2 += addEdge(em, graph, 0, 4, maxPrio, accessEnc, speedEnc, priorityEnc, speed).getDistance(); - dist2 += addEdge(em, graph, 4, 5, maxPrio, accessEnc, speedEnc, priorityEnc, speed).getDistance(); - dist2 += addEdge(em, graph, 5, 3, maxPrio, accessEnc, speedEnc, priorityEnc, speed).getDistance(); + dist2 += addEdge(em, graph, 0, 4, maxPrio, speedEnc, priorityEnc, speed).getDistance(); + dist2 += addEdge(em, graph, 4, 5, maxPrio, speedEnc, priorityEnc, speed).getDistance(); + dist2 += addEdge(em, graph, 5, 3, maxPrio, speedEnc, priorityEnc, speed).getDistance(); // the routes 0-1-2-3 and 0-4-5-3 have similar distances (and use max speed everywhere) // ... but the shorter route 0-1-2-3 has smaller priority @@ -74,9 +73,10 @@ void testMaxPriority() { // A* and Dijkstra should yield the same path (the max priority must be taken into account by weighting.getMinWeight) { CustomModel customModel = new CustomModel(); - CustomWeighting weighting = CustomModelParser.createWeighting(accessEnc, - speedEnc, priorityEnc, em, - TurnCostProvider.NO_TURN_COST_PROVIDER, customModel); + customModel.addToPriority(Statement.If("true", Statement.Op.MULTIPLY, priorityEnc.getName())); + customModel.addToSpeed(Statement.If("true", Statement.Op.LIMIT, speedEnc.getName())); + + CustomWeighting weighting = CustomModelParser.createWeighting(em, TurnCostProvider.NO_TURN_COST_PROVIDER, customModel); Path pathDijkstra = new Dijkstra(graph, weighting, TraversalMode.NODE_BASED).calcPath(0, 3); Path pathAStar = new AStar(graph, weighting, TraversalMode.NODE_BASED).calcPath(0, 3); assertEquals(pathDijkstra.calcNodes(), pathAStar.calcNodes()); @@ -86,9 +86,10 @@ void testMaxPriority() { { CustomModel customModel = new CustomModel(); // now we even increase the priority in the custom model, which also needs to be accounted for in weighting.getMinWeight + customModel.addToPriority(Statement.If("true", Statement.Op.MULTIPLY, priorityEnc.getName())); customModel.addToPriority(Statement.If("road_class == MOTORWAY", Statement.Op.MULTIPLY, "3")); - CustomWeighting weighting = CustomModelParser.createWeighting(accessEnc, speedEnc, - priorityEnc, em, TurnCostProvider.NO_TURN_COST_PROVIDER, customModel); + customModel.addToSpeed(Statement.If("true", Statement.Op.LIMIT, speedEnc.getName())); + CustomWeighting weighting = CustomModelParser.createWeighting(em, TurnCostProvider.NO_TURN_COST_PROVIDER, customModel); Path pathDijkstra = new Dijkstra(graph, weighting, TraversalMode.NODE_BASED).calcPath(0, 3); Path pathAStar = new AStar(graph, weighting, TraversalMode.NODE_BASED).calcPath(0, 3); assertEquals(pathDijkstra.calcNodes(), pathAStar.calcNodes()); @@ -96,10 +97,9 @@ void testMaxPriority() { } } - private EdgeIteratorState addEdge(EncodingManager em, BaseGraph graph, int p, int q, double prio, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc, DecimalEncodedValue priorityEnc, double speed) { + private EdgeIteratorState addEdge(EncodingManager em, BaseGraph graph, int p, int q, double prio, DecimalEncodedValue speedEnc, DecimalEncodedValue priorityEnc, double speed) { EnumEncodedValue roadClassEnc = em.getEnumEncodedValue(RoadClass.KEY, RoadClass.class); return graph.edge(p, q) - .set(accessEnc, true) .set(speedEnc, speed) .set(priorityEnc, prio) .set(roadClassEnc, RoadClass.MOTORWAY) diff --git a/core/src/test/java/com/graphhopper/routing/RandomCHRoutingTest.java b/core/src/test/java/com/graphhopper/routing/RandomCHRoutingTest.java index b05918482b4..97b7ea92228 100644 --- a/core/src/test/java/com/graphhopper/routing/RandomCHRoutingTest.java +++ b/core/src/test/java/com/graphhopper/routing/RandomCHRoutingTest.java @@ -169,7 +169,7 @@ private void runRandomTest(Fixture f, Random rnd) { } if (strictViolations.size() > 0.05 * numQueries) { fail("Too many strict violations: " + strictViolations.size() + "/" + numQueries + "\n" + - Helper.join("\n", strictViolations)); + String.join("\n", strictViolations)); } } } diff --git a/core/src/test/java/com/graphhopper/routing/RandomizedRoutingTest.java b/core/src/test/java/com/graphhopper/routing/RandomizedRoutingTest.java index 70ca4ea8322..8398b4d90f7 100644 --- a/core/src/test/java/com/graphhopper/routing/RandomizedRoutingTest.java +++ b/core/src/test/java/com/graphhopper/routing/RandomizedRoutingTest.java @@ -20,10 +20,7 @@ import com.graphhopper.routing.ch.CHRoutingAlgorithmFactory; import com.graphhopper.routing.ch.PrepareContractionHierarchies; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValueImpl; -import com.graphhopper.routing.ev.Subnetwork; -import com.graphhopper.routing.ev.TurnCost; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.lm.*; import com.graphhopper.routing.querygraph.QueryGraph; import com.graphhopper.routing.querygraph.QueryRoutingCHGraph; diff --git a/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmTest.java b/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmTest.java index da3bda8c8ed..f9a0ef429bf 100644 --- a/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmTest.java +++ b/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmTest.java @@ -906,8 +906,8 @@ public void testTwoWeightsPerEdge2(Fixture f) { private final Weighting tmpW = new SpeedWeighting(f.carSpeedEnc); @Override - public double getMinWeight(double distance) { - return 0.8 * distance; + public double calcMinWeightPerDistance() { + return 0.8; } @Override diff --git a/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmWithOSMTest.java b/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmWithOSMTest.java index f0b217bfc15..38c85a88c65 100644 --- a/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmWithOSMTest.java +++ b/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmWithOSMTest.java @@ -24,7 +24,7 @@ import com.graphhopper.config.CHProfile; import com.graphhopper.config.LMProfile; import com.graphhopper.config.Profile; -import com.graphhopper.jackson.Jackson; +import com.graphhopper.config.TurnCostsConfig; import com.graphhopper.reader.dem.SRTMProvider; import com.graphhopper.storage.Graph; import com.graphhopper.util.*; @@ -34,7 +34,6 @@ import org.junit.jupiter.api.Test; import java.io.File; -import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -73,7 +72,9 @@ public void setup() { @Test public void testMonaco() { - GraphHopper hopper = createHopper(MONACO, new Profile("car").setCustomModel(new CustomModel().setDistanceInfluence(10_000d)).setVehicle("car")); + Profile profile = TestProfiles.accessAndSpeed("car"); + profile.getCustomModel().setDistanceInfluence(10_000d); + GraphHopper hopper = createHopper(MONACO, profile); hopper.importOrLoad(); checkQueries(hopper, createMonacoCarQueries()); Graph g = hopper.getBaseGraph(); @@ -110,15 +111,14 @@ private List createMonacoCarQueries() { public void testMonacoMotorcycleCurvature() { List queries = new ArrayList<>(); queries.add(new Query(43.730729, 7.42135, 43.727697, 7.419199, 2675, 117)); - queries.add(new Query(43.727687, 7.418737, 43.74958, 7.436566, 3730, 173)); + queries.add(new Query(43.727687, 7.418737, 43.74958, 7.436566, 3727, 170)); queries.add(new Query(43.728677, 7.41016, 43.739213, 7.4277, 2769, 167)); queries.add(new Query(43.733802, 7.413433, 43.739662, 7.424355, 2373, 137)); queries.add(new Query(43.730949, 7.412338, 43.739643, 7.424542, 2203, 116)); queries.add(new Query(43.727592, 7.419333, 43.727712, 7.419333, 0, 1)); GraphHopper hopper = createHopper(MONACO, new Profile("car").setCustomModel( - CustomModel.merge(getCustomModel("motorcycle.json"), getCustomModel("curvature.json"))).setVehicle("roads")); - hopper.setVehiclesString("car,roads"); - hopper.setEncodedValuesString("curvature,track_type,surface"); + CustomModel.merge(getCustomModel("motorcycle.json"), getCustomModel("curvature.json")))); + hopper.setEncodedValuesString("curvature,track_type,surface,road_access, road_class, car_average_speed, car_access"); hopper.setElevationProvider(new SRTMProvider(DIR)); hopper.importOrLoad(); checkQueries(hopper, queries); @@ -131,7 +131,7 @@ public void testBike2_issue432() { // reverse route avoids the location // list.add(new OneRun(52.349713, 8.013293, 52.349969, 8.013813, 293, 21)); GraphHopper hopper = createHopper(DIR + "/map-bug432.osm.gz", - new Profile("bike2").setVehicle("bike")); + TestProfiles.accessSpeedAndPriority("bike")); hopper.setElevationProvider(new SRTMProvider(DIR)); hopper.importOrLoad(); checkQueries(hopper, queries); @@ -147,7 +147,7 @@ public void testOneWayCircleBug() { queries.add(new Query(51.376509, -0.530863, 51.376197, -0.531576, 75, 15)); GraphHopper hopper = createHopper(DIR + "/circle-bug.osm.gz", - new Profile("car").setVehicle("car")); + TestProfiles.accessAndSpeed("car")); hopper.importOrLoad(); checkQueries(hopper, queries); } @@ -165,7 +165,7 @@ public void testMoscow() { // respect one way! // http://localhost:8989/?point=55.819066%2C37.596374&point=55.818898%2C37.59661 queries.add(new Query(55.818702, 37.595564, 55.818536, 37.595848, 1114, 23)); - GraphHopper hopper = createHopper(MOSCOW, new Profile("car").setVehicle("car")); + GraphHopper hopper = createHopper(MOSCOW, TestProfiles.accessAndSpeed("car")); hopper.setMinNetworkSize(200); hopper.importOrLoad(); checkQueries(hopper, queries); @@ -176,8 +176,7 @@ public void testMoscowTurnCosts() { List queries = new ArrayList<>(); queries.add(new Query(55.813357, 37.5958585, 55.811042, 37.594689, 1043.99, 12)); queries.add(new Query(55.813159, 37.593884, 55.811278, 37.594217, 1048, 13)); - GraphHopper hopper = createHopper(MOSCOW, - new Profile("car").setVehicle("car").setTurnCosts(true)); + GraphHopper hopper = createHopper(MOSCOW, TestProfiles.accessAndSpeed("car").setTurnCostsConfig(TurnCostsConfig.car())); hopper.setMinNetworkSize(200); hopper.importOrLoad(); checkQueries(hopper, queries); @@ -187,8 +186,7 @@ public void testMoscowTurnCosts() { public void testSimpleTurnCosts() { List list = new ArrayList<>(); list.add(new Query(-0.49, 0.0, 0.0, -0.49, 298792.107, 6)); - GraphHopper hopper = createHopper(DIR + "/test_simple_turncosts.osm.xml", - new Profile("car").setVehicle("car").setTurnCosts(true)); + GraphHopper hopper = createHopper(DIR + "/test_simple_turncosts.osm.xml", TestProfiles.accessAndSpeed("car").setTurnCostsConfig(TurnCostsConfig.car())); hopper.importOrLoad(); checkQueries(hopper, list); } @@ -197,19 +195,13 @@ public void testSimpleTurnCosts() { public void testSimplePTurn() { List list = new ArrayList<>(); list.add(new Query(0, 0.00099, -0.00099, 0, 664, 6)); - GraphHopper hopper = createHopper(DIR + "/test_simple_pturn.osm.xml", - new Profile("car").setVehicle("car").setTurnCosts(true)); + GraphHopper hopper = createHopper(DIR + "/test_simple_pturn.osm.xml", TestProfiles.accessAndSpeed("car").setTurnCostsConfig(TurnCostsConfig.car())); hopper.importOrLoad(); checkQueries(hopper, list); } static CustomModel getCustomModel(String file) { - try { - String string = Helper.readJSONFileWithoutComments(new InputStreamReader(GHUtility.class.getResourceAsStream("/com/graphhopper/custom_models/" + file))); - return Jackson.newObjectMapper().readValue(string, CustomModel.class); - } catch (Exception ex) { - throw new RuntimeException(ex); - } + return GHUtility.loadCustomModelFromJar(file); } @Test @@ -220,7 +212,7 @@ public void testSidewalkNo() { // longer path should go through tertiary, see discussion in #476 queries.add(new Query(57.154888, -2.101822, 57.147299, -2.096286, 1118, 68)); - Profile profile = new Profile("hike").setVehicle("foot"); + Profile profile = TestProfiles.accessSpeedAndPriority("foot"); GraphHopper hopper = createHopper(DIR + "/map-sidewalk-no.osm.gz", profile); hopper.importOrLoad(); checkQueries(hopper, queries); @@ -235,7 +227,7 @@ public void testMonacoFastest() { queries.get(3).getPoints().get(1).expectedPoints = 141; queries.get(4).getPoints().get(1).expectedDistance = 2149; queries.get(4).getPoints().get(1).expectedPoints = 120; - GraphHopper hopper = createHopper(MONACO, new Profile("car").setVehicle("car")); + GraphHopper hopper = createHopper(MONACO, TestProfiles.accessAndSpeed("car")); hopper.importOrLoad(); checkQueries(hopper, queries); } @@ -251,16 +243,20 @@ public void testMonacoMixed() { queries.get(3).getPoints().get(1).expectedPoints = 137; queries.get(4).getPoints().get(1).expectedPoints = 116; - GraphHopper hopper = createHopper(MONACO, - new Profile("car").setCustomModel(new CustomModel().setDistanceInfluence(10_000d)).setVehicle("car"), - new Profile("foot").setCustomModel(new CustomModel().setDistanceInfluence(10_000d)).setVehicle("foot")); + Profile carProfile = TestProfiles.accessAndSpeed("car"); + carProfile.getCustomModel().setDistanceInfluence(10_000d); + Profile footProfile = TestProfiles.accessSpeedAndPriority("foot"); + footProfile.getCustomModel().setDistanceInfluence(10_000d); + GraphHopper hopper = createHopper(MONACO, carProfile, footProfile); hopper.importOrLoad(); checkQueries(hopper, queries); } @Test - public void testMonacoFoot() { - GraphHopper hopper = createHopper(MONACO, new Profile("foot").setCustomModel(new CustomModel().setDistanceInfluence(10_000d)).setVehicle("foot")); + public void testRealFootCustomModelInMonaco() { + Profile profile = new Profile("foot").setCustomModel(getCustomModel("foot.json")); + profile.getCustomModel().setDistanceInfluence(10_000d); + GraphHopper hopper = createHopper(MONACO, profile); hopper.importOrLoad(); checkQueries(hopper, createMonacoFoot()); Graph g = hopper.getBaseGraph(); @@ -286,7 +282,9 @@ public void testMonacoFoot3D() { queries.get(1).getPoints().get(1).expectedDistance = 3573; queries.get(1).getPoints().get(1).expectedPoints = 149; - GraphHopper hopper = createHopper(MONACO, new Profile("foot").setCustomModel(new CustomModel().setDistanceInfluence(10_000d)).setVehicle("foot")); + Profile profile = TestProfiles.accessSpeedAndPriority("foot"); + profile.getCustomModel().setDistanceInfluence(10_000d); + GraphHopper hopper = createHopper(MONACO, profile); hopper.setElevationProvider(new SRTMProvider(DIR)); hopper.importOrLoad(); checkQueries(hopper, queries); @@ -304,25 +302,24 @@ private List createMonacoFoot() { @Test public void testNorthBayreuthHikeFastestAnd3D() { List queries = new ArrayList<>(); - // prefer hiking route 'Teufelsloch Unterwaiz' and 'Rotmain-Wanderweg' + // prefer hiking route 'Teufelsloch Unterwaiz' and 'Rotmain-Wanderweg' queries.add(new Query(49.974972, 11.515657, 49.991022, 11.512299, 2365, 67)); // prefer hiking route 'Markgrafenweg Bayreuth Kulmbach' but avoid tertiary highway from Pechgraben - queries.add(new Query(49.990967, 11.545258, 50.023182, 11.555386, 5636, 97)); - GraphHopper hopper = createHopper(BAYREUTH, new Profile("hike").setCustomModel(getCustomModel("hike.json")).setVehicle("roads")); - hopper.setVehiclesString("roads,foot"); + queries.add(new Query(49.990967, 11.545258, 50.023182, 11.555386, 5690, 118)); + GraphHopper hopper = createHopper(BAYREUTH, new Profile("hike").setCustomModel(getCustomModel("hike.json"))); hopper.setElevationProvider(new SRTMProvider(DIR)); hopper.importOrLoad(); + checkQueries(hopper, queries); } @Test public void testHikeCanUseExtremeSacScales() { - GraphHopper hopper = createHopper(HOHEWARTE, new Profile("hike").setCustomModel(getCustomModel("hike.json")).setVehicle("roads")); - hopper.setVehiclesString("foot,roads"); + GraphHopper hopper = createHopper(HOHEWARTE, new Profile("hike").setCustomModel(getCustomModel("hike.json"))); // do not pull elevation data: hopper.setElevationProvider(new SRTMProvider(DIR)); hopper.importOrLoad(); GHResponse res = hopper.route(new GHRequest(47.290322, 11.333889, 47.301593, 11.333489).setProfile("hike")); - assertEquals(3604, res.getBest().getTime() / 1000.0, 60); // 6100sec with srtm data + assertEquals(4806, res.getBest().getTime() / 1000.0, 60); // 6100sec with srtm data assertEquals(2000, res.getBest().getDistance(), 10); // 2536m with srtm data } @@ -345,12 +342,10 @@ public void testMonacoBike3D() { queries.add(new Query(43.739213, 7.427806, 43.728677, 7.41016, 2806, 145)); // 4. avoid tunnel(s)! queries.add(new Query(43.739662, 7.424355, 43.733802, 7.413433, 1901, 116)); - // atm the custom model is intended to be used with 'roads' vehicle when allowing reverse direction for oneways - // but tests here still assert that reverse oneways are excluded + // tests here still assert that reverse oneways are excluded GraphHopper hopper = createHopper(MONACO, new Profile("bike").setCustomModel(CustomModel.merge(getCustomModel("bike.json"), getCustomModel("bike_elevation.json")). - addToPriority(If("!bike_access", MULTIPLY, "0"))).setVehicle("roads")); - hopper.setVehiclesString("roads,bike"); + addToPriority(If("!bike_access", MULTIPLY, "0")))); hopper.setElevationProvider(new SRTMProvider(DIR)); hopper.importOrLoad(); checkQueries(hopper, queries); @@ -365,7 +360,7 @@ public void testLandmarkBug() { run.add(50.023623, 11.56929, 7069, 178); queries.add(run); - GraphHopper hopper = createHopper(BAYREUTH, new Profile("bike").setVehicle("bike")); + GraphHopper hopper = createHopper(BAYREUTH, TestProfiles.accessSpeedAndPriority("bike")); hopper.importOrLoad(); checkQueries(hopper, queries); } @@ -379,7 +374,7 @@ public void testBug1014() { query.add(50.023623, 11.56929, 6777, 175); queries.add(query); - GraphHopper hopper = createHopper(BAYREUTH, new Profile("bike").setVehicle("bike")); + GraphHopper hopper = createHopper(BAYREUTH, TestProfiles.accessSpeedAndPriority("bike")); hopper.importOrLoad(); checkQueries(hopper, queries); } @@ -391,8 +386,9 @@ public void testMonacoBike() { queries.add(new Query(43.727687, 7.418737, 43.74958, 7.436566, 3580, 168)); queries.add(new Query(43.728677, 7.41016, 43.739213, 7.427806, 2323, 121)); queries.add(new Query(43.733802, 7.413433, 43.739662, 7.424355, 1446, 91)); - GraphHopper hopper = createHopper(MONACO, new Profile("bike"). - setCustomModel(new CustomModel().setDistanceInfluence(7000d)).setVehicle("bike")); + Profile profile = TestProfiles.accessSpeedAndPriority("bike"); + profile.getCustomModel().setDistanceInfluence(7000d); + GraphHopper hopper = createHopper(MONACO, profile); hopper.importOrLoad(); checkQueries(hopper, queries); } @@ -407,15 +403,15 @@ public void testMonacoMountainBike() { // hard to select between secondary and primary (both are AVOID for mtb) queries.add(new Query(43.733802, 7.413433, 43.739662, 7.424355, 1867, 107)); - GraphHopper hopper = createHopper(MONACO, new Profile("mtb").setVehicle("mtb")); + GraphHopper hopper = createHopper(MONACO, TestProfiles.accessSpeedAndPriority("mtb")); hopper.importOrLoad(); checkQueries(hopper, queries); Helper.removeDir(new File(GH_LOCATION)); hopper = createHopper(MONACO, - new Profile("mtb").setVehicle("mtb"), - new Profile("racingbike").setVehicle("racingbike")); + TestProfiles.accessSpeedAndPriority("mtb"), + TestProfiles.accessSpeedAndPriority("racingbike")); hopper.importOrLoad(); checkQueries(hopper, queries); } @@ -428,15 +424,15 @@ public void testMonacoRacingBike() { queries.add(new Query(43.728677, 7.41016, 43.739213, 7.427806, 2651, 167)); queries.add(new Query(43.733802, 7.413433, 43.739662, 7.424355, 1516, 86)); - GraphHopper hopper = createHopper(MONACO, new Profile("racingbike").setVehicle("racingbike")); + GraphHopper hopper = createHopper(MONACO, TestProfiles.accessSpeedAndPriority("racingbike")); hopper.importOrLoad(); checkQueries(hopper, queries); Helper.removeDir(new File(GH_LOCATION)); hopper = createHopper(MONACO, - new Profile("racingbike").setVehicle("racingbike"), - new Profile("bike").setVehicle("bike") + TestProfiles.accessSpeedAndPriority("racingbike"), + TestProfiles.accessSpeedAndPriority("bike") ); hopper.importOrLoad(); checkQueries(hopper, queries); @@ -450,8 +446,7 @@ public void testKremsBikeRelation() { queries.add(new Query(48.412294, 15.62007, 48.398306, 15.609667, 3965, 94)); GraphHopper hopper = createHopper(KREMS, - new Profile("bike"). - setCustomModel(new CustomModel().setDistanceInfluence(70d)).setVehicle("bike")); + TestProfiles.accessSpeedAndPriority("bike")); hopper.importOrLoad(); checkQueries(hopper, queries); hopper.getBaseGraph(); @@ -459,9 +454,8 @@ public void testKremsBikeRelation() { Helper.removeDir(new File(GH_LOCATION)); hopper = createHopper(KREMS, - new Profile("bike"). - setCustomModel(new CustomModel().setDistanceInfluence(70d)).setVehicle("bike"), - new Profile("car").setVehicle("car")); + TestProfiles.accessSpeedAndPriority("bike"), + TestProfiles.accessAndSpeed("car")); hopper.importOrLoad(); checkQueries(hopper, queries); } @@ -473,17 +467,15 @@ public void testKremsMountainBikeRelation() { queries.add(new Query(48.410061, 15.63951, 48.411386, 15.604899, 3101, 94)); queries.add(new Query(48.412294, 15.62007, 48.398306, 15.609667, 3965, 95)); - GraphHopper hopper = createHopper(KREMS, new Profile("mtb"). - setCustomModel(new CustomModel().setDistanceInfluence(70d)).setVehicle("mtb")); + GraphHopper hopper = createHopper(KREMS, TestProfiles.accessSpeedAndPriority("mtb")); hopper.importOrLoad(); checkQueries(hopper, queries); Helper.removeDir(new File(GH_LOCATION)); hopper = createHopper(KREMS, - new Profile("mtb"). - setCustomModel(new CustomModel().setDistanceInfluence(70d)).setVehicle("mtb"), - new Profile("bike").setVehicle("bike")); + TestProfiles.accessSpeedAndPriority("mtb"), + TestProfiles.accessSpeedAndPriority("bike")); hopper.importOrLoad(); checkQueries(hopper, queries); @@ -498,7 +490,7 @@ private List createAndorraQueries() { @Test public void testAndorra() { - Profile profile = new Profile("car").setVehicle("car"); + Profile profile = TestProfiles.accessAndSpeed("car"); GraphHopper hopper = createHopper(ANDORRA, profile); hopper.importOrLoad(); checkQueries(hopper, createAndorraQueries()); @@ -506,7 +498,7 @@ public void testAndorra() { @Test public void testAndorraPbf() { - Profile profile = new Profile("car").setVehicle("car"); + Profile profile = TestProfiles.accessAndSpeed("car"); GraphHopper hopper = createHopper(ANDORRA_PBF, profile); hopper.importOrLoad(); checkQueries(hopper, createAndorraQueries()); @@ -517,12 +509,12 @@ public void testAndorraFoot() { List queries = createAndorraQueries(); queries.get(0).getPoints().get(1).expectedDistance = 16460; queries.get(0).getPoints().get(1).expectedPoints = 653; - queries.get(1).getPoints().get(1).expectedDistance = 12839; + queries.get(1).getPoints().get(1).expectedDistance = 12840; queries.get(1).getPoints().get(1).expectedPoints = 435; queries.add(new Query(42.521269, 1.52298, 42.50418, 1.520662, 3223, 107)); - GraphHopper hopper = createHopper(ANDORRA, new Profile("foot").setVehicle("foot")); + GraphHopper hopper = createHopper(ANDORRA, TestProfiles.accessSpeedAndPriority("foot")); hopper.importOrLoad(); checkQueries(hopper, queries); } @@ -531,14 +523,15 @@ public void testAndorraFoot() { public void testCampoGrande() { // test not only NE quadrant of earth! - // bzcat campo-grande.osm.bz2 - // | ./bin/osmosis --read-xml enableDateParsing=no file=- --bounding-box top=-20.4 left=-54.6 bottom=-20.6 right=-54.5 --write-xml file=- + // bzcat campo-grande.osm.bz2 + // | ./bin/osmosis --read-xml enableDateParsing=no file=- --bounding-box top=-20.4 left=-54.6 bottom=-20.6 right=-54.5 --write-xml file=- // | bzip2 > campo-grande.extracted.osm.bz2 List queries = new ArrayList<>(); queries.add(new Query(-20.4001, -54.5999, -20.598, -54.54, 25323, 271)); queries.add(new Query(-20.43, -54.54, -20.537, -54.5999, 16233, 226)); - GraphHopper hopper = createHopper(DIR + "/campo-grande.osm.gz", - new Profile("car").setCustomModel(new CustomModel().setDistanceInfluence(1_000d)).setVehicle("car")); + Profile profile = TestProfiles.accessAndSpeed("car"); + profile.getCustomModel().setDistanceInfluence(1_000d); + GraphHopper hopper = createHopper(DIR + "/campo-grande.osm.gz", profile); hopper.importOrLoad(); checkQueries(hopper, queries); } @@ -553,7 +546,9 @@ public void testMonacoVia() { List queries = new ArrayList<>(); queries.add(query); - GraphHopper hopper = createHopper(MONACO, new Profile("car").setCustomModel(new CustomModel().setDistanceInfluence(10_000d)).setVehicle("car")); + Profile profile = TestProfiles.accessAndSpeed("car"); + profile.getCustomModel().setDistanceInfluence(10_000d); + GraphHopper hopper = createHopper(MONACO, profile); hopper.importOrLoad(); checkQueries(hopper, queries); } @@ -566,7 +561,7 @@ public void testHarsdorf() { // choose Unterloher Weg and the following residential + cycleway // list.add(new OneRun(50.004333, 11.600254, 50.044449, 11.543434, 6931, 184)); - GraphHopper hopper = createHopper(BAYREUTH, new Profile("bike").setVehicle("bike")); + GraphHopper hopper = createHopper(BAYREUTH, TestProfiles.accessSpeedAndPriority("bike")); hopper.importOrLoad(); checkQueries(hopper, queries); } @@ -577,14 +572,14 @@ public void testNeudrossenfeld() { // choose cycleway (Dreschenauer Straße) list.add(new Query(49.987132, 11.510496, 50.018839, 11.505024, 3985, 106)); - GraphHopper hopper = createHopper(BAYREUTH, new Profile("bike").setVehicle("bike")); + GraphHopper hopper = createHopper(BAYREUTH, TestProfiles.accessSpeedAndPriority("bike")); hopper.setElevationProvider(new SRTMProvider(DIR)); hopper.importOrLoad(); checkQueries(hopper, list); Helper.removeDir(new File(GH_LOCATION)); - hopper = createHopper(BAYREUTH, new Profile("bike2").setVehicle("bike")); + hopper = createHopper(BAYREUTH, TestProfiles.accessSpeedAndPriority("bike")); hopper.setElevationProvider(new SRTMProvider(DIR)); hopper.importOrLoad(); checkQueries(hopper, list); @@ -596,8 +591,7 @@ public void testBikeBayreuth_UseBikeNetwork() { list.add(new Query(49.979667, 11.521019, 49.987415, 11.510577, 1288, 45)); GraphHopper hopper = createHopper(BAYREUTH, new Profile("bike").setCustomModel( - CustomModel.merge(getCustomModel("bike.json"), getCustomModel("bike_elevation.json"))).setVehicle("roads")); - hopper.setVehiclesString("bike,roads"); + CustomModel.merge(getCustomModel("bike.json"), getCustomModel("bike_elevation.json")))); hopper.setElevationProvider(new SRTMProvider(DIR)); hopper.importOrLoad(); checkQueries(hopper, list); @@ -611,7 +605,7 @@ public void testDisconnectedAreaAndMultiplePoints() { query.add(53.751299, 9.3869, 10, 10); GraphHopper hopper = createHopper(DIR + "/krautsand.osm.gz", - new Profile("car").setVehicle("car")); + TestProfiles.accessAndSpeed("car")); hopper.importOrLoad(); for (Function requestFactory : createRequestFactories()) { @@ -625,7 +619,9 @@ public void testDisconnectedAreaAndMultiplePoints() { @Test public void testMonacoParallel() throws InterruptedException { - GraphHopper hopper = createHopper(MONACO, new Profile("car").setCustomModel(new CustomModel().setDistanceInfluence(10_000d)).setVehicle("car")); + Profile profile = TestProfiles.accessAndSpeed("car"); + profile.getCustomModel().setDistanceInfluence(10_000d); + GraphHopper hopper = createHopper(MONACO, profile); hopper.getReaderConfig().setMaxWayPointDistance(0); hopper.getRouterConfig().setSimplifyResponse(false); hopper.importOrLoad(); @@ -644,7 +640,7 @@ public void testMonacoParallel() throws InterruptedException { GHRequest req = requestFactory.apply(query); Thread t = new Thread(() -> { GHResponse res = hopper.route(req); - checkResponse(res, query); + checkResponse(req.getHints().getString("expected_algo", "no_expected_algo"), res, query); routeCount.incrementAndGet(); }); t.start(); @@ -711,7 +707,11 @@ private GraphHopper createHopper(String osmFile, Profile... profiles) { setStoreOnFlush(false). setOSMFile(osmFile). setProfiles(profiles). - setEncodedValuesString("average_slope,max_slope,hike_rating"). + setEncodedValuesString("average_slope, max_slope, hike_rating, car_access, car_average_speed, " + + "foot_access, foot_priority, foot_average_speed, " + + "bike_access, bike_priority, bike_average_speed, foot_network, roundabout, " + + "mtb_access, mtb_priority, mtb_average_speed, " + + "racingbike_access, racingbike_priority, racingbike_average_speed"). setGraphHopperLocation(GH_LOCATION); hopper.getRouterConfig().setSimplifyResponse(false); hopper.setMinNetworkSize(0); @@ -732,10 +732,10 @@ private void checkQueries(GraphHopper hopper, List queries) { Profile profile = hopper.getProfiles().get(0); request.setProfile(profile.getName()); GHResponse res = hopper.route(request); - checkResponse(res, query); String expectedAlgo = request.getHints().getString("expected_algo", "no_expected_algo"); + checkResponse(expectedAlgo, res, query); // for edge-based routing we expect a slightly different algo name for CH - if (profile.isTurnCosts()) + if (profile.hasTurnCosts()) expectedAlgo = expectedAlgo.replaceAll("\\|ch-routing", "|ch|edge_based|no_sod-routing"); assertTrue(res.getBest().getDebugInfo().contains(expectedAlgo), "Response does not contain expected algo string. Expected: '" + expectedAlgo + @@ -744,16 +744,16 @@ private void checkQueries(GraphHopper hopper, List queries) { } } - private void checkResponse(GHResponse res, Query query) { + private void checkResponse(String expectedAlgo, GHResponse res, Query query) { assertFalse(res.hasErrors(), res.getErrors().toString()); ResponsePath responsePath = res.getBest(); assertFalse(responsePath.hasErrors(), responsePath.getErrors().toString()); assertEquals(distCalc.calcDistance(responsePath.getPoints()), responsePath.getDistance(), 2, "responsePath.getDistance does not equal point list distance"); - assertEquals(query.getPoints().stream().mapToDouble(a -> a.expectedDistance).sum(), responsePath.getDistance(), 2, "unexpected distance"); + assertEquals(query.getPoints().stream().mapToDouble(a -> a.expectedDistance).sum(), responsePath.getDistance(), 2, "unexpected distance, " + expectedAlgo); // We check the number of points to make sure we found the expected route. // There are real world instances where A-B-C is identical to A-C (in meter precision). - assertEquals(query.getPoints().stream().mapToInt(a -> a.expectedPoints).sum(), responsePath.getPoints().size(), 1, "unexpected point list size"); + assertEquals(query.getPoints().stream().mapToInt(a -> a.expectedPoints).sum(), responsePath.getPoints().size(), 1, "unexpected point list size, " + expectedAlgo); } private List> createRequestFactories() { diff --git a/core/src/test/java/com/graphhopper/routing/RoutingCHGraphImplTest.java b/core/src/test/java/com/graphhopper/routing/RoutingCHGraphImplTest.java index 2309303d6ec..d463cf4f9ce 100644 --- a/core/src/test/java/com/graphhopper/routing/RoutingCHGraphImplTest.java +++ b/core/src/test/java/com/graphhopper/routing/RoutingCHGraphImplTest.java @@ -22,11 +22,9 @@ import com.carrotsearch.hppc.IntSet; import com.graphhopper.routing.ch.PrepareEncoder; import com.graphhopper.routing.ev.*; -import com.graphhopper.routing.util.AccessFilter; import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.routing.weighting.SpeedWeighting; import com.graphhopper.routing.weighting.Weighting; -import com.graphhopper.routing.weighting.custom.CustomModelParser; import com.graphhopper.storage.*; import com.graphhopper.util.EdgeExplorer; import com.graphhopper.util.EdgeIteratorState; @@ -39,15 +37,14 @@ public class RoutingCHGraphImplTest { @Test public void testBaseAndCHEdges() { - BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); - EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + EncodingManager em = EncodingManager.start().add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); graph.edge(1, 0); graph.edge(8, 9); graph.freeze(); - CHConfig chConfig = CHConfig.nodeBased("p", CustomModelParser.createFastestWeighting(accessEnc, speedEnc, em)); + CHConfig chConfig = CHConfig.nodeBased("p", new SpeedWeighting(speedEnc)); CHStorage store = CHStorage.fromGraph(graph, chConfig); CHStorageBuilder chBuilder = new CHStorageBuilder(store); chBuilder.setIdentityLevels(); @@ -78,15 +75,13 @@ void testShortcutConnection() { // 4 ------ 1 > 0 // ^ \ // 3 2 - BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); - EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + EncodingManager em = EncodingManager.start().add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); - EdgeExplorer baseCarOutExplorer = graph.createEdgeExplorer(AccessFilter.outEdges(accessEnc)); - GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 1).setDistance(30)); + graph.edge(4, 1).setDistance(30).set(speedEnc, 60); graph.freeze(); - CHConfig chConfig = CHConfig.nodeBased("ch", CustomModelParser.createFastestWeighting(accessEnc, speedEnc, em)); + CHConfig chConfig = CHConfig.nodeBased("ch", new SpeedWeighting(speedEnc)); CHStorage store = CHStorage.fromGraph(graph, chConfig); CHStorageBuilder chBuilder = new CHStorageBuilder(store); chBuilder.setIdentityLevels(); @@ -94,6 +89,7 @@ void testShortcutConnection() { chBuilder.addShortcutNodeBased(1, 2, PrepareEncoder.getScDirMask(), 10, 10, 11); chBuilder.addShortcutNodeBased(1, 3, PrepareEncoder.getScBwdDir(), 10, 14, 15); + EdgeExplorer baseExplorer = graph.createEdgeExplorer(); RoutingCHGraph lg = RoutingCHGraphImpl.fromGraph(graph, store, chConfig); RoutingCHEdgeExplorer chOutExplorer = lg.createOutEdgeExplorer(); RoutingCHEdgeExplorer chInExplorer = lg.createInEdgeExplorer(); @@ -105,7 +101,7 @@ void testShortcutConnection() { assertEquals(2, GHUtility.count(chOutExplorer.setBaseNode(1))); assertEquals(3, GHUtility.count(chInExplorer.setBaseNode(1))); assertEquals(GHUtility.asSet(2, 4), GHUtility.getNeighbors(chOutExplorer.setBaseNode(1))); - assertEquals(GHUtility.asSet(4), GHUtility.getNeighbors(baseCarOutExplorer.setBaseNode(1))); + assertEquals(GHUtility.asSet(4), GHUtility.getNeighbors(baseExplorer.setBaseNode(1))); assertEquals(0, GHUtility.count(chOutExplorer.setBaseNode(3))); assertEquals(0, GHUtility.count(chInExplorer.setBaseNode(3))); @@ -116,15 +112,14 @@ void testShortcutConnection() { @Test public void testGetWeight() { - BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); - EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + EncodingManager em = EncodingManager.start().add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); EdgeIteratorState edge1 = graph.edge(0, 1); EdgeIteratorState edge2 = graph.edge(1, 2); graph.freeze(); - CHConfig chConfig = CHConfig.nodeBased("ch", CustomModelParser.createFastestWeighting(accessEnc, speedEnc, em)); + CHConfig chConfig = CHConfig.nodeBased("ch", new SpeedWeighting(speedEnc)); CHStorage store = CHStorage.fromGraph(graph, chConfig); RoutingCHGraph g = RoutingCHGraphImpl.fromGraph(graph, store, chConfig); assertFalse(g.getEdgeIteratorState(edge1.getEdge(), Integer.MIN_VALUE).isShortcut()); @@ -144,14 +139,13 @@ public void testGetWeight() { @Test public void testGetWeightIfAdvancedEncoder() { - BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 4, 2, true); - EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + EncodingManager em = EncodingManager.start().add(speedEnc).build(); BaseGraph ghStorage = new BaseGraph.Builder(em).create(); ghStorage.edge(0, 3); ghStorage.freeze(); - Weighting weighting = CustomModelParser.createFastestWeighting(accessEnc, speedEnc, em); + Weighting weighting = new SpeedWeighting(speedEnc); CHConfig chConfig = CHConfig.nodeBased("p1", weighting); CHStorage chStore = CHStorage.fromGraph(ghStorage, chConfig); CHStorageBuilder chBuilder = new CHStorageBuilder(chStore); @@ -172,15 +166,14 @@ public void testGetWeightIfAdvancedEncoder() { @Test public void testWeightExact() { - BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); - EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + EncodingManager em = EncodingManager.start().add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); - GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + graph.edge(0, 1).setDistance(1).set(speedEnc, 60); + graph.edge(1, 2).setDistance(1).set(speedEnc, 60); graph.freeze(); - CHConfig chConfig = CHConfig.nodeBased("ch", CustomModelParser.createFastestWeighting(accessEnc, speedEnc, em)); + CHConfig chConfig = CHConfig.nodeBased("ch", new SpeedWeighting(speedEnc)); CHStorage store = CHStorage.fromGraph(graph, chConfig); CHStorageBuilder chBuilder = new CHStorageBuilder(store); chBuilder.setIdentityLevels(); @@ -197,16 +190,15 @@ public void testWeightExact() { @Test public void testSimpleShortcutCreationAndTraversal() { - BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); - EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + EncodingManager em = EncodingManager.start().add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); - GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 3).setDistance(10)); - GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(10)); + graph.edge(1, 3).setDistance(10).set(speedEnc, 60); + graph.edge(3, 4).setDistance(10).set(speedEnc, 60); graph.freeze(); - Weighting weighting = CustomModelParser.createFastestWeighting(accessEnc, speedEnc, em); + Weighting weighting = new SpeedWeighting(speedEnc); CHConfig chConfig = CHConfig.nodeBased("p1", weighting); CHStorage chStore = CHStorage.fromGraph(graph, chConfig); CHStorageBuilder chBuilder = new CHStorageBuilder(chStore); @@ -222,15 +214,14 @@ public void testSimpleShortcutCreationAndTraversal() { @Test public void testAddShortcutSkippedEdgesWriteRead() { - BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); - EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + EncodingManager em = EncodingManager.start().add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); - final EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 3).setDistance(10)); - final EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(10)); + final EdgeIteratorState edge1 = graph.edge(1, 3).setDistance(10).set(speedEnc, 60); + final EdgeIteratorState edge2 = graph.edge(3, 4).setDistance(10).set(speedEnc, 60); graph.freeze(); - Weighting weighting = CustomModelParser.createFastestWeighting(accessEnc, speedEnc, em); + Weighting weighting = new SpeedWeighting(speedEnc); CHConfig chConfig = CHConfig.nodeBased("p1", weighting); CHStorage chStore = CHStorage.fromGraph(graph, chConfig); CHStorageBuilder chBuilder = new CHStorageBuilder(chStore); @@ -244,15 +235,14 @@ public void testAddShortcutSkippedEdgesWriteRead() { @Test public void testSkippedEdges() { - BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); - EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + EncodingManager em = EncodingManager.start().add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); - final EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 3).setDistance(10)); - final EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(10)); + final EdgeIteratorState edge1 = graph.edge(1, 3).setDistance(10).set(speedEnc, 60); + final EdgeIteratorState edge2 = graph.edge(3, 4).setDistance(10).set(speedEnc, 60); graph.freeze(); - Weighting weighting = CustomModelParser.createFastestWeighting(accessEnc, speedEnc, em); + Weighting weighting = new SpeedWeighting(speedEnc); CHConfig chConfig = CHConfig.nodeBased("p1", weighting); CHStorage chStore = CHStorage.fromGraph(graph, chConfig); CHStorageBuilder chBuilder = new CHStorageBuilder(chStore); @@ -264,16 +254,16 @@ public void testSkippedEdges() { @Test public void testAddShortcut_edgeBased_throwsIfNotConfiguredForEdgeBased() { - BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); - EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + EncodingManager em = EncodingManager.start().add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); - GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + graph.edge(0, 1).setDistance(1).set(speedEnc, 60); + graph.edge(1, 2).setDistance(1).set(speedEnc, 60); + graph.freeze(); - Weighting weighting = CustomModelParser.createFastestWeighting(accessEnc, speedEnc, em); + Weighting weighting = new SpeedWeighting(speedEnc); CHConfig chConfig = CHConfig.nodeBased("p1", weighting); CHStorage chStore = CHStorage.fromGraph(graph, chConfig); CHStorageBuilder chBuilder = new CHStorageBuilder(chStore); @@ -283,15 +273,14 @@ public void testAddShortcut_edgeBased_throwsIfNotConfiguredForEdgeBased() { @Test public void testAddShortcut_edgeBased() { // 0 -> 1 -> 2 - BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); - EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + EncodingManager em = EncodingManager.start().add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).set3D(true).create(); - GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(3)); + graph.edge(0, 1).setDistance(1).set(speedEnc, 60); + graph.edge(1, 2).setDistance(3).set(speedEnc, 60); graph.freeze(); - Weighting weighting = CustomModelParser.createFastestWeighting(accessEnc, speedEnc, em); + Weighting weighting = new SpeedWeighting(speedEnc); CHConfig chConfig = CHConfig.edgeBased("p1", weighting); CHStorage chStore = CHStorage.fromGraph(graph, chConfig); @@ -304,12 +293,11 @@ public void testAddShortcut_edgeBased() { @Test public void outOfBounds() { - BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); - EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + EncodingManager em = EncodingManager.start().add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).set3D(true).create(); graph.freeze(); - Weighting weighting = CustomModelParser.createFastestWeighting(accessEnc, speedEnc, em); + Weighting weighting = new SpeedWeighting(speedEnc); CHConfig chConfig = CHConfig.nodeBased("p1", weighting); CHStorage chStore = CHStorage.fromGraph(graph, chConfig); RoutingCHGraph lg = RoutingCHGraphImpl.fromGraph(graph, chStore, chConfig); @@ -318,15 +306,14 @@ public void outOfBounds() { @Test public void testGetEdgeIterator() { - BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); - EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + EncodingManager em = EncodingManager.start().add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).set3D(true).create(); - GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + graph.edge(0, 1).setDistance(1).set(speedEnc, 60); + graph.edge(1, 2).setDistance(1).set(speedEnc, 60); graph.freeze(); - Weighting weighting = CustomModelParser.createFastestWeighting(accessEnc, speedEnc, em); + Weighting weighting = new SpeedWeighting(speedEnc); CHConfig chConfig = CHConfig.edgeBased("p1", weighting); CHStorage store = CHStorage.fromGraph(graph, chConfig); CHStorageBuilder chBuilder = new CHStorageBuilder(store); diff --git a/core/src/test/java/com/graphhopper/routing/TrafficChangeWithNodeOrderingReusingTest.java b/core/src/test/java/com/graphhopper/routing/TrafficChangeWithNodeOrderingReusingTest.java index 185b7065daf..0c74844ad6d 100644 --- a/core/src/test/java/com/graphhopper/routing/TrafficChangeWithNodeOrderingReusingTest.java +++ b/core/src/test/java/com/graphhopper/routing/TrafficChangeWithNodeOrderingReusingTest.java @@ -1,5 +1,6 @@ package com.graphhopper.routing; +import com.graphhopper.json.Statement; import com.graphhopper.reader.osm.OSMReader; import com.graphhopper.routing.ch.CHRoutingAlgorithmFactory; import com.graphhopper.routing.ch.PrepareContractionHierarchies; @@ -16,6 +17,7 @@ import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.routing.weighting.custom.CustomModelParser; import com.graphhopper.storage.*; +import com.graphhopper.util.CustomModel; import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.MiniPerfTest; import com.graphhopper.util.PMap; @@ -60,10 +62,15 @@ public Fixture(int maxDeviationPercentage) { BooleanEncodedValue accessEnc = VehicleAccess.create("car"); DecimalEncodedValue speedEnc = VehicleSpeed.create("car", 5, 5, false); em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); - CarAverageSpeedParser carParser = new CarAverageSpeedParser(em, new PMap()); + CarAverageSpeedParser carParser = new CarAverageSpeedParser(em); osmParsers = new OSMParsers() .addWayTagParser(carParser); - baseCHConfig = CHConfig.nodeBased("base", CustomModelParser.createFastestWeighting(accessEnc, speedEnc, em)); + baseCHConfig = CHConfig.nodeBased("base", CustomModelParser.createWeighting(em, TurnCostProvider.NO_TURN_COST_PROVIDER, + new CustomModel() + .addToPriority(Statement.If("!car_access", Statement.Op.MULTIPLY, "0")) + .addToSpeed(Statement.If("true", Statement.Op.LIMIT, "car_average_speed") + ) + )); trafficCHConfig = CHConfig.nodeBased("traffic", new RandomDeviationWeighting(baseCHConfig.getWeighting(), accessEnc, speedEnc, maxDeviationPercentage)); graph = new BaseGraph.Builder(em).create(); } @@ -206,9 +213,9 @@ public RandomDeviationWeighting(Weighting baseWeighting, BooleanEncodedValue acc } @Override - public double getMinWeight(double distance) { + public double calcMinWeightPerDistance() { // left as is, ok for now, but do not use with astar, at least as long as deviations can be negative!! - return this.baseWeighting.getMinWeight(distance); + return this.baseWeighting.calcMinWeightPerDistance(); } @Override diff --git a/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java b/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java index bb58e42b386..b820cdcb4c8 100644 --- a/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java +++ b/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java @@ -317,13 +317,13 @@ public void testStallOnDemandViaVirtuaNode_issue1574() { // * the fact that the CHLevelEdgeFilter always accepts virtual nodes // here we will construct a special case where a connection is not found without the fix in #1574. - BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, true); - EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + EncodingManager encodingManager = EncodingManager.start().add(speedEnc).build(); BaseGraph g = new BaseGraph.Builder(encodingManager).create(); - // use fastest weighting in this test to be able to fine-tune some weights via the speed (see below) - Weighting fastestWeighting = CustomModelParser.createFastestWeighting(accessEnc, speedEnc, encodingManager); - CHConfig chConfig = CHConfig.nodeBased("c", fastestWeighting); + // note: we changed the weighting during some refactorings, so if this test is no longer testing what + // it is supposed to test maybe that's why. + Weighting weighting = new SpeedWeighting(speedEnc); + CHConfig chConfig = CHConfig.nodeBased("c", weighting); // the following graph reproduces the issue. note that we will use the node ids as ch levels, so there will // be a shortcut 3->2 visible at node 2 and another one 3->4 visible at node 3. // we will fine-tune the edge-speeds such that without the fix node 4 will be stalled and node 5 will not get @@ -334,13 +334,13 @@ public void testStallOnDemandViaVirtuaNode_issue1574() { // start 0 - 3 - x - 1 - 2 // \ | // sc ---- 4 - 5 - 6 - 7 finish - g.edge(0, 3).setDistance(1).set(speedEnc, 60, 60).set(accessEnc, true, true); - EdgeIteratorState edge31 = g.edge(3, 1).setDistance(1).set(speedEnc, 60, 60).set(accessEnc, true, true); - g.edge(1, 2).setDistance(1).set(speedEnc, 60, 60).set(accessEnc, true, true); - EdgeIteratorState edge24 = g.edge(2, 4).setDistance(1).set(speedEnc, 60, 60).set(accessEnc, true, true); - g.edge(4, 5).setDistance(1).set(speedEnc, 60, 60).set(accessEnc, true, true); - g.edge(5, 6).setDistance(1).set(speedEnc, 60, 60).set(accessEnc, true, true); - g.edge(6, 7).setDistance(1).set(speedEnc, 60, 60).set(accessEnc, true, true); + g.edge(0, 3).setDistance(1).set(speedEnc, 60, 60); + EdgeIteratorState edge31 = g.edge(3, 1).setDistance(1).set(speedEnc, 60, 60); + g.edge(1, 2).setDistance(1).set(speedEnc, 60, 60); + EdgeIteratorState edge24 = g.edge(2, 4).setDistance(1).set(speedEnc, 60, 60); + g.edge(4, 5).setDistance(1).set(speedEnc, 60, 60); + g.edge(5, 6).setDistance(1).set(speedEnc, 60, 60); + g.edge(6, 7).setDistance(1).set(speedEnc, 60, 60); updateDistancesFor(g, 0, 0.001, 0.0000); updateDistancesFor(g, 3, 0.001, 0.0001); updateDistancesFor(g, 1, 0.001, 0.0002); @@ -381,11 +381,11 @@ public void testStallOnDemandViaVirtuaNode_issue1574() { QueryGraph queryGraph = QueryGraph.create(g, snap); // we make sure our weight fine tunings do what they are supposed to - double weight03 = getWeight(queryGraph, fastestWeighting, accessEnc, 0, 3, false); + double weight03 = getWeight(queryGraph, weighting, 0, 3); double scWeight23 = weight03 + getEdge(routingCHGraph, 2, 3, true).getWeight(false); double scWeight34 = weight03 + getEdge(routingCHGraph, 3, 4, false).getWeight(false); - double sptWeight2 = weight03 + getWeight(queryGraph, fastestWeighting, accessEnc, 3, 8, false) + getWeight(queryGraph, fastestWeighting, accessEnc, 8, 1, false) + getWeight(queryGraph, fastestWeighting, accessEnc, 1, 2, false); - double sptWeight4 = sptWeight2 + getWeight(queryGraph, fastestWeighting, accessEnc, 2, 4, false); + double sptWeight2 = weight03 + getWeight(queryGraph, weighting, 3, 8) + getWeight(queryGraph, weighting, 8, 1) + getWeight(queryGraph, weighting, 1, 2); + double sptWeight4 = sptWeight2 + getWeight(queryGraph, weighting, 2, 4); assertTrue(scWeight23 < sptWeight2, "incoming shortcut weight 3->2 should be smaller than sptWeight at node 2 to make sure 2 gets stalled"); assertTrue(sptWeight4 < scWeight34, "sptWeight at node 4 should be smaller than shortcut weight 3->4 to make sure node 4 gets stalled"); @@ -393,13 +393,12 @@ public void testStallOnDemandViaVirtuaNode_issue1574() { assertEquals(IntArrayList.from(0, 3, 8, 1, 2, 4, 5, 6, 7), path.calcNodes(), "wrong or no path found"); } - private double getWeight(Graph graph, Weighting w, BooleanEncodedValue accessEnc, int from, int to, boolean incoming) { - return w.calcEdgeWeight(getEdge(graph, accessEnc, from, to, false), incoming); + private double getWeight(Graph graph, Weighting w, int from, int to) { + return w.calcEdgeWeight(getEdge(graph, from, to), false); } - private EdgeIteratorState getEdge(Graph graph, BooleanEncodedValue accessEnc, int from, int to, boolean incoming) { - EdgeFilter filter = incoming ? AccessFilter.inEdges(accessEnc) : AccessFilter.outEdges(accessEnc); - EdgeIterator iter = graph.createEdgeExplorer(filter).setBaseNode(from); + private EdgeIteratorState getEdge(Graph graph, int from, int to) { + EdgeIterator iter = graph.createEdgeExplorer().setBaseNode(from); while (iter.next()) { if (iter.getAdjNode() == to) { return iter; diff --git a/core/src/test/java/com/graphhopper/routing/ev/EncodedValueSerializerTest.java b/core/src/test/java/com/graphhopper/routing/ev/EncodedValueSerializerTest.java index 95b3a467e46..1e5dd40fa78 100644 --- a/core/src/test/java/com/graphhopper/routing/ev/EncodedValueSerializerTest.java +++ b/core/src/test/java/com/graphhopper/routing/ev/EncodedValueSerializerTest.java @@ -18,7 +18,6 @@ package com.graphhopper.routing.ev; -import com.graphhopper.util.PMap; import org.junit.jupiter.api.Test; import java.util.ArrayList; @@ -34,11 +33,10 @@ class EncodedValueSerializerTest { public void serializationAndDeserialization() { List encodedValues = new ArrayList<>(); // add enum, int, decimal and boolean encoded values - DefaultEncodedValueFactory evFactory = new DefaultEncodedValueFactory(); - encodedValues.add(evFactory.create(RoadClass.KEY, new PMap())); - encodedValues.add(evFactory.create(Lanes.KEY, new PMap())); - encodedValues.add(evFactory.create(MaxWidth.KEY, new PMap())); - encodedValues.add(evFactory.create(GetOffBike.KEY, new PMap())); + encodedValues.add(RoadClass.create()); + encodedValues.add(Lanes.create()); + encodedValues.add(MaxWidth.create()); + encodedValues.add(GetOffBike.create()); StringEncodedValue namesEnc = new StringEncodedValue("names", 3, Arrays.asList("jim", "joe", "kate"), false); encodedValues.add(namesEnc); @@ -70,11 +68,10 @@ public void serializationAndDeserialization() { @Test void explicitString() { EncodedValue.InitializerConfig initializerConfig = new EncodedValue.InitializerConfig(); - DefaultEncodedValueFactory evFactory = new DefaultEncodedValueFactory(); List evs = Arrays.asList( - evFactory.create(Lanes.KEY, new PMap()), - evFactory.create(MaxWidth.KEY, new PMap()), - evFactory.create(GetOffBike.KEY, new PMap()) + Lanes.create(), + MaxWidth.create(), + GetOffBike.create() ); evs.forEach(ev -> ev.init(initializerConfig)); @@ -98,4 +95,4 @@ void explicitString() { assertEquals("get_off_bike", ev2.getName()); } -} \ No newline at end of file +} diff --git a/core/src/test/java/com/graphhopper/routing/ev/ImportUnitSorterTest.java b/core/src/test/java/com/graphhopper/routing/ev/ImportUnitSorterTest.java new file mode 100644 index 00000000000..61669da09e3 --- /dev/null +++ b/core/src/test/java/com/graphhopper/routing/ev/ImportUnitSorterTest.java @@ -0,0 +1,67 @@ +/* + * 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.routing.ev; + +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +public class ImportUnitSorterTest { + + @Test + public void simple() { + ImportUnit a = create("a"); + ImportUnit b = create("b", "a"); + ImportUnit c = create("c", "b"); + + Map importUnits = new HashMap<>(); + importUnits.put("a", a); + importUnits.put("b", b); + importUnits.put("c", c); + + ImportUnitSorter sorter = new ImportUnitSorter(importUnits); + List sorted = sorter.sort(); + + assertEquals(importUnits.size(), sorted.size()); + assertEquals(List.of("a", "b", "c"), sorted); + } + + @Test + public void cycle() { + ImportUnit a = create("a", "b"); + ImportUnit b = create("b", "a"); + + Map importUnits = new HashMap<>(); + importUnits.put("a", a); + importUnits.put("b", b); + + ImportUnitSorter sorter = new ImportUnitSorter(importUnits); + IllegalArgumentException e = assertThrows(IllegalArgumentException.class, sorter::sort); + assertTrue(e.getMessage().contains("import units with cyclic dependencies are not allowed")); + } + + private ImportUnit create(String name, String... required) { + return ImportUnit.create(name, null, null, required); + } + +} diff --git a/core/src/test/java/com/graphhopper/routing/lm/LMIssueTest.java b/core/src/test/java/com/graphhopper/routing/lm/LMIssueTest.java index ff735e586ce..ff289b01500 100644 --- a/core/src/test/java/com/graphhopper/routing/lm/LMIssueTest.java +++ b/core/src/test/java/com/graphhopper/routing/lm/LMIssueTest.java @@ -21,6 +21,7 @@ import com.graphhopper.routing.*; import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.weighting.SpeedWeighting; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.routing.weighting.custom.CustomModelParser; import com.graphhopper.storage.BaseGraph; @@ -40,7 +41,6 @@ public class LMIssueTest { private Directory dir; private BaseGraph graph; - private BooleanEncodedValue accessEnc; private DecimalEncodedValue speedEnc; private Weighting weighting; private LandmarkStorage lm; @@ -58,15 +58,14 @@ private enum Algo { @BeforeEach public void init() { dir = new RAMDirectory(); - accessEnc = new SimpleBooleanEncodedValue("access", true); - speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, true); DecimalEncodedValue turnCostEnc = TurnCost.create("car", 1); - encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc).addTurnCostEncodedValue(turnCostEnc).add(Subnetwork.create("car")).build(); + encodingManager = new EncodingManager.Builder().add(speedEnc).addTurnCostEncodedValue(turnCostEnc).add(Subnetwork.create("car")).build(); graph = new BaseGraph.Builder(encodingManager) .withTurnCosts(true) .setDir(dir) .create(); - weighting = CustomModelParser.createFastestWeighting(accessEnc, speedEnc, encodingManager); + weighting = new SpeedWeighting(speedEnc); } private void preProcessGraph() { @@ -122,13 +121,13 @@ public void lm_problem_to_node_of_fallback_approximator(Algo algo) { na.setNode(3, 49.403009, 9.708364); na.setNode(4, 49.409021, 9.703622); // 30s - GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 3).setDistance(1000)).set(speedEnc, 120); - GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 2).setDistance(1000)).set(speedEnc, 120); + graph.edge(4, 3).setDistance(1000).set(speedEnc, 120, 120); + graph.edge(0, 2).setDistance(1000).set(speedEnc, 120, 0); // 360s - GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 3).setDistance(1000)).set(speedEnc, 10); + graph.edge(1, 3).setDistance(1000).set(speedEnc, 10, 60); // 80s - GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1000)).set(speedEnc, 45); - GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 4).setDistance(1000)).set(speedEnc, 45); + graph.edge(0, 1).setDistance(1000).set(speedEnc, 45, 0); + graph.edge(1, 4).setDistance(1000).set(speedEnc, 45, 60); preProcessGraph(); int source = 0; @@ -163,13 +162,13 @@ public void lm_issue2(Algo algo) { na.setNode(7, 49.406965, 9.702660); na.setNode(8, 49.405227, 9.702863); na.setNode(9, 49.409411, 9.709085); - GHUtility.setSpeed(112, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(623.197000)); - GHUtility.setSpeed(13, true, true, accessEnc, speedEnc, graph.edge(5, 1).setDistance(741.414000)); - GHUtility.setSpeed(35, true, true, accessEnc, speedEnc, graph.edge(9, 4).setDistance(1140.835000)); - GHUtility.setSpeed(18, true, true, accessEnc, speedEnc, graph.edge(5, 6).setDistance(670.689000)); - GHUtility.setSpeed(88, true, false, accessEnc, speedEnc, graph.edge(5, 9).setDistance(80.731000)); - GHUtility.setSpeed(82, true, true, accessEnc, speedEnc, graph.edge(0, 9).setDistance(273.948000)); - GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 0).setDistance(956.552000)); + graph.edge(0, 1).setDistance(623.197000).set(speedEnc, 112, 112); + graph.edge(5, 1).setDistance(741.414000).set(speedEnc, 13, 13); + graph.edge(9, 4).setDistance(1140.835000).set(speedEnc, 35, 35); + graph.edge(5, 6).setDistance(670.689000).set(speedEnc, 18, 18); + graph.edge(5, 9).setDistance(80.731000).set(speedEnc, 88, 0); + graph.edge(0, 9).setDistance(273.948000).set(speedEnc, 82, 82); + graph.edge(4, 0).setDistance(956.552000).set(speedEnc, 60, 60); preProcessGraph(); int source = 5; diff --git a/core/src/test/java/com/graphhopper/routing/lm/LMPreparationHandlerTest.java b/core/src/test/java/com/graphhopper/routing/lm/LMPreparationHandlerTest.java index 20ba47f0f7e..763b64ee10c 100644 --- a/core/src/test/java/com/graphhopper/routing/lm/LMPreparationHandlerTest.java +++ b/core/src/test/java/com/graphhopper/routing/lm/LMPreparationHandlerTest.java @@ -3,13 +3,10 @@ import com.graphhopper.GraphHopperConfig; import com.graphhopper.config.LMProfile; import com.graphhopper.config.Profile; -import com.graphhopper.routing.ev.BooleanEncodedValue; import com.graphhopper.routing.ev.DecimalEncodedValue; import com.graphhopper.routing.ev.DecimalEncodedValueImpl; -import com.graphhopper.routing.ev.SimpleBooleanEncodedValue; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.weighting.FastestWeighting; -import com.graphhopper.routing.weighting.ShortestWeighting; +import com.graphhopper.routing.weighting.SpeedWeighting; import com.graphhopper.storage.BaseGraph; import org.junit.jupiter.api.Test; @@ -37,12 +34,11 @@ public void maximumLMWeight() { new LMProfile("conf1").setMaximumLMWeight(65_000), new LMProfile("conf2").setMaximumLMWeight(20_000) ); - BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", false); DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); - EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + EncodingManager em = EncodingManager.start().add(speedEnc).build(); List lmConfigs = Arrays.asList( - new LMConfig("conf1", new FastestWeighting(accessEnc, speedEnc)), - new LMConfig("conf2", new ShortestWeighting(accessEnc, speedEnc)) + new LMConfig("conf1", new SpeedWeighting(speedEnc)), + new LMConfig("conf2", new SpeedWeighting(speedEnc)) ); List preparations = handler.createPreparations(lmConfigs, new BaseGraph.Builder(em).build(), em, null); assertEquals(1, preparations.get(0).getLandmarkStorage().getFactor(), .1); diff --git a/core/src/test/java/com/graphhopper/routing/lm/PrepareLandmarksTest.java b/core/src/test/java/com/graphhopper/routing/lm/PrepareLandmarksTest.java index d10b96966fd..459f9234284 100644 --- a/core/src/test/java/com/graphhopper/routing/lm/PrepareLandmarksTest.java +++ b/core/src/test/java/com/graphhopper/routing/lm/PrepareLandmarksTest.java @@ -26,6 +26,7 @@ import com.graphhopper.routing.util.EdgeFilter; import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.routing.util.TraversalMode; +import com.graphhopper.routing.weighting.SpeedWeighting; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.routing.weighting.custom.CustomModelParser; import com.graphhopper.storage.BaseGraph; @@ -56,7 +57,6 @@ * @author Peter Karich */ public class PrepareLandmarksTest { - private BooleanEncodedValue accessEnc; private DecimalEncodedValue speedEnc; private EncodingManager encodingManager; private BaseGraph graph; @@ -64,9 +64,8 @@ public class PrepareLandmarksTest { @BeforeEach public void setUp() { - accessEnc = new SimpleBooleanEncodedValue("access", true); speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); - encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc).add(Subnetwork.create("car")).build(); + encodingManager = new EncodingManager.Builder().add(speedEnc).add(Subnetwork.create("car")).build(); graph = new BaseGraph.Builder(encodingManager).create(); tm = TraversalMode.NODE_BASED; } @@ -86,11 +85,11 @@ public void testLandmarkStorageAndRouting() { // do not connect first with last column! double speed = 20 + rand.nextDouble() * 30; if (wIndex + 1 < width) - graph.edge(node, node + 1).set(accessEnc, true, true).set(speedEnc, speed); + graph.edge(node, node + 1).set(speedEnc, speed); // avoid dead ends if (hIndex + 1 < height) - graph.edge(node, node + width).set(accessEnc, true, true).set(speedEnc, speed); + graph.edge(node, node + width).set(speedEnc, speed); updateDistancesFor(graph, node, -hIndex / 50.0, wIndex / 50.0); } @@ -100,7 +99,7 @@ public void testLandmarkStorageAndRouting() { index.prepareIndex(); int lm = 5, activeLM = 2; - Weighting weighting = CustomModelParser.createFastestWeighting(accessEnc, speedEnc, encodingManager); + Weighting weighting = new SpeedWeighting(speedEnc); LMConfig lmConfig = new LMConfig("car", weighting); LandmarkStorage store = new LandmarkStorage(graph, encodingManager, dir, lmConfig, lm); store.setMinimumNodes(2); @@ -115,13 +114,13 @@ public void testLandmarkStorageAndRouting() { assertEquals(0, store.getFromWeight(0, 224)); double factor = store.getFactor(); - assertEquals(4671, Math.round(store.getFromWeight(0, 47) * factor)); - assertEquals(3640, Math.round(store.getFromWeight(0, 52) * factor)); + assertEquals(1297, Math.round(store.getFromWeight(0, 47) * factor)); + assertEquals(1011, Math.round(store.getFromWeight(0, 52) * factor)); long weight1_224 = store.getFromWeight(1, 224); - assertEquals(5525, Math.round(weight1_224 * factor)); + assertEquals(1535, Math.round(weight1_224 * factor)); long weight1_47 = store.getFromWeight(1, 47); - assertEquals(921, Math.round(weight1_47 * factor)); + assertEquals(256, Math.round(weight1_47 * factor)); // grid is symmetric assertEquals(weight1_224, store.getToWeight(1, 224)); @@ -156,7 +155,7 @@ public void testLandmarkStorageAndRouting() { assertEquals(expectedPath.getWeight(), path.getWeight(), .1); assertEquals(expectedPath.calcNodes(), path.calcNodes()); - assertEquals(expectedAlgo.getVisitedNodes() - 73, oneDirAlgoWithLandmarks.getVisitedNodes()); + assertEquals(expectedAlgo.getVisitedNodes() - 136, oneDirAlgoWithLandmarks.getVisitedNodes()); // landmarks with bidir A* RoutingAlgorithm biDirAlgoWithLandmarks = new LMRoutingAlgorithmFactory(lms).createAlgo(graph, weighting, @@ -164,7 +163,7 @@ public void testLandmarkStorageAndRouting() { path = biDirAlgoWithLandmarks.calcPath(41, 183); assertEquals(expectedPath.getWeight(), path.getWeight(), .1); assertEquals(expectedPath.calcNodes(), path.calcNodes()); - assertEquals(expectedAlgo.getVisitedNodes() - 95, biDirAlgoWithLandmarks.getVisitedNodes()); + assertEquals(expectedAlgo.getVisitedNodes() - 163, biDirAlgoWithLandmarks.getVisitedNodes()); // landmarks with A* and a QueryGraph. We expect slightly less optimal as two more cycles needs to be traversed // due to the two more virtual nodes but this should not harm in practise @@ -179,18 +178,19 @@ public void testLandmarkStorageAndRouting() { expectedPath = expectedAlgo.calcPath(fromSnap.getClosestNode(), toSnap.getClosestNode()); assertEquals(expectedPath.getWeight(), path.getWeight(), .1); assertEquals(expectedPath.calcNodes(), path.calcNodes()); - assertEquals(expectedAlgo.getVisitedNodes() - 73, qGraphOneDirAlgo.getVisitedNodes()); + // todonow: why did visited nodes change? + assertEquals(expectedAlgo.getVisitedNodes() - 136, qGraphOneDirAlgo.getVisitedNodes()); } @Test public void testStoreAndLoad() { - GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(80_000)); - GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(80_000)); + graph.edge(0, 1).setDistance(80_000).set(speedEnc, 60); + graph.edge(1, 2).setDistance(80_000).set(speedEnc, 60); String fileStr = "./target/tmp-lm"; Helper.removeDir(new File(fileStr)); Directory dir = new RAMDirectory(fileStr, true).create(); - Weighting weighting = CustomModelParser.createFastestWeighting(accessEnc, speedEnc, encodingManager); + Weighting weighting = new SpeedWeighting(speedEnc); LMConfig lmConfig = new LMConfig("car", weighting); PrepareLandmarks plm = new PrepareLandmarks(dir, graph, encodingManager, lmConfig, 2); plm.setMinimumNodes(2); @@ -201,7 +201,7 @@ public void testStoreAndLoad() { assertEquals(Arrays.toString(new int[]{ 2, 0 }), Arrays.toString(plm.getLandmarkStorage().getLandmarks(1))); - assertEquals(4800, Math.round(plm.getLandmarkStorage().getFromWeight(0, 1) * expectedFactor)); + assertEquals(1333, Math.round(plm.getLandmarkStorage().getFromWeight(0, 1) * expectedFactor)); dir = new RAMDirectory(fileStr, true); plm = new PrepareLandmarks(dir, graph, encodingManager, lmConfig, 2); @@ -210,7 +210,7 @@ public void testStoreAndLoad() { assertEquals(Arrays.toString(new int[]{ 2, 0 }), Arrays.toString(plm.getLandmarkStorage().getLandmarks(1))); - assertEquals(4800, Math.round(plm.getLandmarkStorage().getFromWeight(0, 1) * expectedFactor)); + assertEquals(1333, Math.round(plm.getLandmarkStorage().getFromWeight(0, 1) * expectedFactor)); Helper.removeDir(new File(fileStr)); } diff --git a/core/src/test/java/com/graphhopper/routing/querygraph/QueryGraphTest.java b/core/src/test/java/com/graphhopper/routing/querygraph/QueryGraphTest.java index 7ad565238d0..a884105e9f7 100644 --- a/core/src/test/java/com/graphhopper/routing/querygraph/QueryGraphTest.java +++ b/core/src/test/java/com/graphhopper/routing/querygraph/QueryGraphTest.java @@ -25,6 +25,7 @@ import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.routing.weighting.SpeedWeighting; import com.graphhopper.routing.weighting.Weighting; +import com.graphhopper.search.KVStorage; import com.graphhopper.storage.*; import com.graphhopper.storage.index.LocationIndexTree; import com.graphhopper.storage.index.Snap; @@ -34,10 +35,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -985,6 +983,89 @@ public void testTotalEdgeCount() { queryGraph.getEdgeIteratorState(i, Integer.MIN_VALUE).toString()).collect(Collectors.joining(","))); } + @Test + public void testExternalEV() { + IntEncodedValue intEnc = new IntEncodedValueImpl("my_int", 3, true); + EnumEncodedValue enumEnc = RoadClass.create(); + BooleanEncodedValue roadClassLincEnc = RoadClassLink.create(); + ExternalBooleanEncodedValue externalEnc = new ExternalBooleanEncodedValue("my_ext", true); + EncodingManager encodingManager = EncodingManager.start() + .add(intEnc).add(enumEnc).add(roadClassLincEnc).add(externalEnc) + .build(); + BaseGraph g = new BaseGraph.Builder(encodingManager).create(); + g.edge(0, 1).set(intEnc, 5).set(enumEnc, RoadClass.BRIDLEWAY).set(roadClassLincEnc, true).set(externalEnc, false, true); + g.edge(1, 2).set(intEnc, 2).set(enumEnc, RoadClass.CYCLEWAY).set(roadClassLincEnc, false).set(externalEnc, false, true); + g.edge(2, 3).set(intEnc, 7).set(enumEnc, RoadClass.PRIMARY).set(roadClassLincEnc, true).set(externalEnc, true, false); + g.edge(3, 4).set(intEnc, 1).set(enumEnc, RoadClass.MOTORWAY).set(roadClassLincEnc, false).set(externalEnc, true, false); + + NodeAccess na = g.getNodeAccess(); + na.setNode(0, 50.00, 10.00); + na.setNode(1, 50.10, 10.10); + na.setNode(2, 50.20, 10.20); + na.setNode(3, 50.30, 10.30); + na.setNode(4, 50.40, 10.40); + + LocationIndexTree locationIndex = new LocationIndexTree(g, g.getDirectory()); + locationIndex.prepareIndex(); + + Snap snap1 = locationIndex.findClosest(50.05, 10.05, EdgeFilter.ALL_EDGES); + Snap snap2 = locationIndex.findClosest(50.35, 10.35, EdgeFilter.ALL_EDGES); + + QueryGraph queryGraph = QueryGraph.create(g, snap1, snap2); + assertEquals(7, queryGraph.getNodes()); + assertEquals(4, queryGraph.getBaseGraph().getEdges()); + assertEquals(8, queryGraph.getVirtualEdges().size()); + + // fetching EVs from the virtual edge yields the values of the corresponding original edge + EdgeIteratorState virt05 = queryGraph.getEdgeIteratorState(4, 5); + assertFalse(queryGraph.getEdgeIteratorState(0, 1).get(externalEnc)); + assertTrue(queryGraph.getEdgeIteratorState(0, 1).getReverse(externalEnc)); + assertFalse(virt05.get(externalEnc)); + assertTrue(virt05.getReverse(externalEnc)); + + assertEquals(RoadClass.MOTORWAY, queryGraph.getEdgeIteratorState(3, 4).get(enumEnc)); + EdgeIteratorState virt64 = queryGraph.getEdgeIteratorState(7, 4); + assertEquals(6, virt64.getBaseNode()); + assertEquals(4, virt64.getAdjNode()); + assertEquals(RoadClass.MOTORWAY, virt64.get(enumEnc)); + assertTrue(virt64.get(externalEnc)); + assertFalse(virt64.getReverse(externalEnc)); + } + + @Test + public void directedKeyValues() { + NodeAccess na = g.getNodeAccess(); + na.setNode(0, 1, 0); + na.setNode(1, 1, 2.5); + ArrayList kvs = new ArrayList<>(); + kvs.add(new KVStorage.KeyValue("a", "hello", true, false)); + kvs.add(new KVStorage.KeyValue("b", "world", false, true)); + EdgeIteratorState origEdge = g.edge(0, 1).setDistance(10).set(speedEnc, 60, 60).setKeyValues(kvs); + + // keyValues List stays the same + assertEquals(origEdge.getKeyValues().toString(), origEdge.detach(true).getKeyValues().toString()); + // determine if edge is reverse via origEdge.get(EdgeIteratorState.REVERSE_STATE) + + // but getValue is sensitive to direction + assertEquals("hello", origEdge.getValue("a")); + assertNull(origEdge.detach(true).getValue("a")); + assertEquals("world", origEdge.detach(true).getValue("b")); + assertNull(origEdge.getValue("b")); + + LocationIndexTree index = new LocationIndexTree(g, new RAMDirectory()); + index.prepareIndex(); + Snap snap = index.findClosest(1.01, 0.7, EdgeFilter.ALL_EDGES); + QueryGraph queryGraph = lookup(snap); + EdgeIteratorState edge0ToSnap = queryGraph.getEdgeIteratorState(1, 2); + + assertEquals(edge0ToSnap.getKeyValues().toString(), edge0ToSnap.detach(true).getKeyValues().toString()); + + assertEquals("hello", edge0ToSnap.getValue("a")); + assertNull(edge0ToSnap.detach(true).getValue("a")); + assertEquals("world", edge0ToSnap.detach(true).getValue("b")); + assertNull(edge0ToSnap.getValue("b")); + } + private QueryGraph lookup(Snap res) { return lookup(Collections.singletonList(res)); } @@ -992,5 +1073,4 @@ private QueryGraph lookup(Snap res) { private QueryGraph lookup(List snaps) { return QueryGraph.create(g, snaps); } - } diff --git a/core/src/test/java/com/graphhopper/routing/util/EncodingManagerTest.java b/core/src/test/java/com/graphhopper/routing/util/EncodingManagerTest.java index 2ecac791e91..23b1a5a17b4 100644 --- a/core/src/test/java/com/graphhopper/routing/util/EncodingManagerTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/EncodingManagerTest.java @@ -17,10 +17,7 @@ */ package com.graphhopper.routing.util; -import com.graphhopper.routing.ev.DecimalEncodedValueImpl; -import com.graphhopper.routing.ev.RoadAccess; -import com.graphhopper.routing.ev.VehicleAccess; -import com.graphhopper.routing.ev.VehicleSpeed; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.parsers.BikeAccessParser; import com.graphhopper.routing.util.parsers.CarAccessParser; import com.graphhopper.routing.util.parsers.FootAccessParser; @@ -39,10 +36,11 @@ public class EncodingManagerTest { @Test public void testSupportFords() { EncodingManager manager = new EncodingManager.Builder() - .add(VehicleEncodedValues.car(new PMap())) - .add(VehicleEncodedValues.bike(new PMap())) - .add(VehicleEncodedValues.foot(new PMap())). - build(); + .add(VehicleAccess.create("car")) + .add(VehicleAccess.create("bike")) + .add(VehicleAccess.create("foot")) + .add(Roundabout.create()) + .build(); // 1) default -> no block fords assertFalse(new CarAccessParser(manager, new PMap()).isBlockFords()); diff --git a/core/src/test/java/com/graphhopper/routing/util/MaxSpeedCalculatorTest.java b/core/src/test/java/com/graphhopper/routing/util/MaxSpeedCalculatorTest.java index b854e36672d..e5986186279 100644 --- a/core/src/test/java/com/graphhopper/routing/util/MaxSpeedCalculatorTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/MaxSpeedCalculatorTest.java @@ -241,6 +241,23 @@ public void testFwdOnly() { assertEquals(100, edge.getReverse(maxSpeedEnc), 1); } + @Test + public void testDifferentStates() { + ReaderWay way = new ReaderWay(0L); + way.setTag("country", Country.USA); + way.setTag("highway", "primary"); + + way.setTag("country_state", State.US_CA); + EdgeIteratorState edge1 = createEdge(way); + way.setTag("country_state", State.US_FL); + EdgeIteratorState edge2 = createEdge(way); + + calc.fillMaxSpeed(graph, em); + + assertEquals(106, edge1.get(maxSpeedEnc)); + assertEquals(90, edge2.get(maxSpeedEnc)); + } + @Test public void testRawAccess_RuralIsDefault_IfNoUrbanDensityWasSet() { Map tags = new HashMap<>(); diff --git a/core/src/test/java/com/graphhopper/routing/util/SlopeCalculatorTest.java b/core/src/test/java/com/graphhopper/routing/util/SlopeCalculatorTest.java index 0a66a3b2811..099ed407bea 100644 --- a/core/src/test/java/com/graphhopper/routing/util/SlopeCalculatorTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/SlopeCalculatorTest.java @@ -31,7 +31,7 @@ void simpleElevation() { assertEquals(-Math.round(2.0 / 210 * 100), averageEnc.getDecimal(true, edgeId, edgeIntAccess), 1e-3); assertEquals(Math.round(1.75 / 105 * 100), maxEnc.getDecimal(false, edgeId, edgeIntAccess), 1e-3); - assertEquals(Math.round(1.75 / 105 * 100), maxEnc.getDecimal(true, edgeId, edgeIntAccess), 1e-3); + assertEquals(-Math.round(1.75 / 105 * 100), maxEnc.getDecimal(true, edgeId, edgeIntAccess), 1e-3); } @Test @@ -53,12 +53,12 @@ public void testAveragingOfMaxSlope() { way.setTag("point_list", pointList); creator.handleWayTags(edgeId, intAccess, way, IntsRef.EMPTY); - assertEquals(Math.round(8.0 / 210 * 100), maxEnc.getDecimal(false, edgeId, intAccess), 1e-3); + assertEquals(-Math.round(8.0 / 210 * 100), maxEnc.getDecimal(false, edgeId, intAccess), 1e-3); assertEquals(Math.round(8.0 / 210 * 100), maxEnc.getDecimal(true, edgeId, intAccess), 1e-3); } @Test - public void test2() { + public void testMaxSlopeLargerThanMaxStorableDecimal() { PointList pointList = new PointList(5, true); pointList.add(47.7281561, 11.9993135, 1163.0); pointList.add(47.7282782, 11.9991944, 1163.0); @@ -66,14 +66,37 @@ public void test2() { ReaderWay way = new ReaderWay(1); way.setTag("point_list", pointList); ArrayEdgeIntAccess intAccess = new ArrayEdgeIntAccess(1); - int edgeId = 0; + DecimalEncodedValue averageEnc = AverageSlope.create(); DecimalEncodedValue maxEnc = MaxSlope.create(); new EncodingManager.Builder().add(averageEnc).add(maxEnc).build(); SlopeCalculator creator = new SlopeCalculator(maxEnc, averageEnc); + int edgeId = 0; creator.handleWayTags(edgeId, intAccess, way, IntsRef.EMPTY); assertEquals(31, maxEnc.getDecimal(false, edgeId, intAccess), 1e-3); - assertEquals(31, averageEnc.getDecimal(false, edgeId, intAccess), 1e-3); + assertEquals(-31, averageEnc.getDecimal(true, edgeId, intAccess), 1e-3); + } + + @Test + public void testMaxSlopeSmallerThanMinStorableDecimal() { + PointList pointList = new PointList(5, true); + pointList.add(47.7283135, 11.9991135, 1178.0); + pointList.add(47.7282782, 11.9991944, 1163.0); + pointList.add(47.7281561, 11.9993135, 1163.0); + + ReaderWay way = new ReaderWay(1); + way.setTag("point_list", pointList); + ArrayEdgeIntAccess intAccess = new ArrayEdgeIntAccess(1); + + DecimalEncodedValue averageEnc = AverageSlope.create(); + DecimalEncodedValue maxEnc = MaxSlope.create(); + new EncodingManager.Builder().add(averageEnc).add(maxEnc).build(); + SlopeCalculator creator = new SlopeCalculator(maxEnc, averageEnc); + + int edgeId = 0; + creator.handleWayTags(edgeId, intAccess, way, IntsRef.EMPTY); + assertEquals(-31, maxEnc.getDecimal(false, edgeId, intAccess), 1e-3); + assertEquals(31, averageEnc.getDecimal(true, edgeId, intAccess), 1e-3); } @Test diff --git a/core/src/test/java/com/graphhopper/routing/util/SnapPreventionEdgeFilterTest.java b/core/src/test/java/com/graphhopper/routing/util/SnapPreventionEdgeFilterTest.java index ae55b75487e..dccf95567dd 100644 --- a/core/src/test/java/com/graphhopper/routing/util/SnapPreventionEdgeFilterTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/SnapPreventionEdgeFilterTest.java @@ -17,7 +17,7 @@ public class SnapPreventionEdgeFilterTest { @Test public void accept() { EdgeFilter trueFilter = edgeState -> true; - EncodingManager em = new EncodingManager.Builder().build(); + EncodingManager em = new EncodingManager.Builder().add(RoadClass.create()).add(RoadEnvironment.create()).build(); EnumEncodedValue rcEnc = em.getEnumEncodedValue(RoadClass.KEY, RoadClass.class); EnumEncodedValue reEnc = em.getEnumEncodedValue(RoadEnvironment.KEY, RoadEnvironment.class); SnapPreventionEdgeFilter filter = new SnapPreventionEdgeFilter(trueFilter, rcEnc, reEnc, Arrays.asList("motorway", "ferry")); @@ -35,4 +35,4 @@ public void accept() { edge.set(rcEnc, RoadClass.MOTORWAY); assertFalse(filter.accept(edge)); } -} \ No newline at end of file +} diff --git a/core/src/test/java/com/graphhopper/routing/util/countryrules/CountryRuleTest.java b/core/src/test/java/com/graphhopper/routing/util/countryrules/CountryRuleTest.java index 274dcfd5f67..def53ce0ea1 100644 --- a/core/src/test/java/com/graphhopper/routing/util/countryrules/CountryRuleTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/countryrules/CountryRuleTest.java @@ -19,9 +19,11 @@ import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.RoadAccess; +import com.graphhopper.routing.ev.Toll; import com.graphhopper.routing.util.TransportationMode; import com.graphhopper.routing.util.countryrules.europe.AustriaCountryRule; import com.graphhopper.routing.util.countryrules.europe.GermanyCountryRule; +import com.graphhopper.routing.util.countryrules.europe.HungaryCountryRule; import org.junit.jupiter.api.Test; @@ -44,6 +46,25 @@ void austria() { assertEquals(RoadAccess.DESTINATION, rule.getAccess(createReaderWay("living_street"), TransportationMode.CAR, RoadAccess.YES)); } + @Test + void hungary() { + HungaryCountryRule rule = new HungaryCountryRule(); + assertEquals(RoadAccess.YES, rule.getAccess(createReaderWay("primary"), TransportationMode.CAR, RoadAccess.YES)); + assertEquals(RoadAccess.DESTINATION, rule.getAccess(createReaderWay("living_street"), TransportationMode.CAR, RoadAccess.YES)); + assertEquals(RoadAccess.YES, rule.getAccess(createReaderWay("living_street"), TransportationMode.BIKE, RoadAccess.YES)); + assertEquals(RoadAccess.PRIVATE, rule.getAccess(createReaderWay("living_street"), TransportationMode.CAR, RoadAccess.PRIVATE)); + assertEquals(RoadAccess.PRIVATE, rule.getAccess(createReaderWay("living_street"), TransportationMode.BIKE, RoadAccess.PRIVATE)); + assertEquals(Toll.ALL, rule.getToll(createReaderWay("motorway"), Toll.MISSING)); + assertEquals(Toll.HGV, rule.getToll(createReaderWay("trunk"), Toll.MISSING)); + assertEquals(Toll.HGV, rule.getToll(createReaderWay("primary"), Toll.MISSING)); + assertEquals(Toll.MISSING, rule.getToll(createReaderWay("secondary"), Toll.MISSING)); + assertEquals(Toll.MISSING, rule.getToll(createReaderWay("residential"), Toll.MISSING)); + assertEquals(Toll.MISSING, rule.getToll(createReaderWay("service"), Toll.MISSING)); + assertEquals(Toll.ALL, rule.getToll(createReaderWay("service"), Toll.ALL)); + assertEquals(Toll.HGV, rule.getToll(createReaderWay("service"), Toll.HGV)); + assertEquals(Toll.NO, rule.getToll(createReaderWay("service"), Toll.NO)); + } + private ReaderWay createReaderWay(String highway) { ReaderWay readerWay = new ReaderWay(123L); readerWay.setTag("highway", highway); diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/AbstractBikeTagParserTester.java b/core/src/test/java/com/graphhopper/routing/util/parsers/AbstractBikeTagParserTester.java index d3b3a8d6b6d..3f379d7e2e6 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/AbstractBikeTagParserTester.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/AbstractBikeTagParserTester.java @@ -24,7 +24,6 @@ import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.routing.util.OSMParsers; import com.graphhopper.routing.util.PriorityCode; -import com.graphhopper.routing.util.VehicleTagParsers; import com.graphhopper.storage.IntsRef; import com.graphhopper.util.Helper; import com.graphhopper.util.PMap; @@ -54,12 +53,12 @@ public abstract class AbstractBikeTagParserTester { @BeforeEach public void setUp() { encodingManager = createEncodingManager(); - VehicleTagParsers parsers = createBikeTagParsers(encodingManager, new PMap("block_fords=true")); - accessParser = (BikeCommonAccessParser) parsers.getAccessParser(); - speedParser = (BikeCommonAverageSpeedParser) parsers.getSpeedParser(); - priorityParser = (BikeCommonPriorityParser) parsers.getPriorityParser(); + accessParser = createAccessParser(encodingManager, new PMap("block_fords=true")); + speedParser = createAverageSpeedParser(encodingManager); + priorityParser = createPriorityParser(encodingManager); osmParsers = new OSMParsers() .addRelationTagParser(relConfig -> new OSMBikeNetworkTagParser(encodingManager.getEnumEncodedValue(BikeNetwork.KEY, RouteNetwork.class), relConfig)) + .addRelationTagParser(relConfig -> new OSMMtbNetworkTagParser(encodingManager.getEnumEncodedValue(MtbNetwork.KEY, RouteNetwork.class), relConfig)) .addWayTagParser(new OSMSmoothnessParser(encodingManager.getEnumEncodedValue(Smoothness.KEY, Smoothness.class))) .addWayTagParser(accessParser).addWayTagParser(speedParser).addWayTagParser(priorityParser); priorityEnc = priorityParser.getPriorityEnc(); @@ -69,7 +68,11 @@ public void setUp() { protected abstract EncodingManager createEncodingManager(); - protected abstract VehicleTagParsers createBikeTagParsers(EncodedValueLookup lookup, PMap pMap); + protected abstract BikeCommonAccessParser createAccessParser(EncodedValueLookup lookup, PMap pMap); + + protected abstract BikeCommonAverageSpeedParser createAverageSpeedParser(EncodedValueLookup lookup); + + protected abstract BikeCommonPriorityParser createPriorityParser(EncodedValueLookup lookup); protected void assertPriority(PriorityCode expectedPrio, ReaderWay way) { IntsRef relFlags = osmParsers.handleRelationTags(new ReaderRelation(0), osmParsers.createRelationFlags()); @@ -214,22 +217,6 @@ public void testAccess() { way.setTag("bicycle", "no"); assertTrue(accessParser.getAccess(way).canSkip()); - DateFormat simpleDateFormat = Helper.createFormatter("yyyy MMM dd"); - - way.clearTags(); - way.setTag("highway", "road"); - way.setTag("bicycle:conditional", "no @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); - assertTrue(accessParser.getAccess(way).canSkip()); - - way.setTag("bicycle", "yes"); // the conditional tag even overrules "yes" - assertTrue(accessParser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "road"); - way.setTag("access", "no"); - way.setTag("bicycle:conditional", "yes @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); - assertTrue(accessParser.getAccess(way).isWay()); - way.clearTags(); way.setTag("highway", "track"); way.setTag("vehicle", "forestry"); @@ -527,7 +514,7 @@ public void testFerries() { @Test void privateAndFords() { // defaults: do not block fords, block private - BikeCommonAccessParser bike = (BikeCommonAccessParser) createBikeTagParsers(encodingManager, new PMap()).getAccessParser(); + BikeCommonAccessParser bike = createAccessParser(encodingManager, new PMap()); assertFalse(bike.isBlockFords()); assertTrue(bike.restrictedValues.contains("private")); assertFalse(bike.intendedValues.contains("private")); @@ -536,7 +523,7 @@ void privateAndFords() { assertTrue(bike.isBarrier(node)); // block fords, unblock private - bike = (BikeCommonAccessParser) createBikeTagParsers(encodingManager, new PMap("block_fords=true|block_private=false")).getAccessParser(); + bike = createAccessParser(encodingManager, new PMap("block_fords=true|block_private=false")); assertTrue(bike.isBlockFords()); assertFalse(bike.restrictedValues.contains("private")); assertTrue(bike.intendedValues.contains("private")); @@ -664,7 +651,6 @@ public void testOneway() { private void assertAccess(ReaderWay way, boolean fwd, boolean bwd) { EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); int edge = 0; - IntsRef edgeFlags = new IntsRef(1); IntsRef relationFlags = new IntsRef(1); accessParser.handleWayTags(edge, edgeIntAccess, way, relationFlags); if (fwd) assertTrue(accessEnc.getBool(false, edge, edgeIntAccess)); diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/BikeTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/BikeTagParserTest.java index ab2baba8ca2..39a9d830553 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/BikeTagParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/BikeTagParserTest.java @@ -23,8 +23,6 @@ import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.routing.util.PriorityCode; -import com.graphhopper.routing.util.VehicleEncodedValues; -import com.graphhopper.routing.util.VehicleTagParsers; import com.graphhopper.storage.IntsRef; import com.graphhopper.util.PMap; import org.junit.jupiter.api.Test; @@ -42,12 +40,31 @@ public class BikeTagParserTest extends AbstractBikeTagParserTester { @Override protected EncodingManager createEncodingManager() { - return new EncodingManager.Builder().add(VehicleEncodedValues.bike(new PMap())).build(); + return new EncodingManager.Builder() + .add(VehicleAccess.create("bike")) + .add(VehicleSpeed.create("bike", 4, 2, false)) + .add(VehiclePriority.create("bike", 4, PriorityCode.getFactor(1), false)) + .add(Roundabout.create()) + .add(Smoothness.create()) + .add(FerrySpeed.create()) + .add(RouteNetwork.create(BikeNetwork.KEY)) + .add(RouteNetwork.create(MtbNetwork.KEY)) + .build(); } @Override - protected VehicleTagParsers createBikeTagParsers(EncodedValueLookup lookup, PMap pMap) { - return VehicleTagParsers.bike(lookup, pMap); + protected BikeCommonAccessParser createAccessParser(EncodedValueLookup lookup, PMap pMap) { + return new BikeAccessParser(lookup, pMap); + } + + @Override + protected BikeCommonAverageSpeedParser createAverageSpeedParser(EncodedValueLookup lookup) { + return new BikeAverageSpeedParser(lookup); + } + + @Override + protected BikeCommonPriorityParser createPriorityParser(EncodedValueLookup lookup) { + return new BikePriorityParser(lookup); } @Test @@ -223,7 +240,7 @@ public void testSpeedAndPriority() { way.setTag("highway", "track"); way.setTag("bicycle", "yes"); way.setTag("surface", "fine_gravel"); - assertPriorityAndSpeed(UNCHANGED, 18, way); + assertPriorityAndSpeed(UNCHANGED, 14, way); way.setTag("surface", "unknown_surface"); assertPriorityAndSpeed(UNCHANGED, PUSHING_SECTION_SPEED, way); @@ -231,7 +248,7 @@ public void testSpeedAndPriority() { way.clearTags(); way.setTag("highway", "primary"); way.setTag("surface", "fine_gravel"); - assertPriorityAndSpeed(BAD, 18, way); + assertPriorityAndSpeed(BAD, 14, way); way.clearTags(); way.setTag("highway", "track"); @@ -317,6 +334,8 @@ public void testCycleway() { way.setTag("highway", "primary"); way.setTag("cycleway:right", "lane"); assertPriority(SLIGHT_PREFER, way); + way.setTag("cycleway:left", "no"); + assertPriority(SLIGHT_PREFER, way); way.clearTags(); way.setTag("highway", "primary"); @@ -596,4 +615,38 @@ public void testAvoidMotorway() { assertPriority(REACH_DESTINATION, osmWay); } + @Test + public void temporalAccess() { + int edgeId = 0; + ArrayEdgeIntAccess access = new ArrayEdgeIntAccess(1); + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "primary"); + way.setTag("access:conditional", "no @ (May - June)"); + accessParser.handleWayTags(edgeId, access, way, null); + assertTrue(accessEnc.getBool(false, edgeId, access)); + + access = new ArrayEdgeIntAccess(1); + way = new ReaderWay(1); + way.setTag("highway", "primary"); + way.setTag("bicycle:conditional", "no @ (May - June)"); + accessParser.handleWayTags(edgeId, access, way, null); + assertTrue(accessEnc.getBool(false, edgeId, access)); + + access = new ArrayEdgeIntAccess(1); + way = new ReaderWay(1); + way.setTag("highway", "primary"); + way.setTag("bicycle", "no"); + way.setTag("access:conditional", "yes @ (May - June)"); + accessParser.handleWayTags(edgeId, access, way, null); + assertFalse(accessEnc.getBool(false, edgeId, access)); + + access = new ArrayEdgeIntAccess(1); + way = new ReaderWay(1); + way.setTag("highway", "primary"); + way.setTag("access", "no"); + way.setTag("bicycle:conditional", "yes @ (May - June)"); + accessParser.handleWayTags(edgeId, access, way, null); + assertTrue(accessEnc.getBool(false, edgeId, access)); + } + } diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/CarTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/CarTagParserTest.java index 6cb932e56d9..43611cb0738 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/CarTagParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/CarTagParserTest.java @@ -19,13 +19,10 @@ import com.graphhopper.reader.ReaderNode; import com.graphhopper.reader.ReaderWay; -import com.graphhopper.reader.osm.conditional.DateRangeParser; import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FerrySpeedCalculator; import com.graphhopper.routing.util.PriorityCode; import com.graphhopper.routing.util.WayAccess; -import com.graphhopper.storage.IntsRef; import com.graphhopper.util.Helper; import com.graphhopper.util.PMap; import org.junit.jupiter.api.Test; @@ -44,7 +41,7 @@ public class CarTagParserTest { private final EncodingManager em = createEncodingManager("car"); final CarAccessParser parser = createParser(em, new PMap("block_fords=true")); - final CarAverageSpeedParser speedParser = new CarAverageSpeedParser(em, new PMap("block_fords=true")); + final CarAverageSpeedParser speedParser = new CarAverageSpeedParser(em); private final BooleanEncodedValue roundaboutEnc = em.getBooleanEncodedValue(Roundabout.KEY); private final BooleanEncodedValue accessEnc = parser.getAccessEnc(); private final DecimalEncodedValue avSpeedEnc = speedParser.getAverageSpeedEnc(); @@ -59,13 +56,13 @@ private EncodingManager createEncodingManager(String carName) { .add(VehiclePriority.create("bike", 4, PriorityCode.getFactor(1), false)) .add(RouteNetwork.create(BikeNetwork.KEY)) .add(Smoothness.create()) + .add(Roundabout.create()) + .add(FerrySpeed.create()) .build(); } CarAccessParser createParser(EncodedValueLookup lookup, PMap properties) { - CarAccessParser carTagParser = new CarAccessParser(lookup, properties); - carTagParser.init(new DateRangeParser()); - return carTagParser; + return new CarAccessParser(lookup, properties); } @Test @@ -144,25 +141,6 @@ public void testAccess() { way.setTag("motor_vehicle", "emergency"); assertTrue(parser.getAccess(way).canSkip()); - DateFormat simpleDateFormat = Helper.createFormatter("yyyy MMM dd"); - - way.clearTags(); - way.setTag("highway", "road"); - way.setTag("access:conditional", "no @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "road"); - way.setTag("access", "no"); - way.setTag("access:conditional", "yes @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); - assertTrue(parser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "road"); - way.setTag("access", "yes"); - way.setTag("access:conditional", "no @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); - assertTrue(parser.getAccess(way).canSkip()); - way.clearTags(); way.setTag("highway", "service"); way.setTag("service", "emergency_access"); @@ -192,7 +170,6 @@ public void testFordAccess() { assertTrue(parser.isBarrier(node)); CarAccessParser tmpParser = new CarAccessParser(em, new PMap("block_fords=false")); - tmpParser.init(new DateRangeParser()); assertTrue(tmpParser.getAccess(way).isWay()); assertFalse(tmpParser.isBarrier(node)); } @@ -608,9 +585,10 @@ public void testMaxValue() { EncodingManager em = new EncodingManager.Builder() .add(new SimpleBooleanEncodedValue("car_access", true)) .add(smallFactorSpeedEnc) + .add(FerrySpeed.create()) .addTurnCostEncodedValue(TurnCost.create("car", 1)) .build(); - CarAverageSpeedParser speedParser = new CarAverageSpeedParser(em, new PMap()); + CarAverageSpeedParser speedParser = new CarAverageSpeedParser(em); ReaderWay way = new ReaderWay(1); way.setTag("highway", "motorway_link"); way.setTag("maxspeed", "60 mph"); @@ -652,7 +630,6 @@ public void testCombination() { way.setTag("sac_scale", "hiking"); BikeAccessParser bikeParser = new BikeAccessParser(em, new PMap()); - bikeParser.init(new DateRangeParser()); assertEquals(WayAccess.CAN_SKIP, parser.getAccess(way)); assertNotEquals(WayAccess.CAN_SKIP, bikeParser.getAccess(way)); EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); @@ -690,12 +667,58 @@ public void testIssue_1256() { EncodingManager lowFactorEm = new EncodingManager.Builder() .add(new SimpleBooleanEncodedValue(VehicleAccess.key("car"), true)) .add(lowFactorSpeedEnc) + .add(FerrySpeed.create()) .build(); edgeIntAccess = new ArrayEdgeIntAccess(lowFactorEm.getIntsForFlags()); - new CarAverageSpeedParser(lowFactorEm, new PMap()).handleWayTags(edgeId, edgeIntAccess, way); + new CarAverageSpeedParser(lowFactorEm).handleWayTags(edgeId, edgeIntAccess, way); assertEquals(1, lowFactorSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), .1); } + @Test + public void temporalAccess() { + int edgeId = 0; + ArrayEdgeIntAccess access = new ArrayEdgeIntAccess(1); + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "primary"); + way.setTag("access:conditional", "no @ (May - June)"); + parser.handleWayTags(edgeId, access, way, null); + assertTrue(accessEnc.getBool(false, edgeId, access)); + + access = new ArrayEdgeIntAccess(1); + way = new ReaderWay(1); + way.setTag("highway", "primary"); + way.setTag("motorcar:conditional", "no @ (May - June)"); + parser.handleWayTags(edgeId, access, way, null); + assertTrue(accessEnc.getBool(false, edgeId, access)); + + access = new ArrayEdgeIntAccess(1); + way = new ReaderWay(1); + way.setTag("highway", "primary"); + way.setTag("motorcar", "no"); + way.setTag("access:conditional", "yes @ (May - June)"); + parser.handleWayTags(edgeId, access, way, null); + assertFalse(accessEnc.getBool(false, edgeId, access)); + + // car should ignore unrelated conditional access restrictions of e.g. bicycle + access = new ArrayEdgeIntAccess(1); + way = new ReaderWay(1); + way.setTag("highway", "primary"); + way.setTag("vehicle", "no"); + way.setTag("bicycle:conditional", "yes @ (May - June)"); + parser.handleWayTags(edgeId, access, way, null); + assertFalse(accessEnc.getBool(false, edgeId, access)); + + // Ignore access restriction if there is a *higher* priority temporal restriction that *could* lift it. + // But this is independent on the date! + access = new ArrayEdgeIntAccess(1); + way = new ReaderWay(1); + way.setTag("highway", "primary"); + way.setTag("access", "no"); + way.setTag("motorcar:conditional", "yes @ (May - June)"); + parser.handleWayTags(edgeId, access, way, null); + assertTrue(accessEnc.getBool(false, edgeId, access)); + } + @ParameterizedTest @ValueSource(strings = {"mofa", "moped", "motorcar", "motor_vehicle", "motorcycle"}) void footway_etc_not_allowed_despite_vehicle_yes(String vehicle) { diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/FootTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/FootTagParserTest.java index ec2fb9bceef..7dd4def46e9 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/FootTagParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/FootTagParserTest.java @@ -19,19 +19,19 @@ import com.graphhopper.reader.ReaderNode; import com.graphhopper.reader.ReaderWay; -import com.graphhopper.reader.osm.conditional.DateRangeParser; import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.AccessFilter; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FerrySpeedCalculator; import com.graphhopper.routing.util.PriorityCode; import com.graphhopper.storage.BaseGraph; -import com.graphhopper.storage.IntsRef; import com.graphhopper.util.*; import org.junit.jupiter.api.Test; import java.text.DateFormat; -import java.util.*; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; import static com.graphhopper.routing.util.parsers.FootAverageSpeedParser.MEAN_SPEED; import static com.graphhopper.routing.util.parsers.FootAverageSpeedParser.SLOW_SPEED; @@ -52,13 +52,13 @@ public class FootTagParserTest { .add(footAccessEnc).add(footAvgSpeedEnc).add(footPriorityEnc).add(RouteNetwork.create(FootNetwork.KEY)) .add(bikeAccessEnc).add(bikeAvgSpeedEnc).add(RouteNetwork.create(BikeNetwork.KEY)) .add(carAccessEnc).add(carAvSpeedEnc) + .add(FerrySpeed.create()) .build(); private final FootAccessParser accessParser = new FootAccessParser(encodingManager, new PMap()); - private final FootAverageSpeedParser speedParser = new FootAverageSpeedParser(encodingManager, new PMap()); - private final FootPriorityParser prioParser = new FootPriorityParser(encodingManager, new PMap()); + private final FootAverageSpeedParser speedParser = new FootAverageSpeedParser(encodingManager); + private final FootPriorityParser prioParser = new FootPriorityParser(encodingManager); public FootTagParserTest() { - accessParser.init(new DateRangeParser()); } @Test @@ -224,22 +224,6 @@ public void testAccess() { way.setTag("foot", "designated"); way.setTag("access", "private"); assertTrue(accessParser.getAccess(way).canSkip()); - - DateFormat simpleDateFormat = Helper.createFormatter("yyyy MMM dd"); - - way.clearTags(); - way.setTag("highway", "footway"); - way.setTag("access:conditional", "no @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); - assertTrue(accessParser.getAccess(way).canSkip()); - - way.setTag("foot", "yes"); // the conditional tag even overrules "yes" - assertTrue(accessParser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "footway"); - way.setTag("access", "no"); - way.setTag("access:conditional", "yes @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); - assertTrue(accessParser.getAccess(way).isWay()); } @Test @@ -342,6 +326,20 @@ public void testPriority() { way.setTag("highway", "primary"); assertEquals(PriorityCode.AVOID.getValue(), prioParser.handlePriority(way, null)); + way.setTag("sidewalk", "yes"); + assertEquals(PriorityCode.AVOID.getValue(), prioParser.handlePriority(way, null)); + + way.setTag("sidewalk", "no"); + assertEquals(PriorityCode.BAD.getValue(), prioParser.handlePriority(way, null)); + + way.clearTags(); + way.setTag("highway", "tertiary"); + assertEquals(PriorityCode.UNCHANGED.getValue(), prioParser.handlePriority(way, null)); + + // tertiary without sidewalk is roughly like primary with sidewalk + way.setTag("sidewalk", "no"); + assertEquals(PriorityCode.AVOID.getValue(), prioParser.handlePriority(way, null)); + way.setTag("highway", "track"); way.setTag("bicycle", "official"); assertEquals(PriorityCode.SLIGHT_AVOID.getValue(), prioParser.handlePriority(way, null)); @@ -355,11 +353,6 @@ public void testPriority() { way.setTag("foot", "designated"); assertEquals(PriorityCode.PREFER.getValue(), prioParser.handlePriority(way, null)); - way.clearTags(); - way.setTag("highway", "primary"); - way.setTag("sidewalk", "yes"); - assertEquals(PriorityCode.SLIGHT_AVOID.getValue(), prioParser.handlePriority(way, null)); - way.clearTags(); way.setTag("highway", "cycleway"); way.setTag("sidewalk", "no"); @@ -372,11 +365,15 @@ public void testPriority() { assertEquals(PriorityCode.SLIGHT_AVOID.getValue(), prioParser.handlePriority(way, null)); way.clearTags(); - way.setTag("highway", "trunk"); + way.setTag("highway", "secondary"); + assertEquals(PriorityCode.AVOID.getValue(), prioParser.handlePriority(way, null)); + way.setTag("highway", "trunk"); // secondary should be better to mostly avoid trunk e.g. here 46.9889,10.5664->47.0172,10.6059 + assertEquals(PriorityCode.BAD.getValue(), prioParser.handlePriority(way, null)); + way.setTag("sidewalk", "no"); - assertEquals(PriorityCode.VERY_BAD.getValue(), prioParser.handlePriority(way, null)); + assertEquals(PriorityCode.REACH_DESTINATION.getValue(), prioParser.handlePriority(way, null)); way.setTag("sidewalk", "none"); - assertEquals(PriorityCode.VERY_BAD.getValue(), prioParser.handlePriority(way, null)); + assertEquals(PriorityCode.REACH_DESTINATION.getValue(), prioParser.handlePriority(way, null)); way.clearTags(); way.setTag("highway", "residential"); @@ -545,4 +542,37 @@ public void maxSpeed() { // note that this test made more sense when we used encoders that defined a max speed. assertEquals(16, speedEnc.getNextStorableValue(15)); } + @Test + public void temporalAccess() { + int edgeId = 0; + ArrayEdgeIntAccess access = new ArrayEdgeIntAccess(1); + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "primary"); + way.setTag("access:conditional", "no @ (May - June)"); + accessParser.handleWayTags(edgeId, access, way, null); + assertTrue(footAccessEnc.getBool(false, edgeId, access)); + + access = new ArrayEdgeIntAccess(1); + way = new ReaderWay(1); + way.setTag("highway", "primary"); + way.setTag("foot:conditional", "no @ (May - June)"); + accessParser.handleWayTags(edgeId, access, way, null); + assertTrue(footAccessEnc.getBool(false, edgeId, access)); + + access = new ArrayEdgeIntAccess(1); + way = new ReaderWay(1); + way.setTag("highway", "primary"); + way.setTag("foot", "no"); + way.setTag("access:conditional", "yes @ (May - June)"); + accessParser.handleWayTags(edgeId, access, way, null); + assertFalse(footAccessEnc.getBool(false, edgeId, access)); + + access = new ArrayEdgeIntAccess(1); + way = new ReaderWay(1); + way.setTag("highway", "primary"); + way.setTag("access", "no"); + way.setTag("foot:conditional", "yes @ (May - June)"); + accessParser.handleWayTags(edgeId, access, way, null); + assertTrue(footAccessEnc.getBool(false, edgeId, access)); + } } diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/HikeCustomModelTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/HikeCustomModelTest.java index 5e1d69fd27f..92e3520187c 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/HikeCustomModelTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/HikeCustomModelTest.java @@ -1,46 +1,46 @@ package com.graphhopper.routing.util.parsers; -import com.graphhopper.jackson.Jackson; import com.graphhopper.reader.ReaderWay; +import com.graphhopper.reader.osm.conditional.DateRangeParser; import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.routing.util.OSMParsers; -import com.graphhopper.routing.util.VehicleEncodedValues; -import com.graphhopper.routing.util.VehicleTagParsers; +import com.graphhopper.routing.util.PriorityCode; import com.graphhopper.routing.weighting.custom.CustomModelParser; import com.graphhopper.routing.weighting.custom.CustomWeighting; import com.graphhopper.storage.BaseGraph; -import com.graphhopper.util.*; +import com.graphhopper.util.CustomModel; +import com.graphhopper.util.EdgeIteratorState; +import com.graphhopper.util.GHUtility; +import com.graphhopper.util.PMap; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.io.File; -import java.io.InputStreamReader; - import static org.junit.jupiter.api.Assertions.assertEquals; public class HikeCustomModelTest { private EncodingManager em; - private DecimalEncodedValue roadsSpeedEnc; private OSMParsers parsers; @BeforeEach public void setup() { IntEncodedValue hikeRating = HikeRating.create(); em = new EncodingManager.Builder(). - add(VehicleEncodedValues.roads(new PMap())). - add(VehicleEncodedValues.foot(new PMap())). + add(VehicleAccess.create("foot")). + add(VehicleSpeed.create("foot", 4, 1, false)). + add(VehiclePriority.create("foot", 4, PriorityCode.getFactor(1), false)). + add(FerrySpeed.create()). + add(RouteNetwork.create(FootNetwork.KEY)). + add(RoadAccess.create()). add(hikeRating).build(); - roadsSpeedEnc = em.getDecimalEncodedValue(VehicleSpeed.key("roads")); parsers = new OSMParsers(). addWayTagParser(new OSMHikeRatingParser(hikeRating)); - for (TagParser p : VehicleTagParsers.foot(em, new PMap()).getTagParsers()) - parsers.addWayTagParser(p); - for (TagParser p : VehicleTagParsers.roads(em, new PMap()).getTagParsers()) - parsers.addWayTagParser(p); + parsers.addWayTagParser(new FootAccessParser(em, new PMap())); + parsers.addWayTagParser(new FootAverageSpeedParser(em)); + parsers.addWayTagParser(new FootPriorityParser(em)); } EdgeIteratorState createEdge(ReaderWay way) { @@ -51,45 +51,36 @@ EdgeIteratorState createEdge(ReaderWay way) { return edge; } - static CustomModel getCustomModel(String file) { - try { - String string = Helper.readJSONFileWithoutComments(new InputStreamReader(GHUtility.class.getResourceAsStream("/com/graphhopper/custom_models/" + file))); - return Jackson.newObjectMapper().readValue(string, CustomModel.class); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - @Test public void testHikePrivate() { + CustomModel cm = GHUtility.loadCustomModelFromJar("hike.json"); ReaderWay way = new ReaderWay(0L); way.setTag("highway", "track"); EdgeIteratorState edge = createEdge(way); - CustomWeighting.Parameters p = CustomModelParser.createWeightingParameters(getCustomModel("hike.json"), em, roadsSpeedEnc, 30, null); + CustomWeighting.Parameters p = CustomModelParser.createWeightingParameters(cm, em); assertEquals(1.2, p.getEdgeToPriorityMapping().get(edge, false), 0.01); way.setTag("motor_vehicle", "private"); edge = createEdge(way); - p = CustomModelParser.createWeightingParameters(getCustomModel("hike.json"), em, roadsSpeedEnc, 30, null); + p = CustomModelParser.createWeightingParameters(cm, em); assertEquals(1.2, p.getEdgeToPriorityMapping().get(edge, false), 0.01); way.setTag("sac_scale", "alpine_hiking"); edge = createEdge(way); - p = CustomModelParser.createWeightingParameters(getCustomModel("hike.json"), em, roadsSpeedEnc, 30, null); + p = CustomModelParser.createWeightingParameters(cm, em); assertEquals(1.2, p.getEdgeToPriorityMapping().get(edge, false), 0.01); - assertEquals(2, p.getEdgeToSpeedMapping().get(edge, false), 0.01); + assertEquals(1.5, p.getEdgeToSpeedMapping().get(edge, false), 0.01); way = new ReaderWay(0L); way.setTag("highway", "track"); way.setTag("access", "private"); edge = createEdge(way); - p = CustomModelParser.createWeightingParameters(getCustomModel("hike.json"), em, roadsSpeedEnc, 30, null); + p = CustomModelParser.createWeightingParameters(cm, em); assertEquals(0, p.getEdgeToPriorityMapping().get(edge, false), 0.01); way.setTag("sac_scale", "alpine_hiking"); edge = createEdge(way); - p = CustomModelParser.createWeightingParameters(getCustomModel("hike.json"), em, roadsSpeedEnc, 30, null); - // TODO this would be wrong tagging but still we should exclude the way - will be fixed with #2819 - // assertEquals(0, p.getEdgeToPriorityMapping().get(edge, false), 0.01); + p = CustomModelParser.createWeightingParameters(cm, em); + assertEquals(0, p.getEdgeToPriorityMapping().get(edge, false), 0.01); } } diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/ModeAccessParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/ModeAccessParserTest.java new file mode 100644 index 00000000000..28e595031b9 --- /dev/null +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/ModeAccessParserTest.java @@ -0,0 +1,244 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.*; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.TransportationMode; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class ModeAccessParserTest { + + private final EncodingManager em = new EncodingManager.Builder().add(Roundabout.create()).add(BusAccess.create()).build(); + private final ModeAccessParser parser = new ModeAccessParser(TransportationMode.BUS, em.getBooleanEncodedValue(BusAccess.KEY), em.getBooleanEncodedValue(Roundabout.KEY), List.of()); + private final BooleanEncodedValue busAccessEnc = em.getBooleanEncodedValue(BusAccess.KEY); + + @Test + public void testAccess() { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "primary"); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + int edgeId = 0; + parser.handleWayTags(edgeId, edgeIntAccess, way, null); + assertTrue(busAccessEnc.getBool(false, edgeId, edgeIntAccess)); + assertTrue(busAccessEnc.getBool(true, edgeId, edgeIntAccess)); + } + + @Test + public void testPrivate() { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "primary"); + way.setTag("access", "private"); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + int edgeId = 0; + parser.handleWayTags(edgeId, edgeIntAccess, way, null); + assertTrue(busAccessEnc.getBool(false, edgeId, edgeIntAccess)); + assertTrue(busAccessEnc.getBool(true, edgeId, edgeIntAccess)); + } + + @Test + public void testOneway() { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "primary"); + way.setTag("oneway", "yes"); + + int edgeId = 0; + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + parser.handleWayTags(edgeId, edgeIntAccess, way, null); + assertTrue(busAccessEnc.getBool(false, edgeId, edgeIntAccess)); + assertFalse(busAccessEnc.getBool(true, edgeId, edgeIntAccess)); + + way.clearTags(); + way.setTag("highway", "tertiary"); + way.setTag("vehicle:forward", "no"); + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + parser.handleWayTags(edgeId, edgeIntAccess, way, null); + assertFalse(busAccessEnc.getBool(false, edgeId, edgeIntAccess)); + assertTrue(busAccessEnc.getBool(true, edgeId, edgeIntAccess)); + + way.clearTags(); + way.setTag("highway", "tertiary"); + way.setTag("vehicle:backward", "no"); + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + parser.handleWayTags(edgeId, edgeIntAccess, way, null); + assertTrue(busAccessEnc.getBool(false, edgeId, edgeIntAccess)); + assertFalse(busAccessEnc.getBool(true, edgeId, edgeIntAccess)); + + way.setTag("bus:backward", "yes"); + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + parser.handleWayTags(edgeId, edgeIntAccess, way, null); + assertTrue(busAccessEnc.getBool(false, edgeId, edgeIntAccess)); + assertTrue(busAccessEnc.getBool(true, edgeId, edgeIntAccess)); + + way.clearTags(); + way.setTag("highway", "tertiary"); + way.setTag("vehicle:backward", "yes"); + way.setTag("bus:backward", "no"); + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + parser.handleWayTags(edgeId, edgeIntAccess, way, null); + assertTrue(busAccessEnc.getBool(false, edgeId, edgeIntAccess)); + assertFalse(busAccessEnc.getBool(true, edgeId, edgeIntAccess)); + } + + @Test + public void testBusYes() { + EdgeIntAccess access = new ArrayEdgeIntAccess(1); + ReaderWay way = new ReaderWay(0); + way.setTag("motor_vehicle", "no"); + way.setTag("highway", "tertiary"); + int edgeId = 0; + parser.handleWayTags(edgeId, access, way, null); + assertFalse(busAccessEnc.getBool(false, edgeId, access)); + + access = new ArrayEdgeIntAccess(1); + way.setTag("bus", "yes"); + parser.handleWayTags(edgeId, access, way, null); + assertTrue(busAccessEnc.getBool(false, edgeId, access)); + + access = new ArrayEdgeIntAccess(1); + way = new ReaderWay(0); + way.setTag("highway", "primary"); + way.setTag("oneway", "yes"); + way.setTag("oneway:bus", "no"); + parser.handleWayTags(edgeId, access, way, null); + assertTrue(busAccessEnc.getBool(false, edgeId, access)); + assertTrue(busAccessEnc.getBool(true, edgeId, access)); + + access = new ArrayEdgeIntAccess(1); + way.setTag("oneway:psv", "no"); + way.setTag("oneway:bus", "yes"); + parser.handleWayTags(edgeId, access, way, null); + assertTrue(busAccessEnc.getBool(false, edgeId, access)); + assertFalse(busAccessEnc.getBool(true, edgeId, access)); + } + + @Test + public void testBusNo() { + EdgeIntAccess access = new ArrayEdgeIntAccess(1); + ReaderWay way = new ReaderWay(0); + way.setTag("highway", "tertiary"); + int edgeId = 0; + parser.handleWayTags(edgeId, access, way, null); + assertTrue(busAccessEnc.getBool(false, edgeId, access)); + + access = new ArrayEdgeIntAccess(1); + way.setTag("bus", "no"); + parser.handleWayTags(edgeId, access, way, null); + assertFalse(busAccessEnc.getBool(false, edgeId, access)); + } + + @Test + public void testBusNodeAccess() { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "secondary"); + way.setTag("gh:barrier_edge", true); + + Map nodeTags = new HashMap<>(); + nodeTags.put("access", "no"); + nodeTags.put("bus", "yes"); + way.setTag("node_tags", Arrays.asList(nodeTags, new HashMap<>())); + EdgeIntAccess access = new ArrayEdgeIntAccess(1); + int edgeId = 0; + parser.handleWayTags(edgeId, access, way, null); + assertTrue(busAccessEnc.getBool(false, edgeId, access)); + + nodeTags = new HashMap<>(); + nodeTags.put("access", "yes"); + nodeTags.put("bus", "no"); + way.setTag("node_tags", Arrays.asList(nodeTags)); + access = new ArrayEdgeIntAccess(1); + parser.handleWayTags(edgeId, access, way, null); + assertFalse(busAccessEnc.getBool(false, edgeId, access)); + + // ensure that allowing node tags (bus=yes) do not unblock the inaccessible way + way.setTag("access", "no"); + nodeTags = new HashMap<>(); + nodeTags.put("bus", "yes"); + way.setTag("node_tags", Arrays.asList(nodeTags, new HashMap<>())); + access = new ArrayEdgeIntAccess(1); + parser.handleWayTags(edgeId, access, way, null); + assertFalse(busAccessEnc.getBool(false, edgeId, access)); + } + + @Test + public void testPsvYes() { + EdgeIntAccess access = new ArrayEdgeIntAccess(1); + ReaderWay way = new ReaderWay(0); + way.setTag("motor_vehicle", "no"); + way.setTag("highway", "tertiary"); + int edgeId = 0; + parser.handleWayTags(edgeId, access, way, null); + assertFalse(busAccessEnc.getBool(false, edgeId, access)); + + access = new ArrayEdgeIntAccess(1); + way.setTag("psv", "yes"); + parser.handleWayTags(edgeId, access, way, null); + assertTrue(busAccessEnc.getBool(false, edgeId, access)); + + access = new ArrayEdgeIntAccess(1); + way.setTag("psv", "yes"); + parser.handleWayTags(edgeId, access, way, null); + assertTrue(busAccessEnc.getBool(false, edgeId, access)); + } + + @Test + public void testMotorcycleYes() { + BooleanEncodedValue mcAccessEnc = new SimpleBooleanEncodedValue("motorcycle_access", true); + EncodingManager mcEM = new EncodingManager.Builder().add(mcAccessEnc).add(Roundabout.create()).build(); + ModeAccessParser mcParser = new ModeAccessParser(TransportationMode.MOTORCYCLE, mcAccessEnc, mcEM.getBooleanEncodedValue(Roundabout.KEY), List.of()); + + int edgeId = 0; + EdgeIntAccess access = new ArrayEdgeIntAccess(1); + ReaderWay way = new ReaderWay(0); + way.setTag("motor_vehicle", "no"); + way.setTag("highway", "tertiary"); + mcParser.handleWayTags(edgeId, access, way, null); + assertFalse(mcAccessEnc.getBool(false, edgeId, access)); + + access = new ArrayEdgeIntAccess(1); + way.setTag("motorcycle", "yes"); + mcParser.handleWayTags(0, access, way, null); + assertTrue(mcAccessEnc.getBool(false, edgeId, access)); + } + + @Test + public void temporalAccess() { + int edgeId = 0; + ArrayEdgeIntAccess access = new ArrayEdgeIntAccess(1); + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "primary"); + way.setTag("access:conditional", "no @ (May - June)"); + parser.handleWayTags(edgeId, access, way, null); + assertTrue(busAccessEnc.getBool(false, edgeId, access)); + + access = new ArrayEdgeIntAccess(1); + way = new ReaderWay(1); + way.setTag("highway", "primary"); + way.setTag("psv:conditional", "no @ (May - June)"); + parser.handleWayTags(edgeId, access, way, null); + assertTrue(busAccessEnc.getBool(false, edgeId, access)); + + access = new ArrayEdgeIntAccess(1); + way = new ReaderWay(1); + way.setTag("highway", "primary"); + way.setTag("psv", "no"); + way.setTag("access:conditional", "yes @ (May - June)"); + parser.handleWayTags(edgeId, access, way, null); + assertFalse(busAccessEnc.getBool(false, edgeId, access)); + + access = new ArrayEdgeIntAccess(1); + way = new ReaderWay(1); + way.setTag("highway", "primary"); + way.setTag("access", "no"); + way.setTag("psv:conditional", "yes @ (May - June)"); + parser.handleWayTags(edgeId, access, way, null); + assertTrue(busAccessEnc.getBool(false, edgeId, access)); + } +} diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/MountainBikeTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/MountainBikeTagParserTest.java index acc46340994..c978340b173 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/MountainBikeTagParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/MountainBikeTagParserTest.java @@ -20,10 +20,10 @@ import com.graphhopper.reader.ReaderNode; import com.graphhopper.reader.ReaderRelation; import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.EncodedValueLookup; +import com.graphhopper.reader.osm.conditional.DateRangeParser; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.VehicleEncodedValues; -import com.graphhopper.routing.util.VehicleTagParsers; +import com.graphhopper.routing.util.PriorityCode; import com.graphhopper.util.PMap; import org.junit.jupiter.api.Test; @@ -35,12 +35,31 @@ public class MountainBikeTagParserTest extends AbstractBikeTagParserTester { @Override protected EncodingManager createEncodingManager() { - return new EncodingManager.Builder().add(VehicleEncodedValues.mountainbike(new PMap())).build(); + return new EncodingManager.Builder() + .add(VehicleAccess.create("mtb")) + .add(VehicleSpeed.create("mtb", 4, 2, false)) + .add(VehiclePriority.create("mtb", 4, PriorityCode.getFactor(1), false)) + .add(Roundabout.create()) + .add(Smoothness.create()) + .add(FerrySpeed.create()) + .add(RouteNetwork.create(BikeNetwork.KEY)) + .add(RouteNetwork.create(MtbNetwork.KEY)) + .build(); } @Override - protected VehicleTagParsers createBikeTagParsers(EncodedValueLookup lookup, PMap pMap) { - return VehicleTagParsers.mtb(lookup, pMap); + protected BikeCommonAccessParser createAccessParser(EncodedValueLookup lookup, PMap pMap) { + return new MountainBikeAccessParser(lookup, pMap); + } + + @Override + protected BikeCommonAverageSpeedParser createAverageSpeedParser(EncodedValueLookup lookup) { + return new MountainBikeAverageSpeedParser(lookup); + } + + @Override + protected BikeCommonPriorityParser createPriorityParser(EncodedValueLookup lookup) { + return new MountainBikePriorityParser(lookup); } @Test @@ -134,26 +153,26 @@ public void testSacScale() { } @Test - public void testHandleWayTagsInfluencedByRelation() { + public void testHandleWayTagsInfluencedByBikeAndMtbRelation() { ReaderWay osmWay = new ReaderWay(1); osmWay.setTag("highway", "track"); ReaderRelation osmRel = new ReaderRelation(1); // unchanged - assertPriorityAndSpeed(PREFER, 18, osmWay); + assertPriorityAndSpeed(PREFER, 18, osmWay, osmRel); // relation code is PREFER osmRel.setTag("route", "bicycle"); osmRel.setTag("network", "lcn"); - assertPriorityAndSpeed(PREFER, 18, osmWay); + assertPriorityAndSpeed(BEST, 18, osmWay, osmRel); // relation code is PREFER osmRel.setTag("network", "rcn"); - assertPriorityAndSpeed(PREFER, 18, osmWay); + assertPriorityAndSpeed(PREFER, 18, osmWay, osmRel); // relation code is PREFER osmRel.setTag("network", "ncn"); - assertPriorityAndSpeed(PREFER, 18, osmWay); + assertPriorityAndSpeed(PREFER, 18, osmWay, osmRel); // PREFER relation, but tertiary road // => no pushing section but road wayTypeCode and faster @@ -162,10 +181,33 @@ public void testHandleWayTagsInfluencedByRelation() { osmRel.setTag("route", "bicycle"); osmRel.setTag("network", "lcn"); - assertPriorityAndSpeed(PREFER, 18, osmWay); + assertPriorityAndSpeed(BEST, 18, osmWay, osmRel); + + osmWay.clearTags(); + osmRel.clearTags(); + osmWay.setTag("highway", "track"); + // unchanged + assertPriorityAndSpeed(PREFER, 18, osmWay, osmRel); + + osmRel.setTag("route", "mtb"); + osmRel.setTag("network", "lcn"); + assertPriorityAndSpeed(PREFER, 18, osmWay, osmRel); + + osmRel.setTag("network", "rcn"); + assertPriorityAndSpeed(PREFER, 18, osmWay, osmRel); + + osmRel.setTag("network", "ncn"); + assertPriorityAndSpeed(PREFER, 18, osmWay, osmRel); + + osmWay.clearTags(); + osmWay.setTag("highway", "tertiary"); + + osmRel.setTag("route", "mtb"); + osmRel.setTag("network", "lcn"); + assertPriorityAndSpeed(PREFER, 18, osmWay, osmRel); } - // Issue 407 : Always block kissing_gate execpt for mountainbikes + // Issue 407 : Always block kissing_gate except for mountainbikes @Test @Override public void testBarrierAccess() { diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMGetOffBikeParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMGetOffBikeParserTest.java index c3dc0a29ea8..422d6e29737 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMGetOffBikeParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMGetOffBikeParserTest.java @@ -1,13 +1,8 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.reader.ReaderWay; -import com.graphhopper.reader.osm.conditional.DateRangeParser; -import com.graphhopper.routing.ev.ArrayEdgeIntAccess; -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.EdgeIntAccess; -import com.graphhopper.routing.ev.GetOffBike; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.VehicleEncodedValues; import com.graphhopper.storage.IntsRef; import com.graphhopper.util.PMap; import org.junit.jupiter.api.Test; @@ -21,9 +16,8 @@ public class OSMGetOffBikeParserTest { private final OSMGetOffBikeParser getOffParser; public OSMGetOffBikeParserTest() { - EncodingManager em = new EncodingManager.Builder().add(offBikeEnc).add(VehicleEncodedValues.bike(new PMap()).getAccessEnc()).build(); + EncodingManager em = new EncodingManager.Builder().add(offBikeEnc).add(VehicleAccess.create("bike")).add(Roundabout.create()).build(); accessParser = new BikeAccessParser(em, new PMap()); - accessParser.init(new DateRangeParser()); getOffParser = new OSMGetOffBikeParser(offBikeEnc, accessParser.getAccessEnc()); } @@ -134,4 +128,4 @@ private boolean isGetOffBike(ReaderWay way) { getOffParser.handleWayTags(edgeId, edgeIntAccess, way, rel); return offBikeEnc.getBool(false, edgeId, edgeIntAccess); } -} \ No newline at end of file +} diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMTemporalAccessParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMTemporalAccessParserTest.java new file mode 100644 index 00000000000..c6489583190 --- /dev/null +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMTemporalAccessParserTest.java @@ -0,0 +1,85 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.ArrayEdgeIntAccess; +import com.graphhopper.routing.ev.CarTemporalAccess; +import com.graphhopper.routing.ev.EnumEncodedValue; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.storage.IntsRef; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class OSMTemporalAccessParserTest { + + private final EnumEncodedValue restricted = CarTemporalAccess.create(); + private final EncodingManager em = new EncodingManager.Builder().add(restricted).build(); + private final OSMTemporalAccessParser parser = new OSMTemporalAccessParser(CarTemporalAccess.CONDITIONALS, + (edgeId, access, b) -> restricted.setEnum(false, edgeId, access, b ? CarTemporalAccess.YES : CarTemporalAccess.NO), "2023-05-17"); + + @Test + public void testBasics() { + String today = "2023 May 17"; + ArrayEdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + assertEquals(CarTemporalAccess.MISSING, restricted.getEnum(false, edgeId, edgeIntAccess)); + + ReaderWay way = new ReaderWay(0L); + way.setTag("highway", "road"); + way.setTag("access:conditional", "no @ (" + today + ")"); + parser.handleWayTags(edgeId, edgeIntAccess, way, IntsRef.EMPTY); + assertEquals(CarTemporalAccess.NO, restricted.getEnum(false, edgeId, edgeIntAccess)); + + edgeIntAccess = new ArrayEdgeIntAccess(1); + way.setTag("access:conditional", "no @ ( 2023 Mar 23 - " + today + " )"); + parser.handleWayTags(edgeId, edgeIntAccess, way, IntsRef.EMPTY); + assertEquals(CarTemporalAccess.NO, restricted.getEnum(false, edgeId, edgeIntAccess)); + + edgeIntAccess = new ArrayEdgeIntAccess(1); + way.clearTags(); + way.setTag("highway", "road"); + way.setTag("access", "no"); + way.setTag("access:conditional", "yes @ (" + today + ")"); + parser.handleWayTags(edgeId, edgeIntAccess, way, IntsRef.EMPTY); + assertEquals(CarTemporalAccess.YES, restricted.getEnum(false, edgeId, edgeIntAccess)); + + // for now consider if seasonal range + edgeIntAccess = new ArrayEdgeIntAccess(1); + way.setTag("access:conditional", "no @ ( Mar 23 - Aug 23 )"); + parser.handleWayTags(edgeId, edgeIntAccess, way, IntsRef.EMPTY); + assertEquals(CarTemporalAccess.NO, restricted.getEnum(false, edgeId, edgeIntAccess)); + + // range does not match => inverse! + edgeIntAccess = new ArrayEdgeIntAccess(1); + way.setTag("access:conditional", "no @ ( Jun 23 - Aug 23 )"); + parser.handleWayTags(edgeId, edgeIntAccess, way, IntsRef.EMPTY); + assertEquals(CarTemporalAccess.YES, restricted.getEnum(false, edgeId, edgeIntAccess)); + + edgeIntAccess = new ArrayEdgeIntAccess(1); + way.setTag("access:conditional", "no @ ( 2023 Mar 23 )"); + parser.handleWayTags(edgeId, edgeIntAccess, way, IntsRef.EMPTY); + assertEquals(CarTemporalAccess.YES, restricted.getEnum(false, edgeId, edgeIntAccess)); + + edgeIntAccess = new ArrayEdgeIntAccess(1); + way.setTag("access:conditional", "yes @ Apr-Nov"); + parser.handleWayTags(edgeId, edgeIntAccess, way, IntsRef.EMPTY); + assertEquals(CarTemporalAccess.YES, restricted.getEnum(false, edgeId, edgeIntAccess)); + } + + @Test + public void testTaggingMistake() { + ArrayEdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + ReaderWay way = new ReaderWay(0L); + way.setTag("highway", "road"); + // ignore incomplete values + way.setTag("access:conditional", "no @ 2023 Mar-Oct"); + parser.handleWayTags(edgeId, edgeIntAccess, way, IntsRef.EMPTY); + assertEquals(CarTemporalAccess.MISSING, restricted.getEnum(false, edgeId, edgeIntAccess)); + + // here the "1" will be interpreted as year -> incorrect range + way.setTag("access:conditional", "no @ 1 Nov - 1 Mar"); + parser.handleWayTags(edgeId, edgeIntAccess, way, IntsRef.EMPTY); + assertEquals(CarTemporalAccess.MISSING, restricted.getEnum(false, edgeId, edgeIntAccess)); + } +} diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/RacingBikeTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/RacingBikeTagParserTest.java index 015d47977b6..b24ea9105ae 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/RacingBikeTagParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/RacingBikeTagParserTest.java @@ -19,11 +19,10 @@ import com.graphhopper.reader.ReaderRelation; import com.graphhopper.reader.ReaderWay; +import com.graphhopper.reader.osm.conditional.DateRangeParser; import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.routing.util.PriorityCode; -import com.graphhopper.routing.util.VehicleEncodedValues; -import com.graphhopper.routing.util.VehicleTagParsers; import com.graphhopper.util.PMap; import org.junit.jupiter.api.Test; @@ -42,12 +41,31 @@ public class RacingBikeTagParserTest extends AbstractBikeTagParserTester { @Override protected EncodingManager createEncodingManager() { - return new EncodingManager.Builder().add(VehicleEncodedValues.racingbike(new PMap())).build(); + return new EncodingManager.Builder() + .add(VehicleAccess.create("racingbike")) + .add(VehicleSpeed.create("racingbike", 4, 2, false)) + .add(VehiclePriority.create("racingbike", 4, PriorityCode.getFactor(1), false)) + .add(Roundabout.create()) + .add(Smoothness.create()) + .add(FerrySpeed.create()) + .add(RouteNetwork.create(BikeNetwork.KEY)) + .add(RouteNetwork.create(MtbNetwork.KEY)) + .build(); + } + + @Override + protected BikeCommonAccessParser createAccessParser(EncodedValueLookup lookup, PMap pMap) { + return (BikeCommonAccessParser) new RacingBikeAccessParser(lookup, pMap); + } + + @Override + protected BikeCommonAverageSpeedParser createAverageSpeedParser(EncodedValueLookup lookup) { + return new RacingBikeAverageSpeedParser(lookup); } @Override - protected VehicleTagParsers createBikeTagParsers(EncodedValueLookup lookup, PMap pMap) { - return VehicleTagParsers.racingbike(lookup, pMap); + protected BikeCommonPriorityParser createPriorityParser(EncodedValueLookup lookup) { + return new RacingBikePriorityParser(lookup); } @Test @@ -220,10 +238,11 @@ public void testPriority_avoidanceOfHighMaxSpeed() { .add(accessEnc).add(speedEnc).add(priorityEnc) .add(RouteNetwork.create(BikeNetwork.KEY)) .add(Smoothness.create()) + .add(FerrySpeed.create()) .build(); List parsers = Arrays.asList( - new RacingBikeAverageSpeedParser(encodingManager, new PMap()), - new RacingBikePriorityParser(encodingManager, new PMap()) + new RacingBikeAverageSpeedParser(encodingManager), + new RacingBikePriorityParser(encodingManager) ); ReaderWay osmWay = new ReaderWay(1); osmWay.setTag("highway", "tertiary"); diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/RestrictionSetterTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/RestrictionSetterTest.java index 2166e9d0276..40574f287fd 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/RestrictionSetterTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/RestrictionSetterTest.java @@ -16,8 +16,7 @@ import java.util.Arrays; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.*; public class RestrictionSetterTest { private static final IntArrayList NO_PATH = IntArrayList.from(); @@ -165,6 +164,83 @@ void viaWay_no_withOverlap_more_complex() { assertEquals(nodes(6, 7, 8, 9), calcPath(6, 9, turnRestrictionEnc)); } + @Test + void viaWay_common_via_edge_opposite_direction() { + // a b + // 0---1---2 + // |c + // 3---4---5 + // d e + int a = edge(0, 1); + int b = edge(1, 2); + int c = edge(1, 4); + int d = edge(3, 4); + int e = edge(4, 5); + + BooleanEncodedValue turnRestrictionEnc = createTurnRestrictionEnc("car"); + r.setRestrictions(Arrays.asList( + // A rather common case where u-turns between the a-b and d-e lanes are forbidden. + // Importantly, the via-edge c is used only once per direction so a single artificial edge is sufficient. + new Pair<>(GraphRestriction.way(b, c, e, nodes(1, 4)), RestrictionType.NO), + new Pair<>(GraphRestriction.way(d, c, a, nodes(4, 1)), RestrictionType.NO) + ), turnRestrictionEnc); + + assertEquals(nodes(0, 1, 2), calcPath(0, 2, turnRestrictionEnc)); + assertEquals(nodes(0, 1, 4, 5), calcPath(0, 5, turnRestrictionEnc)); + assertEquals(nodes(0, 1, 4, 3), calcPath(0, 3, turnRestrictionEnc)); + assertEquals(nodes(2, 1, 0), calcPath(2, 0, turnRestrictionEnc)); + assertEquals(nodes(2, 1, 4, 3), calcPath(2, 3, turnRestrictionEnc)); + assertEquals(NO_PATH, calcPath(2, 5, turnRestrictionEnc)); + assertEquals(NO_PATH, calcPath(3, 0, turnRestrictionEnc)); + assertEquals(nodes(3, 4, 1, 2), calcPath(3, 2, turnRestrictionEnc)); + assertEquals(nodes(3, 4, 5), calcPath(3, 5, turnRestrictionEnc)); + assertEquals(nodes(5, 4, 1, 0), calcPath(5, 0, turnRestrictionEnc)); + assertEquals(nodes(5, 4, 1, 2), calcPath(5, 2, turnRestrictionEnc)); + assertEquals(nodes(5, 4, 3), calcPath(5, 3, turnRestrictionEnc)); + } + + @Test + void viaWay_common_via_edge_opposite_direction_edge0() { + // a v b + // 0---1---2---3 + int v = edge(1, 2); + int a = edge(0, 1); + int b = edge(2, 3); + + BooleanEncodedValue turnRestrictionEnc = createTurnRestrictionEnc("car"); + IllegalStateException ex = assertThrows(IllegalStateException.class, () -> r.setRestrictions(Arrays.asList( + // This is rather academic, but for the special case where the via edge is edge 0 + // we cannot use two restrictions even though the edge is used in opposite directions. + new Pair<>(GraphRestriction.way(a, v, b, nodes(1, 2)), RestrictionType.NO), + new Pair<>(GraphRestriction.way(b, v, a, nodes(2, 1)), RestrictionType.NO) + ), turnRestrictionEnc)); + assertTrue(ex.getMessage().contains("We cannot deal with multiple via-way restrictions if the via-edge is edge 0")); + } + + @Test + void viaWay_common_via_edge_same_direction() { + // a b + // 0---1---2 + // |c + // 3---4---5 + // d e + int a = edge(0, 1); + int b = edge(1, 2); + int c = edge(1, 4); + int d = edge(3, 4); + int e = edge(4, 5); + + BooleanEncodedValue turnRestrictionEnc = createTurnRestrictionEnc("car"); + // Here edge c is used by both restrictions in the same direction. Supporting this + // with our current approach would require a second artificial edge. See #2907 + IllegalStateException ex = assertThrows(IllegalStateException.class, () -> + r.setRestrictions(Arrays.asList( + new Pair<>(GraphRestriction.way(a, c, d, nodes(1, 4)), RestrictionType.NO), + new Pair<>(GraphRestriction.way(b, c, e, nodes(1, 4)), RestrictionType.NO) + ), turnRestrictionEnc)); + assertTrue(ex.getMessage().contains("We cannot deal with multiple via-way restrictions that use the same via edge in the same direction"), ex.getMessage()); + } + @Test void viaWay_only() { // 0 @@ -185,7 +261,7 @@ void viaWay_only() { r.setRestrictions(Arrays.asList( new Pair<>(GraphRestriction.way(a, d, f, nodes(2, 5)), RestrictionType.ONLY), // we add a few more restrictions, because that happens a lot in real data - new Pair<>(GraphRestriction.way(c, d, g, nodes(2, 5)), RestrictionType.NO), + new Pair<>(GraphRestriction.node(d, 5, e), RestrictionType.NO), new Pair<>(GraphRestriction.node(e, 5, f), RestrictionType.NO) ), turnRestrictionEnc); // following the restriction is allowed of course @@ -194,7 +270,7 @@ void viaWay_only() { assertEquals(nodes(), calcPath(1, 3, turnRestrictionEnc)); // taking another turn after the first turn is not allowed either assertEquals(nodes(), calcPath(1, 4, turnRestrictionEnc)); - // coming from somewhere we can go anywhere + // coming from somewhere else we can go anywhere assertEquals(nodes(0, 2, 5, 6), calcPath(0, 6, turnRestrictionEnc)); assertEquals(nodes(0, 2, 5, 7), calcPath(0, 7, turnRestrictionEnc)); } @@ -213,8 +289,9 @@ void viaWay_only_twoRestrictionsSharingSameVia() { BooleanEncodedValue turnRestrictionEnc = createTurnRestrictionEnc("car"); assertThrows(IllegalStateException.class, () -> r.setRestrictions(Arrays.asList( // These are two 'only' via-way restrictions that share the same via way. A real-world example can - // be found in Rüdesheim am Rhein where vehicles either have to go straight or enter the ferry depending - // on the from-way, even though they use the same via way before. + // be found in Rüdesheim am Rhein (49.97645, 7.91309) where vehicles either have to go straight or enter the ferry depending + // on the from-way, even though they use the same via way before. This is the same + // problem we saw in #2907. // We have to make sure such cases are ignored already when we parse the OSM data. new Pair<>(GraphRestriction.way(a, c, d, nodes(1, 2)), RestrictionType.ONLY), new Pair<>(GraphRestriction.way(b, c, e, nodes(1, 2)), RestrictionType.ONLY) @@ -222,6 +299,37 @@ void viaWay_only_twoRestrictionsSharingSameVia() { ); } + @Test + void viaWay_only_twoRestrictionsSharingSameVia_different_directions() { + // a c d + // 0---1---2---3 + // |b |e + // 5--/ \--4 + int a = edge(0, 1); + int b = edge(5, 1); + int c = edge(1, 2); + int d = edge(2, 3); + int e = edge(2, 4); + BooleanEncodedValue turnRestrictionEnc = createTurnRestrictionEnc("car"); + r.setRestrictions(Arrays.asList( + // since the via-edge is used in opposite directions we can deal with these restrictions + new Pair<>(GraphRestriction.way(a, c, d, nodes(1, 2)), RestrictionType.ONLY), + new Pair<>(GraphRestriction.way(e, c, b, nodes(2, 1)), RestrictionType.ONLY) + ), turnRestrictionEnc); + assertEquals(nodes(0, 1, 2, 3), calcPath(0, 3, turnRestrictionEnc)); + assertEquals(NO_PATH, calcPath(0, 4, turnRestrictionEnc)); + assertEquals(NO_PATH, calcPath(0, 5, turnRestrictionEnc)); + assertEquals(nodes(3, 2, 1, 0), calcPath(3, 0, turnRestrictionEnc)); + assertEquals(nodes(3, 2, 4), calcPath(3, 4, turnRestrictionEnc)); + assertEquals(nodes(3, 2, 1, 5), calcPath(3, 5, turnRestrictionEnc)); + assertEquals(NO_PATH, calcPath(4, 0, turnRestrictionEnc)); + assertEquals(NO_PATH, calcPath(4, 3, turnRestrictionEnc)); + assertEquals(nodes(4, 2, 1, 5), calcPath(4, 5, turnRestrictionEnc)); + assertEquals(nodes(5, 1, 0), calcPath(5, 0, turnRestrictionEnc)); + assertEquals(nodes(5, 1, 2, 3), calcPath(5, 3, turnRestrictionEnc)); + assertEquals(nodes(5, 1, 2, 4), calcPath(5, 4, turnRestrictionEnc)); + } + private static BooleanEncodedValue createTurnRestrictionEnc(String name) { BooleanEncodedValue turnRestrictionEnc = TurnRestriction.create(name); turnRestrictionEnc.init(new EncodedValue.InitializerConfig()); diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/RoadsTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/RoadsTagParserTest.java deleted file mode 100644 index bb68170c981..00000000000 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/RoadsTagParserTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.graphhopper.routing.util.parsers; - -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.ArrayEdgeIntAccess; -import com.graphhopper.routing.ev.EdgeIntAccess; -import com.graphhopper.routing.ev.VehicleSpeed; -import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.VehicleEncodedValues; -import com.graphhopper.util.PMap; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -class RoadsTagParserTest { - - private final EncodingManager encodingManager = new EncodingManager.Builder().add(VehicleEncodedValues.roads(new PMap())).build(); - private final RoadsAverageSpeedParser parser; - - public RoadsTagParserTest() { - parser = new RoadsAverageSpeedParser(encodingManager, new PMap()); - } - - @Test - public void testSpeed() { - ReaderWay way = new ReaderWay(1); - EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); - int edgeId = 0; - parser.handleWayTags(edgeId, edgeIntAccess, way, null); - assertTrue(encodingManager.getDecimalEncodedValue(VehicleSpeed.key("roads")).getDecimal(false, edgeId, edgeIntAccess) > 200); - } - -} \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/TagParsingTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/TagParsingTest.java index 487dd396478..ef659085d2f 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/TagParsingTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/TagParsingTest.java @@ -20,7 +20,6 @@ import com.graphhopper.reader.ReaderRelation; import com.graphhopper.reader.ReaderWay; -import com.graphhopper.reader.osm.conditional.DateRangeParser; import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.routing.util.OSMParsers; @@ -54,9 +53,10 @@ public void testCombineRelations() { .add(bike2AccessEnc).add(bike2SpeedEnc).add(bike2PriorityEnc) .add(bikeNetworkEnc) .add(Smoothness.create()) + .add(RoadClass.create()) .build(); - BikePriorityParser bike1Parser = new BikePriorityParser(em, new PMap("name=bike1")); - BikePriorityParser bike2Parser = new BikePriorityParser(em, new PMap("name=bike2")) { + BikePriorityParser bike1Parser = new BikePriorityParser(bike1PriorityEnc, bike1SpeedEnc, bikeNetworkEnc); + BikePriorityParser bike2Parser = new BikePriorityParser(bike2PriorityEnc, bike2SpeedEnc, bikeNetworkEnc) { @Override public void handleWayTags(int edgeId, EdgeIntAccess intAccess, ReaderWay way, IntsRef relTags) { // accept less relations @@ -102,9 +102,10 @@ public void testMixBikeTypesAndRelationCombination() { .add(mtbAccessEnc).add(mtbSpeedEnc).add(mtbPriorityEnc) .add(bikeNetworkEnc) .add(Smoothness.create()) + .add(RoadClass.create()) .build(); - BikePriorityParser bikeTagParser = new BikePriorityParser(em, new PMap()); - MountainBikePriorityParser mtbTagParser = new MountainBikePriorityParser(em, new PMap()); + BikePriorityParser bikeTagParser = new BikePriorityParser(em); + MountainBikePriorityParser mtbTagParser = new MountainBikePriorityParser(em); OSMParsers osmParsers = new OSMParsers() .addRelationTagParser(relConfig -> new OSMBikeNetworkTagParser(bikeNetworkEnc, relConfig)) .addWayTagParser(new OSMRoadClassParser(em.getEnumEncodedValue(RoadClass.KEY, RoadClass.class))) @@ -138,6 +139,7 @@ public void testSharedEncodedValues() { .add(mtbAccessEnc).add(VehicleSpeed.create("mtb", 4, 2, false)).add(VehiclePriority.create("mtb", 4, PriorityCode.getFactor(1), false)) .add(RouteNetwork.create(FootNetwork.KEY)) .add(RouteNetwork.create(BikeNetwork.KEY)) + .add(Roundabout.create()) .add(Smoothness.create()) .build(); @@ -149,9 +151,6 @@ public void testSharedEncodedValues() { new BikeAccessParser(manager, new PMap()), new MountainBikeAccessParser(manager, new PMap()) ); - for (TagParser tagParser : tagParsers) - if (tagParser instanceof AbstractAccessParser) - ((AbstractAccessParser) tagParser).init(new DateRangeParser()); final ArrayEdgeIntAccess intAccess = new ArrayEdgeIntAccess(manager.getIntsForFlags()); int edgeId = 0; @@ -176,4 +175,4 @@ public void testSharedEncodedValues() { assertTrue(accessEnc.getBool(false, edgeId, intAccess)); } -} \ No newline at end of file +} diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/WheelchairTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/WheelchairTagParserTest.java deleted file mode 100644 index 60a3d77d88f..00000000000 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/WheelchairTagParserTest.java +++ /dev/null @@ -1,591 +0,0 @@ -/* - * 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.routing.util.parsers; - -import com.graphhopper.reader.ReaderNode; -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.reader.osm.conditional.DateRangeParser; -import com.graphhopper.routing.ev.*; -import com.graphhopper.routing.util.AccessFilter; -import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.PriorityCode; -import com.graphhopper.storage.BaseGraph; -import com.graphhopper.storage.NodeAccess; -import com.graphhopper.util.*; -import org.junit.jupiter.api.Test; - -import java.text.DateFormat; -import java.util.Date; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * @author don-philipe - */ -public class WheelchairTagParserTest { - private final BooleanEncodedValue wheelchairAccessEnc; - private final DecimalEncodedValue wheelchairAvSpeedEnc; - private final DecimalEncodedValue wheelchairPriorityEnc; - private final BooleanEncodedValue carAccessEnc; - private final DecimalEncodedValue carAvSpeedEnc; - private final EncodingManager encodingManager; - private final WheelchairAccessParser accessParser; - private final WheelchairAverageSpeedParser speedParser; - private final WheelchairPriorityParser prioParser; - - public WheelchairTagParserTest() { - wheelchairAccessEnc = VehicleAccess.create("wheelchair"); - wheelchairAvSpeedEnc = VehicleSpeed.create("wheelchair", 4, 1, true); - wheelchairPriorityEnc = VehiclePriority.create("wheelchair", 4, PriorityCode.getFactor(1), false); - carAccessEnc = VehicleAccess.create("car"); - carAvSpeedEnc = VehicleSpeed.create("car", 5, 5, false); - encodingManager = EncodingManager.start() - .add(wheelchairAccessEnc).add(wheelchairAvSpeedEnc).add(wheelchairPriorityEnc).add(RouteNetwork.create(FootNetwork.KEY)) - .add(carAccessEnc).add(carAvSpeedEnc) - .build(); - accessParser = new WheelchairAccessParser(encodingManager, new PMap()); - accessParser.init(new DateRangeParser()); - speedParser = new WheelchairAverageSpeedParser(encodingManager, new PMap()) { - @Override - public void applyWayTags(ReaderWay way, int edgeId, EdgeIntAccess edgeIntAccess) { - if (way.hasTag("point_list") && way.hasTag("edge_distance")) - super.applyWayTags(way, edgeId, edgeIntAccess); - } - }; - prioParser = new WheelchairPriorityParser(encodingManager, new PMap()); - } - - @Test - public void testGetSpeed() { - EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); - int edgeId = 0; - wheelchairAccessEnc.setBool(false, edgeId, edgeIntAccess, true); - wheelchairAccessEnc.setBool(true, edgeId, edgeIntAccess, true); - wheelchairAvSpeedEnc.setDecimal(false, edgeId, edgeIntAccess, 10); - assertEquals(10, wheelchairAvSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), .1); - } - - @Test - public void testCombined() { - BaseGraph g = new BaseGraph.Builder(encodingManager).create(); - EdgeIteratorState edge = g.edge(0, 1); - edge.set(wheelchairAvSpeedEnc, 10.0).set(wheelchairAccessEnc, true, true); - edge.set(carAvSpeedEnc, 100.0).set(carAccessEnc, true, false); - - assertEquals(10, edge.get(wheelchairAvSpeedEnc), .1); - assertTrue(edge.get(wheelchairAccessEnc)); - assertTrue(edge.getReverse(wheelchairAccessEnc)); - - assertEquals(100, edge.get(carAvSpeedEnc), .1); - assertTrue(edge.get(carAccessEnc)); - assertFalse(edge.getReverse(carAccessEnc)); - - EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); - int edgeId = 0; - wheelchairAvSpeedEnc.setDecimal(false, edgeId, edgeIntAccess, 10); - wheelchairAccessEnc.setBool(false, edgeId, edgeIntAccess, true); - wheelchairAccessEnc.setBool(true, edgeId, edgeIntAccess, true); - assertEquals(0, carAvSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), .1); - } - - @Test - public void testGraph() { - BaseGraph g = new BaseGraph.Builder(encodingManager).create(); - g.edge(0, 1).setDistance(10).set(wheelchairAvSpeedEnc, 10.0).set(wheelchairAccessEnc, true, true); - g.edge(0, 2).setDistance(10).set(wheelchairAvSpeedEnc, 5.0).set(wheelchairAccessEnc, true, true); - g.edge(1, 3).setDistance(10).set(wheelchairAvSpeedEnc, 10.0).set(wheelchairAccessEnc, true, true); - EdgeExplorer out = g.createEdgeExplorer(AccessFilter.outEdges(accessParser.getAccessEnc())); - assertEquals(GHUtility.asSet(1, 2), GHUtility.getNeighbors(out.setBaseNode(0))); - assertEquals(GHUtility.asSet(0, 3), GHUtility.getNeighbors(out.setBaseNode(1))); - assertEquals(GHUtility.asSet(0), GHUtility.getNeighbors(out.setBaseNode(2))); - } - - @Test - public void testAccess() { - ReaderWay way = new ReaderWay(1); - - way.setTag("highway", "motorway"); - way.setTag("sidewalk", "yes"); - assertTrue(accessParser.getAccess(way).isWay()); - way.setTag("sidewalk", "left"); - assertTrue(accessParser.getAccess(way).isWay()); - - way.setTag("sidewalk", "none"); - assertTrue(accessParser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "tertiary"); - way.setTag("sidewalk", "left"); - way.setTag("access", "private"); - assertTrue(accessParser.getAccess(way).canSkip()); - way.clearTags(); - - way.setTag("highway", "pedestrian"); - assertTrue(accessParser.getAccess(way).isWay()); - - way.setTag("highway", "footway"); - assertTrue(accessParser.getAccess(way).isWay()); - - way.setTag("highway", "platform"); - assertTrue(accessParser.getAccess(way).isWay()); - - way.setTag("highway", "motorway"); - assertTrue(accessParser.getAccess(way).canSkip()); - - way.setTag("bicycle", "official"); - assertTrue(accessParser.getAccess(way).canSkip()); - way.setTag("foot", "no"); - assertTrue(accessParser.getAccess(way).canSkip()); - - way.setTag("foot", "official"); - assertTrue(accessParser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "service"); - way.setTag("access", "no"); - assertTrue(accessParser.getAccess(way).canSkip()); - way.setTag("foot", "yes"); - assertTrue(accessParser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "service"); - way.setTag("vehicle", "no"); - assertTrue(accessParser.getAccess(way).isWay()); - way.setTag("foot", "no"); - assertTrue(accessParser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "tertiary"); - way.setTag("motorroad", "yes"); - assertTrue(accessParser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "cycleway"); - assertTrue(accessParser.getAccess(way).isWay()); - way.setTag("foot", "no"); - assertTrue(accessParser.getAccess(way).canSkip()); - way.setTag("access", "yes"); - assertTrue(accessParser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "service"); - way.setTag("foot", "yes"); - way.setTag("access", "no"); - assertTrue(accessParser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "track"); - way.setTag("ford", "yes"); - assertTrue(accessParser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("route", "ferry"); - assertTrue(accessParser.getAccess(way).isFerry()); - way.setTag("foot", "no"); - assertTrue(accessParser.getAccess(way).canSkip()); - - // #1562, test if ferry route with foot - way.clearTags(); - way.setTag("route", "ferry"); - way.setTag("foot", "yes"); - assertTrue(accessParser.getAccess(way).isFerry()); - - way.setTag("foot", "designated"); - assertTrue(accessParser.getAccess(way).isFerry()); - - way.setTag("foot", "official"); - assertTrue(accessParser.getAccess(way).isFerry()); - - way.setTag("foot", "permissive"); - assertTrue(accessParser.getAccess(way).isFerry()); - - way.setTag("foot", "no"); - assertTrue(accessParser.getAccess(way).canSkip()); - - way.setTag("foot", "designated"); - way.setTag("access", "private"); - assertTrue(accessParser.getAccess(way).canSkip()); - - DateFormat simpleDateFormat = Helper.createFormatter("yyyy MMM dd"); - - way.clearTags(); - way.setTag("highway", "footway"); - way.setTag("access:conditional", "no @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); - assertTrue(accessParser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "footway"); - way.setTag("access", "no"); - way.setTag("access:conditional", "yes @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); - assertTrue(accessParser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "steps"); - assertTrue(accessParser.getAccess(way).canSkip()); - - way.clearTags(); - // allow paths as they are used as generic path - way.setTag("highway", "path"); - assertTrue(accessParser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "track"); - assertTrue(accessParser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("sac_scale", "hiking"); - assertTrue(accessParser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "footway"); - assertTrue(accessParser.getAccess(way).isWay()); - way.setTag("incline", "up"); - assertTrue(accessParser.getAccess(way).isWay()); - way.setTag("incline", "3%"); - assertTrue(accessParser.getAccess(way).isWay()); - way.setTag("incline", "9.1%"); - assertTrue(accessParser.getAccess(way).canSkip()); - way.setTag("incline", "1°"); - assertTrue(accessParser.getAccess(way).isWay()); - way.setTag("incline", "5°"); - assertTrue(accessParser.getAccess(way).canSkip()); - way.setTag("incline", "-4%"); - assertTrue(accessParser.getAccess(way).isWay()); - way.setTag("incline", "-9%"); - assertTrue(accessParser.getAccess(way).canSkip()); - way.setTag("incline", "-3°"); - assertTrue(accessParser.getAccess(way).isWay()); - way.setTag("incline", "-6.5°"); - assertTrue(accessParser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "footway"); - way.setTag("wheelchair", "no"); - assertTrue(accessParser.getAccess(way).canSkip()); - way.setTag("wheelchair", "limited"); - assertTrue(accessParser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "footway"); - assertTrue(accessParser.getAccess(way).isWay()); - way.setTag("kerb", "lowered"); - assertTrue(accessParser.getAccess(way).isWay()); - way.setTag("kerb", "raised"); - assertTrue(accessParser.getAccess(way).canSkip()); - way.setTag("kerb", "2cm"); - assertTrue(accessParser.getAccess(way).isWay()); - way.setTag("kerb", "4cm"); - assertTrue(accessParser.getAccess(way).canSkip()); - way.setTag("kerb", "20mm"); - assertTrue(accessParser.getAccess(way).isWay()); - - // highway tag required - way.clearTags(); - way.setTag("wheelchair", "yes"); - assertTrue(accessParser.getAccess(way).canSkip()); - way.setTag("highway", "footway"); - assertTrue(accessParser.getAccess(way).isWay()); - } - - @Test - public void testPier() { - ReaderWay way = new ReaderWay(1); - way.setTag("man_made", "pier"); - EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); - int edgeId = 0; - accessParser.handleWayTags(edgeId, edgeIntAccess, way, null); - speedParser.handleWayTags(edgeId, edgeIntAccess, way, null); - assertTrue(wheelchairAccessEnc.getBool(false, edgeId, edgeIntAccess)); - assertTrue(wheelchairAccessEnc.getBool(true, edgeId, edgeIntAccess)); - assertEquals(5, wheelchairAvSpeedEnc.getDecimal(false, edgeId, edgeIntAccess)); - } - - @Test - public void testMixSpeedAndSafe() { - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "motorway"); - EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); - int edgeId = 0; - accessParser.handleWayTags(edgeId, edgeIntAccess, way, null); - assertFalse(wheelchairAccessEnc.getBool(false, edgeId, edgeIntAccess)); - assertFalse(wheelchairAccessEnc.getBool(true, edgeId, edgeIntAccess)); - - way.setTag("sidewalk", "yes"); - edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); - accessParser.handleWayTags(edgeId, edgeIntAccess, way, null); - speedParser.handleWayTags(edgeId, edgeIntAccess, way, null); - assertTrue(wheelchairAccessEnc.getBool(false, edgeId, edgeIntAccess)); - assertTrue(wheelchairAccessEnc.getBool(true, edgeId, edgeIntAccess)); - assertEquals(5, wheelchairAvSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), .1); - - way.clearTags(); - way.setTag("highway", "track"); - edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); - accessParser.handleWayTags(edgeId, edgeIntAccess, way, null); - assertFalse(wheelchairAccessEnc.getBool(false, edgeId, edgeIntAccess)); - assertFalse(wheelchairAccessEnc.getBool(true, edgeId, edgeIntAccess)); - } - - @Test - public void testPriority() { - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "cycleway"); - assertEquals(PriorityCode.UNCHANGED.getValue(), prioParser.handlePriority(way, null)); - way.setTag("highway", "primary"); - assertEquals(PriorityCode.AVOID.getValue(), prioParser.handlePriority(way, null)); - way.setTag("highway", "secondary"); - assertEquals(PriorityCode.AVOID.getValue(), prioParser.handlePriority(way, null)); - - way.setTag("highway", "track"); - way.setTag("bicycle", "official"); - assertEquals(PriorityCode.SLIGHT_AVOID.getValue(), prioParser.handlePriority(way, null)); - - way.setTag("highway", "track"); - way.setTag("bicycle", "designated"); - assertEquals(PriorityCode.SLIGHT_AVOID.getValue(), prioParser.handlePriority(way, null)); - - way.setTag("highway", "cycleway"); - way.setTag("bicycle", "designated"); - way.setTag("foot", "designated"); - assertEquals(PriorityCode.PREFER.getValue(), prioParser.handlePriority(way, null)); - - way.clearTags(); - way.setTag("highway", "primary"); - way.setTag("sidewalk", "yes"); - assertEquals(PriorityCode.SLIGHT_AVOID.getValue(), prioParser.handlePriority(way, null)); - - way.clearTags(); - way.setTag("highway", "cycleway"); - way.setTag("sidewalk", "no"); - assertEquals(PriorityCode.AVOID.getValue(), prioParser.handlePriority(way, null)); - - way.clearTags(); - way.setTag("highway", "road"); - way.setTag("bicycle", "official"); - way.setTag("sidewalk", "no"); - assertEquals(PriorityCode.SLIGHT_AVOID.getValue(), prioParser.handlePriority(way, null)); - - way.clearTags(); - way.setTag("highway", "trunk"); - way.setTag("sidewalk", "no"); - assertEquals(PriorityCode.VERY_BAD.getValue(), prioParser.handlePriority(way, null)); - way.setTag("sidewalk", "none"); - assertEquals(PriorityCode.VERY_BAD.getValue(), prioParser.handlePriority(way, null)); - - way.clearTags(); - way.setTag("highway", "residential"); - way.setTag("sidewalk", "yes"); - assertEquals(PriorityCode.PREFER.getValue(), prioParser.handlePriority(way, null)); - - way.clearTags(); - way.setTag("highway", "footway"); - assertEquals(PriorityCode.PREFER.getValue(), prioParser.handlePriority(way, null)); - way.setTag("wheelchair", "designated"); - assertEquals(PriorityCode.VERY_NICE.getValue(), prioParser.handlePriority(way, null)); - - way.clearTags(); - way.setTag("highway", "footway"); - assertEquals(PriorityCode.PREFER.getValue(), prioParser.handlePriority(way, null)); - way.setTag("wheelchair", "limited"); - assertEquals(PriorityCode.AVOID.getValue(), prioParser.handlePriority(way, null)); - } - - @Test - public void testBarrierAccess() { - // by default allow access through the gate for bike & foot! - ReaderNode node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "gate"); - // no barrier! - assertFalse(accessParser.isBarrier(node)); - - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "gate"); - node.setTag("access", "yes"); - // no barrier! - assertFalse(accessParser.isBarrier(node)); - - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "gate"); - node.setTag("access", "no"); - // barrier! - assertTrue(accessParser.isBarrier(node)); - - node.setTag("bicycle", "yes"); - // no barrier!? - // assertTrue(wheelchairEncoder.handleNodeTags(node) == false); - - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "gate"); - node.setTag("access", "no"); - node.setTag("foot", "yes"); - // no barrier! - assertFalse(accessParser.isBarrier(node)); - - node.setTag("locked", "yes"); - // barrier! - assertTrue(accessParser.isBarrier(node)); - } - - @Test - public void testBlockByDefault() { - ReaderNode node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "gate"); - // passByDefaultBarriers are no barrier by default - assertFalse(accessParser.isBarrier(node)); - node.setTag("access", "no"); - assertTrue(accessParser.isBarrier(node)); - - // these barriers block - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "fence"); - assertTrue(accessParser.isBarrier(node)); - node.setTag("barrier", "wall"); - assertTrue(accessParser.isBarrier(node)); - node.setTag("barrier", "handrail"); - assertTrue(accessParser.isBarrier(node)); - node.setTag("barrier", "turnstile"); - assertTrue(accessParser.isBarrier(node)); - // Explictly allowed access is allowed - node.setTag("barrier", "fence"); - node.setTag("access", "yes"); - assertFalse(accessParser.isBarrier(node)); - - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "gate"); - node.setTag("access", "yes"); - assertFalse(accessParser.isBarrier(node)); - - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "kerb"); - assertFalse(accessParser.isBarrier(node)); - node.setTag("wheelchair", "yes"); - assertFalse(accessParser.isBarrier(node)); - - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "fence"); - assertTrue(accessParser.isBarrier(node)); - } - - @Test - public void testSurfaces() { - ReaderWay way = new ReaderWay(1); - - way.setTag("highway", "footway"); - assertTrue(accessParser.getAccess(way).isWay()); - - way.setTag("surface", "cobblestone"); - assertTrue(accessParser.getAccess(way).canSkip()); - way.setTag("surface", "sand"); - assertTrue(accessParser.getAccess(way).canSkip()); - way.setTag("surface", "gravel"); - assertTrue(accessParser.getAccess(way).canSkip()); - - way.setTag("surface", "asphalt"); - assertTrue(accessParser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "service"); - assertTrue(accessParser.getAccess(way).isWay()); - - way.setTag("surface", "sand"); - assertTrue(accessParser.getAccess(way).canSkip()); - - way.setTag("sidewalk", "left"); - assertTrue(accessParser.getAccess(way).isWay()); - - way.setTag("sidewalk:left:surface", "cobblestone"); - assertTrue(accessParser.getAccess(way).canSkip()); - } - - @Test - public void testSmoothness() { - ReaderWay way = new ReaderWay(1); - - way.setTag("highway", "residential"); - assertTrue(accessParser.getAccess(way).isWay()); - - way.setTag("smoothness", "bad"); - assertTrue(accessParser.getAccess(way).canSkip()); - - way.setTag("sidewalk", "both"); - assertTrue(accessParser.getAccess(way).isWay()); - - way.setTag("sidewalk:both:smoothness", "horrible"); - assertTrue(accessParser.getAccess(way).canSkip()); - } - - @Test - public void testApplyWayTags() { - BaseGraph graph = new BaseGraph.Builder(encodingManager).set3D(true).create(); - NodeAccess na = graph.getNodeAccess(); - // incline of 5% over all - na.setNode(0, 51.1, 12.0010, 50); - na.setNode(1, 51.1, 12.0015, 55); - EdgeIteratorState edge01 = graph.edge(0, 1).setWayGeometry(Helper.createPointList3D(51.1, 12.0011, 49, 51.1, 12.0015, 55)); - edge01.setDistance(100); - GHUtility.setSpeed(5, 5, wheelchairAccessEnc, wheelchairAvSpeedEnc, edge01); - - // incline of 10% & shorter edge - na.setNode(2, 51.2, 12.1010, 50); - na.setNode(3, 51.2, 12.1015, 60); - EdgeIteratorState edge23 = graph.edge(2, 3).setWayGeometry(Helper.createPointList3D(51.2, 12.1011, 49, 51.2, 12.1015, 55)); - edge23.setDistance(30); - GHUtility.setSpeed(5, 5, wheelchairAccessEnc, wheelchairAvSpeedEnc, edge23); - - // incline of 10% & longer edge - na.setNode(4, 51.2, 12.101, 50); - na.setNode(5, 51.2, 12.102, 60); - EdgeIteratorState edge45 = graph.edge(2, 3).setWayGeometry(Helper.createPointList3D(51.2, 12.1011, 49, 51.2, 12.1015, 55)); - edge45.setDistance(100); - GHUtility.setSpeed(5, 5, wheelchairAccessEnc, wheelchairAvSpeedEnc, edge45); - - - ReaderWay way1 = new ReaderWay(1); - way1.setTag("point_list", edge01.fetchWayGeometry(FetchMode.ALL)); - way1.setTag("edge_distance", edge01.getDistance()); - EdgeIntAccess edgeIntAccess = graph.createEdgeIntAccess(); - speedParser.applyWayTags(way1, edge01.getEdge(), edgeIntAccess); - - assertTrue(edge01.get(wheelchairAccessEnc)); - assertTrue(edge01.getReverse(wheelchairAccessEnc)); - assertEquals(2, edge01.get(wheelchairAvSpeedEnc), 0); - assertEquals(2, edge01.get(speedParser.getAverageSpeedEnc()), 0); - assertEquals(5, edge01.getReverse(speedParser.getAverageSpeedEnc()), 0); - - ReaderWay way2 = new ReaderWay(2); - way2.setTag("point_list", edge23.fetchWayGeometry(FetchMode.ALL)); - way2.setTag("edge_distance", edge23.getDistance()); - speedParser.applyWayTags(way2, edge23.getEdge(), edgeIntAccess); - - assertTrue(edge23.get(wheelchairAccessEnc)); - assertTrue(edge23.getReverse(wheelchairAccessEnc)); - - assertEquals(2, edge23.get(speedParser.getAverageSpeedEnc()), 0); - assertEquals(2, edge23.getReverse(speedParser.getAverageSpeedEnc()), 0); - - // only exclude longer edges with too large incline: - ReaderWay way3 = new ReaderWay(3); - way3.setTag("point_list", edge45.fetchWayGeometry(FetchMode.ALL)); - way3.setTag("edge_distance", edge45.getDistance()); - speedParser.handleWayTags(edge45.getEdge(), edgeIntAccess, way3, null); - speedParser.applyWayTags(way3, edge45.getEdge(), edgeIntAccess); - - assertEquals(0, edge45.get(wheelchairAvSpeedEnc), 0.1); - assertEquals(0, edge45.getReverse(wheelchairAvSpeedEnc), 0.1); - } -} diff --git a/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java b/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java deleted file mode 100644 index 5011517e23b..00000000000 --- a/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * 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.routing.weighting; - -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.*; -import com.graphhopper.routing.querygraph.VirtualEdgeIteratorState; -import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.storage.BaseGraph; -import com.graphhopper.storage.Graph; -import com.graphhopper.util.*; -import com.graphhopper.util.Parameters.Routing; -import org.junit.jupiter.api.Test; - -import static com.graphhopper.routing.weighting.FastestWeighting.DESTINATION_FACTOR; -import static com.graphhopper.routing.weighting.FastestWeighting.PRIVATE_FACTOR; -import static com.graphhopper.util.GHUtility.getEdge; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * @author Peter Karich - */ -public class FastestWeightingTest { - private final BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); - private final DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); - private final BooleanEncodedValue turnRestrictionEnc = TurnRestriction.create("car"); - private final EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).addTurnCostEncodedValue(turnRestrictionEnc).build(); - private final BaseGraph graph = new BaseGraph.Builder(encodingManager).create(); - - @Test - public void testMinWeightHasSameUnitAs_getWeight() { - EdgeIteratorState edge = graph.edge(0, 1).setDistance(10); - GHUtility.setSpeed(140, 0, accessEnc, speedEnc, edge); - Weighting instance = new FastestWeighting(accessEnc, speedEnc); - assertEquals(instance.getMinWeight(10), instance.calcEdgeWeight(edge, false), 1e-8); - } - - @Test - public void testWeightWrongHeading() { - final double penalty = 100; - Weighting instance = new FastestWeighting(accessEnc, speedEnc, null, new PMap().putObject(Parameters.Routing.HEADING_PENALTY, penalty), TurnCostProvider.NO_TURN_COST_PROVIDER); - EdgeIteratorState edge = graph.edge(1, 2).setDistance(10).setWayGeometry(Helper.createPointList(51, 0, 51, 1)); - GHUtility.setSpeed(10, 10, accessEnc, speedEnc, edge); - VirtualEdgeIteratorState virtEdge = new VirtualEdgeIteratorState(edge.getEdgeKey(), 99, 5, 6, edge.getDistance(), edge.getFlags(), - edge.getKeyValues(), edge.fetchWayGeometry(FetchMode.PILLAR_ONLY), false); - double time = instance.calcEdgeWeight(virtEdge, false); - - // no penalty on edge - assertEquals(time, instance.calcEdgeWeight(virtEdge, false), 1e-8); - assertEquals(time, instance.calcEdgeWeight(virtEdge, true), 1e-8); - // ... unless setting it to unfavored (in both directions) - virtEdge.setUnfavored(true); - assertEquals(time + penalty, instance.calcEdgeWeight(virtEdge, false), 1e-8); - assertEquals(time + penalty, instance.calcEdgeWeight(virtEdge, true), 1e-8); - // but not after releasing it - virtEdge.setUnfavored(false); - assertEquals(time, instance.calcEdgeWeight(virtEdge, false), 1e-8); - assertEquals(time, instance.calcEdgeWeight(virtEdge, true), 1e-8); - - // test default penalty - virtEdge.setUnfavored(true); - instance = new FastestWeighting(accessEnc, speedEnc); - assertEquals(time + Routing.DEFAULT_HEADING_PENALTY, instance.calcEdgeWeight(virtEdge, false), 1e-8); - assertEquals(time + Routing.DEFAULT_HEADING_PENALTY, instance.calcEdgeWeight(virtEdge, true), 1e-8); - } - - @Test - public void testSpeed0() { - EdgeIteratorState edge = graph.edge(0, 1).setDistance(10); - Weighting instance = new FastestWeighting(accessEnc, speedEnc); - edge.set(speedEnc, 0); - assertTrue(Double.isInfinite(instance.calcEdgeWeight(edge, false))); - - // 0 / 0 returns NaN but calcWeight should not return NaN! - edge.setDistance(0); - assertTrue(Double.isInfinite(instance.calcEdgeWeight(edge, false))); - } - - @Test - public void testTime() { - BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); - DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 4, 2, true); - EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); - BaseGraph g = new BaseGraph.Builder(em).create(); - Weighting w = new FastestWeighting(accessEnc, speedEnc); - EdgeIteratorState edge = g.edge(0, 1).setDistance(100_000); - GHUtility.setSpeed(15, 10, accessEnc, speedEnc, edge); - assertEquals(375 * 60 * 1000, w.calcEdgeMillis(edge, false)); - assertEquals(600 * 60 * 1000, w.calcEdgeMillis(edge, true)); - } - - @Test - public void calcWeightAndTime_withTurnCosts() { - BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); - Weighting weighting = new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage())); - GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(100)); - EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(100)); - setTurnRestriction(graph, 0, 1, 2); - assertTrue(Double.isInfinite(GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0))); - assertEquals(Long.MAX_VALUE, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0)); - } - - @Test - public void calcWeightAndTime_uTurnCosts() { - BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); - Weighting weighting = new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage(), 40)); - EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(100)); - assertEquals(6 + 40, GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6); - assertEquals((6 + 40) * 1000, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6); - } - - @Test - public void calcWeightAndTime_withTurnCosts_shortest() { - BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); - Weighting weighting = new ShortestWeighting(accessEnc, speedEnc, - new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage())); - GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(100)); - EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(100)); - setTurnRestriction(graph, 0, 1, 2); - assertTrue(Double.isInfinite(GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0))); - assertEquals(Long.MAX_VALUE, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0)); - } - - @Test - public void testDestinationTag() { - BooleanEncodedValue carAccessEnc = new SimpleBooleanEncodedValue("car_access", true); - DecimalEncodedValue carSpeedEnc = new DecimalEncodedValueImpl("car_speed", 5, 5, false); - BooleanEncodedValue bikeAccessEnc = new SimpleBooleanEncodedValue("bike_access", true); - DecimalEncodedValue bikeSpeedEnc = new DecimalEncodedValueImpl("bike_speed", 4, 2, false); - EncodingManager em = EncodingManager.start().add(carAccessEnc).add(carSpeedEnc).add(bikeAccessEnc).add(bikeSpeedEnc).build(); - BaseGraph graph = new BaseGraph.Builder(em).create(); - EdgeIteratorState edge = graph.edge(0, 1).setDistance(1000); - edge.set(carAccessEnc, true, true); - edge.set(bikeAccessEnc, true, true); - edge.set(carSpeedEnc, 60); - edge.set(bikeSpeedEnc, 18); - EnumEncodedValue roadAccessEnc = em.getEnumEncodedValue(RoadAccess.KEY, RoadAccess.class); - - FastestWeighting weighting = new FastestWeighting(carAccessEnc, carSpeedEnc, roadAccessEnc, - new PMap().putObject(DESTINATION_FACTOR, 10), TurnCostProvider.NO_TURN_COST_PROVIDER); - FastestWeighting bikeWeighting = new FastestWeighting(bikeAccessEnc, bikeSpeedEnc, roadAccessEnc, - new PMap().putObject(DESTINATION_FACTOR, 1), TurnCostProvider.NO_TURN_COST_PROVIDER); - - edge.set(roadAccessEnc, RoadAccess.YES); - assertEquals(60, weighting.calcEdgeWeight(edge, false), 1.e-6); - assertEquals(200, bikeWeighting.calcEdgeWeight(edge, false), 1.e-6); - - // the destination tag does not change the weight for the bike weighting - edge.set(roadAccessEnc, RoadAccess.DESTINATION); - assertEquals(600, weighting.calcEdgeWeight(edge, false), 0.1); - assertEquals(200, bikeWeighting.calcEdgeWeight(edge, false), 0.1); - } - - @Test - public void testPrivateTag() { - BooleanEncodedValue carAccessEnc = new SimpleBooleanEncodedValue("car_access", true); - DecimalEncodedValue carSpeedEnc = new DecimalEncodedValueImpl("car_speed", 5, 5, false); - BooleanEncodedValue bikeAccessEnc = new SimpleBooleanEncodedValue("bike_access", true); - DecimalEncodedValue bikeSpeedEnc = new DecimalEncodedValueImpl("bike_speed", 4, 2, false); - EncodingManager em = EncodingManager.start().add(carAccessEnc).add(carSpeedEnc).add(bikeAccessEnc).add(bikeSpeedEnc).build(); - BaseGraph graph = new BaseGraph.Builder(em).create(); - EdgeIteratorState edge = graph.edge(0, 1).setDistance(1000); - edge.set(carAccessEnc, true, true); - edge.set(bikeAccessEnc, true, true); - edge.set(carSpeedEnc, 60); - edge.set(bikeSpeedEnc, 18); - EnumEncodedValue roadAccessEnc = em.getEnumEncodedValue(RoadAccess.KEY, RoadAccess.class); - - FastestWeighting weighting = new FastestWeighting(carAccessEnc, carSpeedEnc, roadAccessEnc, - new PMap().putObject(PRIVATE_FACTOR, 10), TurnCostProvider.NO_TURN_COST_PROVIDER); - FastestWeighting bikeWeighting = new FastestWeighting(bikeAccessEnc, bikeSpeedEnc, roadAccessEnc, - new PMap().putObject(PRIVATE_FACTOR, 1.2), TurnCostProvider.NO_TURN_COST_PROVIDER); - - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "secondary"); - - edge.set(roadAccessEnc, RoadAccess.YES); - assertEquals(60, weighting.calcEdgeWeight(edge, false), 1.e-6); - assertEquals(200, bikeWeighting.calcEdgeWeight(edge, false), 1.e-6); - - edge.set(roadAccessEnc, RoadAccess.PRIVATE); - assertEquals(600, weighting.calcEdgeWeight(edge, false), 1.e-6); - // private should influence bike only slightly - assertEquals(240, bikeWeighting.calcEdgeWeight(edge, false), 1.e-6); - } - - private void setTurnRestriction(Graph graph, int from, int via, int to) { - graph.getTurnCostStorage().set(turnRestrictionEnc, getEdge(graph, from, via).getEdge(), via, getEdge(graph, via, to).getEdge(), true); - } - -} diff --git a/core/src/test/java/com/graphhopper/routing/weighting/AbstractWeightingTest.java b/core/src/test/java/com/graphhopper/routing/weighting/WeightingTest.java similarity index 76% rename from core/src/test/java/com/graphhopper/routing/weighting/AbstractWeightingTest.java rename to core/src/test/java/com/graphhopper/routing/weighting/WeightingTest.java index 045d717fa6e..de3bb190ce3 100644 --- a/core/src/test/java/com/graphhopper/routing/weighting/AbstractWeightingTest.java +++ b/core/src/test/java/com/graphhopper/routing/weighting/WeightingTest.java @@ -25,13 +25,13 @@ /** * @author Peter Karich */ -public class AbstractWeightingTest { +public class WeightingTest { @Test public void testToString() { - assertTrue(AbstractWeighting.isValidName("blup")); - assertTrue(AbstractWeighting.isValidName("blup_a")); - assertTrue(AbstractWeighting.isValidName("blup|a")); - assertFalse(AbstractWeighting.isValidName("Blup")); - assertFalse(AbstractWeighting.isValidName("Blup!")); + assertTrue(Weighting.isValidName("blup")); + assertTrue(Weighting.isValidName("blup_a")); + assertTrue(Weighting.isValidName("blup|a")); + assertFalse(Weighting.isValidName("Blup")); + assertFalse(Weighting.isValidName("Blup!")); } } diff --git a/core/src/test/java/com/graphhopper/routing/weighting/custom/ConditionalExpressionVisitorTest.java b/core/src/test/java/com/graphhopper/routing/weighting/custom/ConditionalExpressionVisitorTest.java index 04f9f3de6fe..cab19e50036 100644 --- a/core/src/test/java/com/graphhopper/routing/weighting/custom/ConditionalExpressionVisitorTest.java +++ b/core/src/test/java/com/graphhopper/routing/weighting/custom/ConditionalExpressionVisitorTest.java @@ -43,33 +43,33 @@ public void protectUsFromStuff() { ") edge (", "in(area_blup(), edge)", "s -> truevalue")) { - ParseResult res = parse(toParse, allNamesInvalid); + ParseResult res = parse(toParse, allNamesInvalid, k -> ""); assertFalse(res.ok, "should not be simple condition: " + toParse); assertTrue(res.guessedVariables == null || res.guessedVariables.isEmpty()); } - assertFalse(parse("edge; getClass()", allNamesInvalid).ok); + assertFalse(parse("edge; getClass()", allNamesInvalid, k -> "").ok); } @Test public void testConvertExpression() { NameValidator validVariable = s -> Helper.toUpperCase(s).equals(s) || s.equals("road_class") || s.equals("toll"); - ParseResult result = parse("toll == NO", validVariable); + ParseResult result = parse("toll == NO", validVariable, k -> ""); assertTrue(result.ok); assertEquals("[toll]", result.guessedVariables.toString()); - assertEquals("road_class == RoadClass.PRIMARY", - parse("road_class == PRIMARY", validVariable).converted.toString()); - assertEquals("toll == Toll.NO", parse("toll == NO", validVariable).converted.toString()); - assertEquals("toll == Toll.NO || road_class == RoadClass.NO", parse("toll == NO || road_class == NO", validVariable).converted.toString()); + assertEquals("road_class == Hello.PRIMARY", + parse("road_class == PRIMARY", validVariable, k -> "Hello").converted.toString()); + assertEquals("toll == Toll.NO", parse("toll == NO", validVariable, k -> "Toll").converted.toString()); + assertEquals("toll == Toll.NO || road_class == RoadClass.NO", parse("toll == NO || road_class == NO", validVariable, k -> k.equals("toll") ? "Toll" : "RoadClass").converted.toString()); // convert in_area variable to function call: assertEquals(CustomWeightingHelper.class.getSimpleName() + ".in(this.in_custom_1, edge)", - parse("in_custom_1", validVariable).converted.toString()); + parse("in_custom_1", validVariable, k -> "").converted.toString()); // no need to inject: - assertNull(parse("toll == Toll.NO", validVariable).converted); + assertNull(parse("toll == Toll.NO", validVariable, k -> "").converted); } @Test @@ -77,59 +77,66 @@ public void isValidAndSimpleCondition() { NameValidator validVariable = s -> Helper.toUpperCase(s).equals(s) || s.equals("road_class") || s.equals("toll") || s.equals("my_speed") || s.equals("backward_my_speed"); - ParseResult result = parse("in_something", validVariable); + ParseResult result = parse("in_something", validVariable, k -> ""); assertTrue(result.ok); assertEquals("[in_something]", result.guessedVariables.toString()); - result = parse("edge == edge", validVariable); + result = parse("edge == edge", validVariable, k -> ""); assertFalse(result.ok); - result = parse("Math.sqrt(my_speed)", validVariable); + result = parse("Math.sqrt(my_speed)", validVariable, k -> ""); assertTrue(result.ok); assertEquals("[my_speed]", result.guessedVariables.toString()); - result = parse("Math.sqrt(2)", validVariable); + result = parse("Math.sqrt(2)", validVariable, k -> ""); assertTrue(result.ok); assertTrue(result.guessedVariables.isEmpty()); - result = parse("edge.blup()", validVariable); + result = parse("edge.blup()", validVariable, k -> ""); assertFalse(result.ok); assertTrue(result.guessedVariables.isEmpty()); - result = parse("edge.getDistance()", validVariable); + result = parse("edge.getDistance()", validVariable, k -> ""); assertTrue(result.ok); assertEquals("[edge]", result.guessedVariables.toString()); - assertFalse(parse("road_class == PRIMARY", s -> false).ok); - result = parse("road_class == PRIMARY", validVariable); + assertFalse(parse("road_class == PRIMARY", s -> false, k -> "").ok); + result = parse("road_class == PRIMARY", validVariable, k -> ""); assertTrue(result.ok); assertEquals("[road_class]", result.guessedVariables.toString()); - result = parse("toll == Toll.NO", validVariable); + result = parse("toll == Toll.NO", validVariable, k -> ""); assertFalse(result.ok); assertEquals("[toll]", result.guessedVariables.toString()); - assertTrue(parse("road_class.ordinal()*2 == PRIMARY.ordinal()*2", validVariable).ok); - assertTrue(parse("Math.sqrt(road_class.ordinal()) > 1", validVariable).ok); + assertTrue(parse("road_class.ordinal()*2 == PRIMARY.ordinal()*2", validVariable, k -> "").ok); + assertTrue(parse("Math.sqrt(road_class.ordinal()) > 1", validVariable, k -> "").ok); - result = parse("(toll == NO || road_class == PRIMARY) && toll == NO", validVariable); + result = parse("(toll == NO || road_class == PRIMARY) && toll == NO", validVariable, k -> ""); assertTrue(result.ok); assertEquals("[toll, road_class]", result.guessedVariables.toString()); - result = parse("backward_my_speed", validVariable); + result = parse("backward_my_speed", validVariable, k -> ""); assertTrue(result.ok); assertEquals("[backward_my_speed]", result.guessedVariables.toString()); } + @Test + public void testAbs() { + ParseResult result = parse("Math.abs(average_slope) < -0.5", "average_slope"::equals, k -> ""); + assertTrue(result.ok); + assertEquals("[average_slope]", result.guessedVariables.toString()); + } + @Test public void testNegativeConstant() { - ParseResult result = parse("average_slope < -0.5", "average_slope"::equals); + ParseResult result = parse("average_slope < -0.5", "average_slope"::equals, k -> ""); assertTrue(result.ok); assertEquals("[average_slope]", result.guessedVariables.toString()); - result = parse("-average_slope > -0.5", "average_slope"::equals); + result = parse("-average_slope > -0.5", "average_slope"::equals, k -> ""); assertTrue(result.ok); assertEquals("[average_slope]", result.guessedVariables.toString()); - result = parse("Math.sqrt(-2)", (var) -> false); + result = parse("Math.sqrt(-2)", (var) -> false, k -> ""); assertTrue(result.ok); assertTrue(result.guessedVariables.isEmpty()); } diff --git a/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomModelParserTest.java b/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomModelParserTest.java index 574cde7898f..fa1bc62ee24 100644 --- a/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomModelParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomModelParserTest.java @@ -33,11 +33,13 @@ import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import static com.graphhopper.json.Statement.*; import static com.graphhopper.json.Statement.Op.LIMIT; import static com.graphhopper.json.Statement.Op.MULTIPLY; import static com.graphhopper.routing.ev.RoadClass.*; +import static com.graphhopper.routing.weighting.custom.CustomModelParser.findVariablesForEncodedValuesString; import static com.graphhopper.routing.weighting.custom.CustomModelParser.parseExpressions; import static org.junit.jupiter.api.Assertions.*; @@ -51,14 +53,18 @@ class CustomModelParserTest { EnumEncodedValue stateEnc; double maxSpeed; + public enum MyBus { + MISSING, YES, DESIGNATED, DESTINATION, NO + } + @BeforeEach void setup() { accessEnc = VehicleAccess.create("car"); avgSpeedEnc = VehicleSpeed.create("car", 5, 5, false); countryEnc = Country.create(); stateEnc = State.create(); - encodingManager = new EncodingManager.Builder().add(accessEnc).add(avgSpeedEnc) - .add(stateEnc).add(countryEnc).add(MaxSpeed.create()).add(Surface.create()).build(); + encodingManager = new EncodingManager.Builder().add(accessEnc).add(avgSpeedEnc).add(new EnumEncodedValue<>("bus", MyBus.class)) + .add(stateEnc).add(countryEnc).add(MaxSpeed.create()).add(Surface.create()).add(RoadClass.create()).add(RoadEnvironment.create()).build(); graph = new BaseGraph.Builder(encodingManager).create(); roadClassEnc = encodingManager.getEnumEncodedValue(RoadClass.KEY, RoadClass.class); maxSpeed = 140; @@ -68,8 +74,8 @@ void setup() { void setPriorityForRoadClass() { CustomModel customModel = new CustomModel(); customModel.addToPriority(If("road_class == PRIMARY", MULTIPLY, "0.5")); - CustomWeighting.EdgeToDoubleMapping priorityMapping = CustomModelParser.createWeightingParameters(customModel, encodingManager, - avgSpeedEnc, maxSpeed, null).getEdgeToPriorityMapping(); + customModel.addToSpeed(If("true", LIMIT, "100")); + CustomWeighting.EdgeToDoubleMapping priorityMapping = CustomModelParser.createWeightingParameters(customModel, encodingManager).getEdgeToPriorityMapping(); BaseGraph graph = new BaseGraph.Builder(encodingManager).create(); EdgeIteratorState edge1 = graph.edge(0, 1).setDistance(100).set(roadClassEnc, RoadClass.PRIMARY); @@ -93,9 +99,8 @@ void testPriority() { customModel.addToPriority(ElseIf("road_class == SECONDARY", MULTIPLY, "0.7")); customModel.addToPriority(Else(MULTIPLY, "0.9")); customModel.addToPriority(If("road_environment != FERRY", MULTIPLY, "0.8")); - - CustomWeighting.EdgeToDoubleMapping priorityMapping = CustomModelParser.createWeightingParameters(customModel, encodingManager, - avgSpeedEnc, maxSpeed, null).getEdgeToPriorityMapping(); + customModel.addToSpeed(If("true", LIMIT, "100")); + CustomWeighting.EdgeToDoubleMapping priorityMapping = CustomModelParser.createWeightingParameters(customModel, encodingManager).getEdgeToPriorityMapping(); assertEquals(0.5 * 0.8, priorityMapping.get(primary, false), 0.01); assertEquals(0.7 * 0.8, priorityMapping.get(secondary, false), 0.01); @@ -105,8 +110,8 @@ void testPriority() { customModel = new CustomModel(); customModel.addToPriority(If("road_class == PRIMARY", MULTIPLY, "1")); customModel.addToPriority(If("road_class == SECONDARY", MULTIPLY, "0.9")); - priorityMapping = CustomModelParser.createWeightingParameters(customModel, encodingManager, - avgSpeedEnc, maxSpeed, null).getEdgeToPriorityMapping(); + customModel.addToSpeed(If("true", LIMIT, "100")); + priorityMapping = CustomModelParser.createWeightingParameters(customModel, encodingManager).getEdgeToPriorityMapping(); assertEquals(1, priorityMapping.get(primary, false), 0.01); assertEquals(0.9, priorityMapping.get(secondary, false), 0.01); } @@ -127,9 +132,8 @@ public void testCountry() { customModel.addToPriority(If("country == USA", MULTIPLY, "0.5")); customModel.addToPriority(If("country == USA && state == US_AK", MULTIPLY, "0.6")); customModel.addToPriority(If("country == DEU", MULTIPLY, "0.8")); - - CustomWeighting.EdgeToDoubleMapping priorityMapping = CustomModelParser.createWeightingParameters(customModel, encodingManager, - avgSpeedEnc, maxSpeed, null).getEdgeToPriorityMapping(); + customModel.addToSpeed(If("true", LIMIT, "100")); + CustomWeighting.EdgeToDoubleMapping priorityMapping = CustomModelParser.createWeightingParameters(customModel, encodingManager).getEdgeToPriorityMapping(); assertEquals(0.6 * 0.5, priorityMapping.get(usRoad, false), 0.01); assertEquals(0.5, priorityMapping.get(us2Road, false), 0.01); @@ -145,8 +149,8 @@ public void testBrackets() { CustomModel customModel = new CustomModel(); customModel.addToPriority(If("(road_class == PRIMARY || car_access == true) && car_average_speed > 50", MULTIPLY, "0.9")); - CustomWeighting.Parameters parameters = CustomModelParser.createWeightingParameters(customModel, encodingManager, - avgSpeedEnc, maxSpeed, null); + customModel.addToSpeed(If("true", LIMIT, "100")); + CustomWeighting.Parameters parameters = CustomModelParser.createWeightingParameters(customModel, encodingManager); assertEquals(0.9, parameters.getEdgeToPriorityMapping().get(primary, false), 0.01); assertEquals(1, parameters.getEdgeToPriorityMapping().get(secondary, false), 0.01); } @@ -160,9 +164,9 @@ public void testSpeedFactorAndPriorityAndMaxSpeed() { CustomModel customModel = new CustomModel(); customModel.addToPriority(If("road_class == PRIMARY", MULTIPLY, "0.9")); + customModel.addToSpeed(If("true", LIMIT, avgSpeedEnc.getName())); customModel.addToSpeed(If("road_class == PRIMARY", MULTIPLY, "0.8")); - CustomWeighting.Parameters parameters = CustomModelParser.createWeightingParameters(customModel, encodingManager, - avgSpeedEnc, maxSpeed, null); + CustomWeighting.Parameters parameters = CustomModelParser.createWeightingParameters(customModel, encodingManager); assertEquals(0.9, parameters.getEdgeToPriorityMapping().get(primary, false), 0.01); assertEquals(64, parameters.getEdgeToSpeedMapping().get(primary, false), 0.01); @@ -170,8 +174,7 @@ public void testSpeedFactorAndPriorityAndMaxSpeed() { assertEquals(70, parameters.getEdgeToSpeedMapping().get(secondary, false), 0.01); customModel.addToSpeed(If("road_class != PRIMARY", LIMIT, "50")); - CustomWeighting.EdgeToDoubleMapping speedMapping = CustomModelParser.createWeightingParameters(customModel, encodingManager, - avgSpeedEnc, maxSpeed, null).getEdgeToSpeedMapping(); + CustomWeighting.EdgeToDoubleMapping speedMapping = CustomModelParser.createWeightingParameters(customModel, encodingManager).getEdgeToSpeedMapping(); assertEquals(64, speedMapping.get(primary, false), 0.01); assertEquals(50, speedMapping.get(secondary, false), 0.01); } @@ -181,14 +184,12 @@ void testIllegalOrder() { CustomModel customModel = new CustomModel(); customModel.addToPriority(Else(MULTIPLY, "0.9")); customModel.addToPriority(If("road_environment != FERRY", MULTIPLY, "0.8")); - assertThrows(IllegalArgumentException.class, () -> CustomModelParser.createWeightingParameters(customModel, encodingManager, - avgSpeedEnc, maxSpeed, null)); + assertThrows(IllegalArgumentException.class, () -> CustomModelParser.createWeightingParameters(customModel, encodingManager)); CustomModel customModel2 = new CustomModel(); customModel2.addToPriority(ElseIf("road_environment != FERRY", MULTIPLY, "0.9")); customModel2.addToPriority(If("road_class != PRIMARY", MULTIPLY, "0.8")); - assertThrows(IllegalArgumentException.class, () -> CustomModelParser.createWeightingParameters(customModel2, encodingManager, - avgSpeedEnc, maxSpeed, null)); + assertThrows(IllegalArgumentException.class, () -> CustomModelParser.createWeightingParameters(customModel2, encodingManager)); } @Test @@ -221,26 +222,24 @@ public void multipleAreas() { new HashMap<>())); customModel.setAreas(areas); + customModel.addToSpeed(If("true", LIMIT, avgSpeedEnc.getName())); customModel.addToSpeed(If("in_area_1", LIMIT, "100")); customModel.addToSpeed(If("!in_area_2", LIMIT, "25")); customModel.addToSpeed(Else(LIMIT, "15")); // No exception is thrown during createWeightingParameters - assertAll(() -> - CustomModelParser.createWeightingParameters(customModel, encodingManager, - avgSpeedEnc, maxSpeed, null)); + assertAll(() -> CustomModelParser.createWeightingParameters(customModel, encodingManager)); CustomModel customModel2 = new CustomModel(); customModel2.setAreas(areas); + customModel2.addToSpeed(If("true", LIMIT, avgSpeedEnc.getName())); customModel2.addToSpeed(If("in_area_1", LIMIT, "100")); customModel2.addToSpeed(If("in_area_2", LIMIT, "25")); customModel2.addToSpeed(If("in_area_3", LIMIT, "150")); customModel2.addToSpeed(Else(LIMIT, "15")); - assertThrows(IllegalArgumentException.class, () -> - CustomModelParser.createWeightingParameters(customModel2, encodingManager, - avgSpeedEnc, maxSpeed, null)); + assertThrows(IllegalArgumentException.class, () -> CustomModelParser.createWeightingParameters(customModel2, encodingManager)); } @Test @@ -252,9 +251,10 @@ public void parseValue() { set(maxSpeedEnc, 70).set(avgSpeedEnc, 70).set(accessEnc, true, true); CustomModel customModel = new CustomModel(); + customModel.addToSpeed(If("true", LIMIT, avgSpeedEnc.getName())); customModel.addToSpeed(If("true", LIMIT, "max_speed * 1.1")); - CustomWeighting.EdgeToDoubleMapping speedMapping = CustomModelParser.createWeightingParameters(customModel, encodingManager, - avgSpeedEnc, maxSpeed, null).getEdgeToSpeedMapping(); + CustomWeighting.EdgeToDoubleMapping speedMapping = CustomModelParser.createWeightingParameters(customModel, encodingManager). + getEdgeToSpeedMapping(); assertEquals(70.0, speedMapping.get(maxSame, false), 0.01); assertEquals(66.0, speedMapping.get(maxLower, false), 0.01); } @@ -265,24 +265,15 @@ public void parseValueWithError() { customModel1.addToSpeed(If("true", LIMIT, "unknown")); IllegalArgumentException ret = assertThrows(IllegalArgumentException.class, - () -> CustomModelParser.createWeightingParameters(customModel1, encodingManager, - avgSpeedEnc, maxSpeed, null)); - assertTrue(ret.getMessage().startsWith("Cannot compile expression: 'unknown' not available"), ret.getMessage()); - - CustomModel customModel2 = new CustomModel(); - customModel2.addToSpeed(If("road_class == PRIMARY", MULTIPLY, "0.5")); - customModel2.addToSpeed(Else(MULTIPLY, "-0.5")); - ret = assertThrows(IllegalArgumentException.class, - () -> CustomModelParser.createWeightingParameters(customModel2, encodingManager, - avgSpeedEnc, maxSpeed, null)); - assertTrue(ret.getMessage().startsWith("Cannot compile expression: speed has to be >=0 but can be negative (-0.5)"), ret.getMessage()); + () -> CustomModelParser.createWeightingParameters(customModel1, encodingManager)); + assertEquals("Cannot compile expression: 'unknown' not available", ret.getMessage()); CustomModel customModel3 = new CustomModel(); + customModel3.addToSpeed(If("true", LIMIT, avgSpeedEnc.getName())); customModel3.addToSpeed(If("road_class == PRIMARY", MULTIPLY, "0.5")); customModel3.addToSpeed(Else(MULTIPLY, "road_class")); ret = assertThrows(IllegalArgumentException.class, - () -> CustomModelParser.createWeightingParameters(customModel3, encodingManager, - avgSpeedEnc, maxSpeed, null)); + () -> CustomModelParser.createWeightingParameters(customModel3, encodingManager)); assertTrue(ret.getMessage().contains("Binary numeric promotion not possible on types \"double\" and \"com.graphhopper.routing.ev.RoadClass\""), ret.getMessage()); } @@ -294,21 +285,22 @@ public void parseConditionWithError() { IllegalArgumentException ret = assertThrows(IllegalArgumentException.class, () -> parseExpressions(new StringBuilder(), validVariable, "[HERE]", new HashSet<>(), - Arrays.asList(If("max_weight > 10", MULTIPLY, "0")))); + Arrays.asList(If("max_weight > 10", MULTIPLY, "0")), s -> "") + ); assertTrue(ret.getMessage().startsWith("[HERE] invalid condition \"max_weight > 10\": 'max_weight' not available"), ret.getMessage()); // invalid variable or constant (NameValidator returns false) ret = assertThrows(IllegalArgumentException.class, () -> parseExpressions(new StringBuilder(), validVariable, "[HERE]", new HashSet<>(), - Arrays.asList(If("country == GERMANY", MULTIPLY, "0")))); + Arrays.asList(If("country == GERMANY", MULTIPLY, "0")), s -> "")); assertTrue(ret.getMessage().startsWith("[HERE] invalid condition \"country == GERMANY\": 'GERMANY' not available"), ret.getMessage()); // not whitelisted method ret = assertThrows(IllegalArgumentException.class, () -> parseExpressions(new StringBuilder(), validVariable, "[HERE]", new HashSet<>(), - Arrays.asList(If("edge.fetchWayGeometry().size() > 2", MULTIPLY, "0")))); + Arrays.asList(If("edge.fetchWayGeometry().size() > 2", MULTIPLY, "0")), s -> "")); assertTrue(ret.getMessage().startsWith("[HERE] invalid condition \"edge.fetchWayGeometry().size() > 2\": size is an illegal method"), ret.getMessage()); } @@ -316,8 +308,9 @@ public void parseConditionWithError() { void testBackwardFunction() { CustomModel customModel = new CustomModel(); customModel.addToPriority(If("backward_car_access != car_access", MULTIPLY, "0.5")); - CustomWeighting.EdgeToDoubleMapping priorityMapping = CustomModelParser.createWeightingParameters(customModel, encodingManager, - avgSpeedEnc, maxSpeed, null).getEdgeToPriorityMapping(); + customModel.addToSpeed(If("true", LIMIT, "100")); + CustomWeighting.EdgeToDoubleMapping priorityMapping = CustomModelParser.createWeightingParameters(customModel, encodingManager). + getEdgeToPriorityMapping(); BaseGraph graph = new BaseGraph.Builder(encodingManager).create(); EdgeIteratorState edge1 = graph.edge(0, 1).setDistance(100).set(accessEnc, true, false); @@ -327,4 +320,17 @@ void testBackwardFunction() { assertEquals(1.0, priorityMapping.get(edge2, false), 1.e-6); } + @Test + public void findVariablesForEncodedValueString() { + CustomModel customModel = new CustomModel(); + customModel.addToPriority(If("backward_car_access != car_access", MULTIPLY, "0.5")); + List variables = findVariablesForEncodedValuesString(customModel, s -> new DefaultImportRegistry().createImportUnit(s) != null, s -> ""); + assertEquals(List.of("car_access"), variables); + + customModel = new CustomModel(); + customModel.addToPriority(If("!foot_access && (hike_rating < 4 || road_access == PRIVATE)", MULTIPLY, "0")); + //, {"if": "true", "multiply_by": foot_priority}, {"if": "foot_network == INTERNATIONAL || foot_network == NATIONAL", "multiply_by": 1.7}, {"else_if": "foot_network == REGIONAL || foot_network == LOCAL", "multiply_by": 1.5}]|areas=[]|turnCostsConfig=transportationMode=null, restrictions=false, uTurnCosts=-1 + variables = findVariablesForEncodedValuesString(customModel, s -> new DefaultImportRegistry().createImportUnit(s) != null, s -> ""); + assertEquals(List.of("foot_access", "hike_rating", "road_access"), variables); + } } diff --git a/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingHelperTest.java b/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingHelperTest.java index c032b43bb13..e4addcf4f69 100644 --- a/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingHelperTest.java +++ b/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingHelperTest.java @@ -1,12 +1,18 @@ package com.graphhopper.routing.weighting.custom; +import com.graphhopper.routing.ev.VehicleSpeed; +import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.storage.BaseGraph; +import com.graphhopper.util.CustomModel; import com.graphhopper.util.EdgeIteratorState; -import com.graphhopper.util.FetchMode; import com.graphhopper.util.Helper; import com.graphhopper.util.shapes.Polygon; import org.junit.jupiter.api.Test; +import static com.graphhopper.json.Statement.Else; +import static com.graphhopper.json.Statement.If; +import static com.graphhopper.json.Statement.Op.LIMIT; +import static com.graphhopper.json.Statement.Op.MULTIPLY; import static org.junit.jupiter.api.Assertions.*; class CustomWeightingHelperTest { @@ -46,4 +52,18 @@ public void testInRectangle() { edge = g.edge(6, 7).setWayGeometry(Helper.createPointList(30, 30)); assertFalse(CustomWeightingHelper.in(square, edge)); } + + @Test + public void testNegativeMax() { + CustomModel customModel = new CustomModel(); + customModel.addToSpeed(If("true", LIMIT, VehicleSpeed.key("car"))); + customModel.addToSpeed(If("road_class == PRIMARY", MULTIPLY, "0.5")); + customModel.addToSpeed(Else(MULTIPLY, "-0.5")); + + CustomWeightingHelper helper = new CustomWeightingHelper(); + EncodingManager lookup = new EncodingManager.Builder().add(VehicleSpeed.create("car", 5, 5, true)).build(); + helper.init(customModel, lookup, null); + IllegalArgumentException ret = assertThrows(IllegalArgumentException.class, helper::calcMaxSpeed); + assertTrue(ret.getMessage().startsWith("statement resulted in negative value")); + } } diff --git a/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java b/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java index 942715ac2e4..69e7b7226bc 100644 --- a/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java +++ b/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java @@ -14,8 +14,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.util.Random; - import static com.graphhopper.json.Statement.*; import static com.graphhopper.json.Statement.Op.LIMIT; import static com.graphhopper.json.Statement.Op.MULTIPLY; @@ -41,6 +39,9 @@ public void setup() { .add(Toll.create()) .add(Hazmat.create()) .add(RouteNetwork.create(BikeNetwork.KEY)) + .add(MaxSpeed.create()) + .add(RoadClass.create()) + .add(RoadClassLink.create()) .addTurnCostEncodedValue(turnRestrictionEnc) .build(); maxSpeedEnc = encodingManager.getDecimalEncodedValue(MaxSpeed.KEY); @@ -48,71 +49,87 @@ public void setup() { graph = new BaseGraph.Builder(encodingManager).create(); } + private void setTurnRestriction(Graph graph, int from, int via, int to) { + graph.getTurnCostStorage().set(turnRestrictionEnc, getEdge(graph, from, via).getEdge(), via, getEdge(graph, via, to).getEdge(), true); + } + + private CustomModel createSpeedCustomModel(DecimalEncodedValue speedEnc) { + CustomModel customModel = new CustomModel(); + customModel.addToSpeed(If("true", LIMIT, speedEnc.getName())); + return customModel; + } + + private Weighting createWeighting(CustomModel vehicleModel) { + return CustomModelParser.createWeighting(encodingManager, NO_TURN_COST_PROVIDER, vehicleModel); + } + @Test public void speedOnly() { // 50km/h -> 72s per km, 100km/h -> 36s per km - EdgeIteratorState edge; - GHUtility.setSpeed(50, 100, accessEnc, avSpeedEnc, edge = graph.edge(0, 1).setDistance(1000)); - assertEquals(72, createWeighting(new CustomModel().setDistanceInfluence(0d)).calcEdgeWeight(edge, false), 1.e-6); - assertEquals(36, createWeighting(new CustomModel().setDistanceInfluence(0d)).calcEdgeWeight(edge, true), 1.e-6); + EdgeIteratorState edge = graph.edge(0, 1).setDistance(1000).set(avSpeedEnc, 50, 100); + CustomModel customModel = createSpeedCustomModel(avSpeedEnc) + .setDistanceInfluence(0d); + Weighting weighting = createWeighting(customModel); + + assertEquals(72, weighting.calcEdgeWeight(edge, false), 1.e-6); + assertEquals(36, weighting.calcEdgeWeight(edge, true), 1.e-6); } @Test public void withPriority() { // 25km/h -> 144s per km, 50km/h -> 72s per km, 100km/h -> 36s per km - EdgeIteratorState slow = GHUtility.setSpeed(25, true, true, accessEnc, avSpeedEnc, graph.edge(0, 1).setDistance(1000)). + EdgeIteratorState slow = graph.edge(0, 1).set(avSpeedEnc, 25, 25).setDistance(1000). set(roadClassEnc, SECONDARY); - EdgeIteratorState medium = GHUtility.setSpeed(50, true, true, accessEnc, avSpeedEnc, graph.edge(0, 1).setDistance(1000)). + EdgeIteratorState medium = graph.edge(0, 1).set(avSpeedEnc, 50, 50).setDistance(1000). set(roadClassEnc, SECONDARY); - EdgeIteratorState fast = GHUtility.setSpeed(100, true, true, accessEnc, avSpeedEnc, graph.edge(0, 1).setDistance(1000)). + EdgeIteratorState fast = graph.edge(0, 1).set(avSpeedEnc, 100).setDistance(1000). set(roadClassEnc, SECONDARY); - // without priority costs fastest weighting is the same as custom weighting - assertEquals(144, CustomModelParser.createFastestWeighting(accessEnc, avSpeedEnc, encodingManager).calcEdgeWeight(slow, false), .1); - assertEquals(72, CustomModelParser.createFastestWeighting(accessEnc, avSpeedEnc, encodingManager).calcEdgeWeight(medium, false), .1); - assertEquals(36, CustomModelParser.createFastestWeighting(accessEnc, avSpeedEnc, encodingManager).calcEdgeWeight(fast, false), .1); - - CustomModel model = new CustomModel().setDistanceInfluence(0d); - assertEquals(144, createWeighting(model).calcEdgeWeight(slow, false), .1); - assertEquals(72, createWeighting(model).calcEdgeWeight(medium, false), .1); - assertEquals(36, createWeighting(model).calcEdgeWeight(fast, false), .1); + Weighting weighting = createWeighting(createSpeedCustomModel(avSpeedEnc)); + assertEquals(144, weighting.calcEdgeWeight(slow, false), .1); + assertEquals(72, weighting.calcEdgeWeight(medium, false), .1); + assertEquals(36, weighting.calcEdgeWeight(fast, false), .1); // if we reduce the priority we get higher edge weights - model.addToPriority(If("road_class == SECONDARY", MULTIPLY, "0.5")); - // the absolute priority costs depend on the speed, so setting priority=0.5 means a lower absolute weight - // weight increase for fast edges and a higher absolute increase for slower edges - assertEquals(2 * 144, createWeighting(model).calcEdgeWeight(slow, false), .1); - assertEquals(2 * 72, createWeighting(model).calcEdgeWeight(medium, false), .1); - assertEquals(2 * 36, createWeighting(model).calcEdgeWeight(fast, false), .1); + weighting = CustomModelParser.createWeighting(encodingManager, NO_TURN_COST_PROVIDER, + createSpeedCustomModel(avSpeedEnc) + .addToPriority(If("road_class == SECONDARY", MULTIPLY, "0.5")) + ); + assertEquals(2 * 144, weighting.calcEdgeWeight(slow, false), .1); + assertEquals(2 * 72, weighting.calcEdgeWeight(medium, false), .1); + assertEquals(2 * 36, weighting.calcEdgeWeight(fast, false), .1); } @Test public void withDistanceInfluence() { - EdgeIteratorState edge = graph.edge(0, 1).setDistance(10_000).set(avSpeedEnc, 50).set(accessEnc, true, true); - assertEquals(720, createWeighting(new CustomModel().setDistanceInfluence(0d)).calcEdgeWeight(edge, false), .1); - assertEquals(720_000, createWeighting(new CustomModel().setDistanceInfluence(0d)).calcEdgeMillis(edge, false), .1); - // distance_influence=30 means that for every kilometer we get additional costs of 30s, so +300s here - assertEquals(1020, createWeighting(new CustomModel().setDistanceInfluence(30d)).calcEdgeWeight(edge, false), .1); - // ... but the travelling time stays the same - assertEquals(720_000, createWeighting(new CustomModel().setDistanceInfluence(30d)).calcEdgeMillis(edge, false), .1); - + EdgeIteratorState edge1 = graph.edge(0, 1).setDistance(10_000).set(avSpeedEnc, 50); + EdgeIteratorState edge2 = graph.edge(0, 1).setDistance(5_000).set(avSpeedEnc, 25); + Weighting weighting = createWeighting(createSpeedCustomModel(avSpeedEnc).setDistanceInfluence(0d)); + assertEquals(720, weighting.calcEdgeWeight(edge1, false), .1); + assertEquals(720_000, weighting.calcEdgeMillis(edge1, false), .1); // we can also imagine a shorter but slower road that takes the same time - edge = graph.edge(0, 1).setDistance(5_000).set(avSpeedEnc, 25).set(accessEnc, true, true); - assertEquals(720, createWeighting(new CustomModel().setDistanceInfluence(0d)).calcEdgeWeight(edge, false), .1); - assertEquals(720_000, createWeighting(new CustomModel().setDistanceInfluence(0d)).calcEdgeMillis(edge, false), .1); - // and if we include the distance influence the weight will be bigger but still smaller than what we got for - // the longer and faster edge - assertEquals(870, createWeighting(new CustomModel().setDistanceInfluence(30d)).calcEdgeWeight(edge, false), .1); + assertEquals(720, weighting.calcEdgeWeight(edge2, false), .1); + assertEquals(720_000, weighting.calcEdgeMillis(edge2, false), .1); + + // distance_influence=30 means that for every kilometer we get additional costs of 30s, so +300s here + weighting = createWeighting(createSpeedCustomModel(avSpeedEnc).setDistanceInfluence(30d)); + assertEquals(1020, weighting.calcEdgeWeight(edge1, false), .1); + // for the shorter but slower edge the distance influence also increases the weight, but not as much because it is shorter + assertEquals(870, weighting.calcEdgeWeight(edge2, false), .1); + // ... the travelling times stay the same + assertEquals(720_000, weighting.calcEdgeMillis(edge1, false), .1); + assertEquals(720_000, weighting.calcEdgeMillis(edge2, false), .1); } @Test public void testSpeedFactorBooleanEV() { - EdgeIteratorState edge = GHUtility.setSpeed(15, true, true, accessEnc, avSpeedEnc, graph.edge(0, 1).setDistance(10)); - CustomModel vehicleModel = new CustomModel().setDistanceInfluence(70d); - assertEquals(3.1, createWeighting(vehicleModel).calcEdgeWeight(edge, false), 0.01); + EdgeIteratorState edge = graph.edge(0, 1).set(avSpeedEnc, 15, 15).setDistance(10); + Weighting weighting = createWeighting(createSpeedCustomModel(avSpeedEnc).setDistanceInfluence(70d)); + assertEquals(3.1, weighting.calcEdgeWeight(edge, false), 0.01); // here we increase weight for edges that are road class links - vehicleModel.addToPriority(If(RoadClassLink.KEY, MULTIPLY, "0.5")); - Weighting weighting = createWeighting(vehicleModel); + weighting = createWeighting(createSpeedCustomModel(avSpeedEnc) + .setDistanceInfluence(70d) + .addToPriority(If(RoadClassLink.KEY, MULTIPLY, "0.5"))); BooleanEncodedValue rcLinkEnc = encodingManager.getBooleanEncodedValue(RoadClassLink.KEY); assertEquals(3.1, weighting.calcEdgeWeight(edge.set(rcLinkEnc, false), false), 0.01); assertEquals(5.5, weighting.calcEdgeWeight(edge.set(rcLinkEnc, true), false), 0.01); @@ -120,21 +137,19 @@ public void testSpeedFactorBooleanEV() { @Test public void testBoolean() { - BooleanEncodedValue accessEnc = VehicleAccess.create("car"); - DecimalEncodedValue avSpeedEnc = VehicleSpeed.create("car", 5, 5, false); BooleanEncodedValue specialEnc = new SimpleBooleanEncodedValue("special", true); - encodingManager = new EncodingManager.Builder().add(accessEnc).add(avSpeedEnc).add(specialEnc).build(); + DecimalEncodedValue avSpeedEnc = VehicleSpeed.create("car", 5, 5, false); + encodingManager = new EncodingManager.Builder().add(specialEnc).add(avSpeedEnc).build(); graph = new BaseGraph.Builder(encodingManager).create(); - EdgeIteratorState edge = graph.edge(0, 1).set(accessEnc, true).setReverse(accessEnc, true). - set(avSpeedEnc, 15).set(specialEnc, false).setReverse(specialEnc, true).setDistance(10); + EdgeIteratorState edge = graph.edge(0, 1).set(specialEnc, false, true).set(avSpeedEnc, 15).setDistance(10); - CustomModel vehicleModel = new CustomModel().setDistanceInfluence(70d); - Weighting weighting = CustomModelParser.createWeighting(accessEnc, avSpeedEnc, null, encodingManager, NO_TURN_COST_PROVIDER, vehicleModel); + Weighting weighting = createWeighting(createSpeedCustomModel(avSpeedEnc).setDistanceInfluence(70d)); assertEquals(3.1, weighting.calcEdgeWeight(edge, false), 0.01); - vehicleModel.addToPriority(If("special == true", MULTIPLY, "0.8")); - vehicleModel.addToPriority(If("special == false", MULTIPLY, "0.4")); - weighting = CustomModelParser.createWeighting(accessEnc, avSpeedEnc, null, encodingManager, NO_TURN_COST_PROVIDER, vehicleModel); + weighting = createWeighting(createSpeedCustomModel(avSpeedEnc) + .setDistanceInfluence(70d) + .addToPriority(If("special == true", MULTIPLY, "0.8")) + .addToPriority(If("special == false", MULTIPLY, "0.4"))); assertEquals(6.7, weighting.calcEdgeWeight(edge, false), 0.01); assertEquals(3.7, weighting.calcEdgeWeight(edge, true), 0.01); } @@ -142,153 +157,161 @@ public void testBoolean() { @Test public void testSpeedFactorAndPriority() { EdgeIteratorState primary = graph.edge(0, 1).setDistance(10). - set(roadClassEnc, PRIMARY).set(avSpeedEnc, 80).set(accessEnc, true, true); + set(roadClassEnc, PRIMARY).set(avSpeedEnc, 80); EdgeIteratorState secondary = graph.edge(1, 2).setDistance(10). - set(roadClassEnc, SECONDARY).set(avSpeedEnc, 70).set(accessEnc, true, true); + set(roadClassEnc, SECONDARY).set(avSpeedEnc, 70); - CustomModel vehicleModel = new CustomModel().setDistanceInfluence(70d). + CustomModel customModel = createSpeedCustomModel(avSpeedEnc).setDistanceInfluence(70d). addToPriority(If("road_class != PRIMARY", MULTIPLY, "0.5")). addToSpeed(If("road_class != PRIMARY", MULTIPLY, "0.9")); - assertEquals(1.15, createWeighting(vehicleModel).calcEdgeWeight(primary, false), 0.01); - assertEquals(1.84, createWeighting(vehicleModel).calcEdgeWeight(secondary, false), 0.01); + Weighting weighting = createWeighting(customModel); + assertEquals(1.15, weighting.calcEdgeWeight(primary, false), 0.01); + assertEquals(1.84, weighting.calcEdgeWeight(secondary, false), 0.01); - vehicleModel = new CustomModel().setDistanceInfluence(70d). + customModel = createSpeedCustomModel(avSpeedEnc).setDistanceInfluence(70d). addToPriority(If("road_class == PRIMARY", MULTIPLY, "1.0")). addToPriority(Else(MULTIPLY, "0.5")). addToSpeed(If("road_class != PRIMARY", MULTIPLY, "0.9")); - assertEquals(1.15, createWeighting(vehicleModel).calcEdgeWeight(primary, false), 0.01); - assertEquals(1.84, createWeighting(vehicleModel).calcEdgeWeight(secondary, false), 0.01); + weighting = createWeighting(customModel); + assertEquals(1.15, weighting.calcEdgeWeight(primary, false), 0.01); + assertEquals(1.84, weighting.calcEdgeWeight(secondary, false), 0.01); } @Test public void testIssueSameKey() { - EdgeIteratorState withToll = graph.edge(0, 1).setDistance(10). - set(avSpeedEnc, 80).set(accessEnc, true, true); - EdgeIteratorState noToll = graph.edge(1, 2).setDistance(10). - set(avSpeedEnc, 80).set(accessEnc, true, true); + EdgeIteratorState withToll = graph.edge(0, 1).setDistance(10).set(avSpeedEnc, 80); + EdgeIteratorState noToll = graph.edge(1, 2).setDistance(10).set(avSpeedEnc, 80); - CustomModel vehicleModel = new CustomModel(); - vehicleModel.setDistanceInfluence(70d). + CustomModel customModel = createSpeedCustomModel(avSpeedEnc); + customModel.setDistanceInfluence(70d). addToSpeed(If("toll == HGV || toll == ALL", MULTIPLY, "0.8")). addToSpeed(If("hazmat != NO", MULTIPLY, "0.8")); - assertEquals(1.26, createWeighting(vehicleModel).calcEdgeWeight(withToll, false), 0.01); - assertEquals(1.26, createWeighting(vehicleModel).calcEdgeWeight(noToll, false), 0.01); + Weighting weighting = createWeighting(customModel); + assertEquals(1.26, weighting.calcEdgeWeight(withToll, false), 0.01); + assertEquals(1.26, weighting.calcEdgeWeight(noToll, false), 0.01); - vehicleModel = new CustomModel().setDistanceInfluence(70d). + customModel = createSpeedCustomModel(avSpeedEnc); + customModel.setDistanceInfluence(70d). addToSpeed(If("bike_network != OTHER", MULTIPLY, "0.8")); - assertEquals(1.26, createWeighting(vehicleModel).calcEdgeWeight(withToll, false), 0.01); - assertEquals(1.26, createWeighting(vehicleModel).calcEdgeWeight(noToll, false), 0.01); + weighting = createWeighting(customModel); + assertEquals(1.26, weighting.calcEdgeWeight(withToll, false), 0.01); + assertEquals(1.26, weighting.calcEdgeWeight(noToll, false), 0.01); } @Test public void testFirstMatch() { EdgeIteratorState primary = graph.edge(0, 1).setDistance(10). - set(roadClassEnc, PRIMARY).set(avSpeedEnc, 80).set(accessEnc, true, true); + set(roadClassEnc, PRIMARY).set(avSpeedEnc, 80); EdgeIteratorState secondary = graph.edge(1, 2).setDistance(10). - set(roadClassEnc, SECONDARY).set(avSpeedEnc, 70).set(accessEnc, true, true); + set(roadClassEnc, SECONDARY).set(avSpeedEnc, 70); - CustomModel vehicleModel = new CustomModel().setDistanceInfluence(70d). + CustomModel customModel = createSpeedCustomModel(avSpeedEnc).setDistanceInfluence(70d). addToSpeed(If("road_class == PRIMARY", MULTIPLY, "0.8")); - assertEquals(1.26, createWeighting(vehicleModel).calcEdgeWeight(primary, false), 0.01); - assertEquals(1.21, createWeighting(vehicleModel).calcEdgeWeight(secondary, false), 0.01); + Weighting weighting = createWeighting(customModel); + assertEquals(1.26, weighting.calcEdgeWeight(primary, false), 0.01); + assertEquals(1.21, weighting.calcEdgeWeight(secondary, false), 0.01); - vehicleModel.addToPriority(If("road_class == PRIMARY", MULTIPLY, "0.9")); - vehicleModel.addToPriority(ElseIf("road_class == SECONDARY", MULTIPLY, "0.8")); + customModel.addToPriority(If("road_class == PRIMARY", MULTIPLY, "0.9")); + customModel.addToPriority(ElseIf("road_class == SECONDARY", MULTIPLY, "0.8")); - assertEquals(1.33, createWeighting(vehicleModel).calcEdgeWeight(primary, false), 0.01); - assertEquals(1.34, createWeighting(vehicleModel).calcEdgeWeight(secondary, false), 0.01); + weighting = createWeighting(customModel); + assertEquals(1.33, weighting.calcEdgeWeight(primary, false), 0.01); + assertEquals(1.34, weighting.calcEdgeWeight(secondary, false), 0.01); } @Test - public void testCarAccess() { - EdgeIteratorState edge40 = graph.edge(0, 1).setDistance(10).set(avSpeedEnc, 40).set(accessEnc, true, true); - EdgeIteratorState edge50 = graph.edge(1, 2).setDistance(10).set(avSpeedEnc, 50).set(accessEnc, true, true); + public void testSpeedBiggerThan() { + EdgeIteratorState edge40 = graph.edge(0, 1).setDistance(10).set(avSpeedEnc, 40); + EdgeIteratorState edge50 = graph.edge(1, 2).setDistance(10).set(avSpeedEnc, 50); - CustomModel vehicleModel = new CustomModel().setDistanceInfluence(70d). + CustomModel customModel = createSpeedCustomModel(avSpeedEnc).setDistanceInfluence(70d). addToPriority(If("car_average_speed > 40", MULTIPLY, "0.5")); + Weighting weighting = createWeighting(customModel); - assertEquals(1.60, createWeighting(vehicleModel).calcEdgeWeight(edge40, false), 0.01); - assertEquals(2.14, createWeighting(vehicleModel).calcEdgeWeight(edge50, false), 0.01); + assertEquals(1.60, weighting.calcEdgeWeight(edge40, false), 0.01); + assertEquals(2.14, weighting.calcEdgeWeight(edge50, false), 0.01); } @Test public void testRoadClass() { EdgeIteratorState primary = graph.edge(0, 1).setDistance(10). - set(roadClassEnc, PRIMARY).set(avSpeedEnc, 80).set(accessEnc, true, true); + set(roadClassEnc, PRIMARY).set(avSpeedEnc, 80); EdgeIteratorState secondary = graph.edge(1, 2).setDistance(10). - set(roadClassEnc, SECONDARY).set(avSpeedEnc, 80).set(accessEnc, true, true); - CustomModel vehicleModel = new CustomModel().setDistanceInfluence(70d). + set(roadClassEnc, SECONDARY).set(avSpeedEnc, 80); + CustomModel customModel = createSpeedCustomModel(avSpeedEnc).setDistanceInfluence(70d). addToPriority(If("road_class == PRIMARY", MULTIPLY, "0.5")); - assertEquals(1.6, createWeighting(vehicleModel).calcEdgeWeight(primary, false), 0.01); - assertEquals(1.15, createWeighting(vehicleModel).calcEdgeWeight(secondary, false), 0.01); + Weighting weighting = createWeighting(customModel); + assertEquals(1.6, weighting.calcEdgeWeight(primary, false), 0.01); + assertEquals(1.15, weighting.calcEdgeWeight(secondary, false), 0.01); } @Test public void testArea() throws Exception { EdgeIteratorState edge1 = graph.edge(0, 1).setDistance(10). - set(roadClassEnc, PRIMARY).set(avSpeedEnc, 80).set(accessEnc, true, true); + set(roadClassEnc, PRIMARY).set(avSpeedEnc, 80); EdgeIteratorState edge2 = graph.edge(2, 3).setDistance(10). - set(roadClassEnc, PRIMARY).set(avSpeedEnc, 80).set(accessEnc, true, true); + set(roadClassEnc, PRIMARY).set(avSpeedEnc, 80); graph.getNodeAccess().setNode(0, 50.0120, 11.582); graph.getNodeAccess().setNode(1, 50.0125, 11.585); graph.getNodeAccess().setNode(2, 40.0, 8.0); graph.getNodeAccess().setNode(3, 40.1, 8.1); - CustomModel vehicleModel = new CustomModel().setDistanceInfluence(70d). + CustomModel customModel = createSpeedCustomModel(avSpeedEnc).setDistanceInfluence(70d). addToPriority(If("in_custom1", MULTIPLY, "0.5")); ObjectMapper om = new ObjectMapper().registerModule(new JtsModule()); JsonFeature json = om.readValue("{ \"geometry\":{ \"type\": \"Polygon\", \"coordinates\": " + "[[[11.5818,50.0126], [11.5818,50.0119], [11.5861,50.0119], [11.5861,50.0126], [11.5818,50.0126]]] }}", JsonFeature.class); json.setId("custom1"); - vehicleModel.getAreas().getFeatures().add(json); + customModel.getAreas().getFeatures().add(json); + Weighting weighting = createWeighting(customModel); // edge1 is located within the area custom1, edge2 is not - assertEquals(1.6, createWeighting(vehicleModel).calcEdgeWeight(edge1, false), 0.01); - assertEquals(1.15, createWeighting(vehicleModel).calcEdgeWeight(edge2, false), 0.01); + assertEquals(1.6, weighting.calcEdgeWeight(edge1, false), 0.01); + assertEquals(1.15, weighting.calcEdgeWeight(edge2, false), 0.01); } @Test public void testMaxSpeed() { assertEquals(155, avSpeedEnc.getMaxOrMaxStorableDecimal(), 0.1); - assertEquals(1000.0 / 72 * 3.6, createWeighting(new CustomModel(). - addToSpeed(If("true", LIMIT, "72")).setDistanceInfluence(0d)).getMinWeight(1000)); + assertEquals(1d / 72 * 3.6, createWeighting(createSpeedCustomModel(avSpeedEnc). + addToSpeed(If("true", LIMIT, "72"))).calcMinWeightPerDistance(), .001); // ignore too big limit to let custom model compatibility not break when max speed of encoded value later decreases - assertEquals(1000.0 / 155 * 3.6, createWeighting(new CustomModel(). - addToSpeed(If("true", LIMIT, "180")).setDistanceInfluence(0d)).getMinWeight(1000)); + assertEquals(1d / 155 * 3.6, createWeighting(createSpeedCustomModel(avSpeedEnc). + addToSpeed(If("true", LIMIT, "180"))).calcMinWeightPerDistance(), .001); // reduce speed only a bit - assertEquals(1000.0 / 150 * 3.6, createWeighting(new CustomModel(). + assertEquals(1d / 150 * 3.6, createWeighting(createSpeedCustomModel(avSpeedEnc). addToSpeed(If("road_class == SERVICE", MULTIPLY, "1.5")). - addToSpeed(If("true", LIMIT, "150")).setDistanceInfluence(0d)).getMinWeight(1000)); + addToSpeed(If("true", LIMIT, "150"))).calcMinWeightPerDistance(), .001); } @Test public void testMaxPriority() { - assertEquals(155, avSpeedEnc.getMaxOrMaxStorableDecimal(), 0.1); double maxSpeed = 155; - assertEquals(1000.0 / maxSpeed / 0.5 * 3.6, createWeighting(new CustomModel(). - addToPriority(If("true", MULTIPLY, "0.5")).setDistanceInfluence(0d)).getMinWeight(1000), 1.e-6); + assertEquals(maxSpeed, avSpeedEnc.getMaxOrMaxStorableDecimal(), 0.1); + assertEquals(1d / maxSpeed / 0.5 * 3.6, createWeighting(createSpeedCustomModel(avSpeedEnc). + addToPriority(If("true", MULTIPLY, "0.5"))).calcMinWeightPerDistance(), 1.e-6); // ignore too big limit - assertEquals(1000.0 / maxSpeed / 1.0 * 3.6, createWeighting(new CustomModel(). - addToPriority(If("true", LIMIT, "2.0")).setDistanceInfluence(0d)).getMinWeight(1000), 1.e-6); + assertEquals(1d / maxSpeed / 1.0 * 3.6, createWeighting(createSpeedCustomModel(avSpeedEnc). + addToPriority(If("true", LIMIT, "2.0"))).calcMinWeightPerDistance(), 1.e-6); // priority bigger 1 is fine (if CustomModel not in query) - assertEquals(1000.0 / maxSpeed / 2.0 * 3.6, createWeighting(new CustomModel(). + assertEquals(1d / maxSpeed / 2.0 * 3.6, createWeighting(createSpeedCustomModel(avSpeedEnc). addToPriority(If("true", MULTIPLY, "3.0")). - addToPriority(If("true", LIMIT, "2.0")).setDistanceInfluence(0d)).getMinWeight(1000), 1.e-6); - assertEquals(1000.0 / maxSpeed / 1.5 * 3.6, createWeighting(new CustomModel(). - addToPriority(If("true", MULTIPLY, "1.5")).setDistanceInfluence(0d)).getMinWeight(1000), 1.e-6); + addToPriority(If("true", LIMIT, "2.0"))).calcMinWeightPerDistance(), 1.e-6); + assertEquals(1d / maxSpeed / 1.5 * 3.6, createWeighting(createSpeedCustomModel(avSpeedEnc). + addToPriority(If("true", MULTIPLY, "1.5"))).calcMinWeightPerDistance(), 1.e-6); // pick maximum priority from value even if this is for a special case - assertEquals(1000.0 / maxSpeed / 3.0 * 3.6, createWeighting(new CustomModel(). - addToPriority(If("road_class == SERVICE", MULTIPLY, "3.0")).setDistanceInfluence(0d)).getMinWeight(1000), 1.e-6); + assertEquals(1d / maxSpeed / 3.0 * 3.6, createWeighting(createSpeedCustomModel(avSpeedEnc). + addToPriority(If("road_class == SERVICE", MULTIPLY, "3.0"))).calcMinWeightPerDistance(), 1.e-6); // do NOT pick maximum priority when it is for a special case - assertEquals(1000.0 / maxSpeed / 1.0 * 3.6, createWeighting(new CustomModel(). - addToPriority(If("road_class == SERVICE", MULTIPLY, "0.5")).setDistanceInfluence(0d)).getMinWeight(1000), 1.e-6); + assertEquals(1d / maxSpeed / 1.0 * 3.6, createWeighting(createSpeedCustomModel(avSpeedEnc). + addToPriority(If("road_class == SERVICE", MULTIPLY, "0.5"))).calcMinWeightPerDistance(), 1.e-6); } @Test @@ -304,8 +327,8 @@ public void tooManyStatements() { @Test public void maxSpeedViolated_bug_2307() { EdgeIteratorState motorway = graph.edge(0, 1).setDistance(10). - set(roadClassEnc, MOTORWAY).set(avSpeedEnc, 80).set(accessEnc, true, true); - CustomModel customModel = new CustomModel() + set(roadClassEnc, MOTORWAY).set(avSpeedEnc, 80); + CustomModel customModel = createSpeedCustomModel(avSpeedEnc) .setDistanceInfluence(70d) .addToSpeed(Statement.If("road_class == MOTORWAY", Statement.Op.MULTIPLY, "0.7")) .addToSpeed(Statement.Else(LIMIT, "30")); @@ -317,90 +340,71 @@ public void maxSpeedViolated_bug_2307() { @Test public void bugWithNaNForBarrierEdges() { EdgeIteratorState motorway = graph.edge(0, 1).setDistance(0). - set(roadClassEnc, MOTORWAY).set(avSpeedEnc, 80).set(accessEnc, true, true); - CustomModel customModel = new CustomModel() + set(roadClassEnc, MOTORWAY).set(avSpeedEnc, 80); + CustomModel customModel = createSpeedCustomModel(avSpeedEnc) .addToPriority(Statement.If("road_class == MOTORWAY", Statement.Op.MULTIPLY, "0")); Weighting weighting = createWeighting(customModel); assertFalse(Double.isNaN(weighting.calcEdgeWeight(motorway, false))); assertTrue(Double.isInfinite(weighting.calcEdgeWeight(motorway, false))); } - @Test - void sameTimeAsFastestWeighting() { - // we make sure the returned times are the same, so we can check for regressions more easily when we migrate from fastest to custom - FastestWeighting fastestWeighting = new FastestWeighting(accessEnc, avSpeedEnc); - Weighting customWeighting = createWeighting(new CustomModel().setDistanceInfluence(0d)); - Random rnd = new Random(); - for (int i = 0; i < 100; i++) { - double speed = 5 + rnd.nextDouble() * 100; - double distance = rnd.nextDouble() * 1000; - EdgeIteratorState edge = graph.edge(0, 1).setDistance(distance); - GHUtility.setSpeed(speed, speed, accessEnc, avSpeedEnc, edge); - long fastestMillis = fastestWeighting.calcEdgeMillis(edge, false); - long customMillis = customWeighting.calcEdgeMillis(edge, false); - assertEquals(fastestMillis, customMillis); - } - } - - private Weighting createWeighting(CustomModel vehicleModel) { - return CustomModelParser.createWeighting(accessEnc, avSpeedEnc, null, encodingManager, NO_TURN_COST_PROVIDER, vehicleModel); - } - @Test public void testMinWeightHasSameUnitAs_getWeight() { - EdgeIteratorState edge = graph.edge(0, 1).setDistance(10); - GHUtility.setSpeed(140, 0, accessEnc, avSpeedEnc, edge); - Weighting instance = CustomModelParser.createFastestWeighting(accessEnc, avSpeedEnc, encodingManager); - assertEquals(instance.getMinWeight(10), instance.calcEdgeWeight(edge, false), 1e-8); + EdgeIteratorState edge = graph.edge(0, 1).set(avSpeedEnc, 140, 0).setDistance(10); + CustomModel customModel = createSpeedCustomModel(avSpeedEnc); + Weighting weighting = createWeighting(customModel); + assertEquals(weighting.calcMinWeightPerDistance() * 10, weighting.calcEdgeWeight(edge, false), 1e-8); } @Test public void testWeightWrongHeading() { - Weighting instance = CustomModelParser.createWeighting(accessEnc, avSpeedEnc, null, encodingManager, - TurnCostProvider.NO_TURN_COST_PROVIDER, new CustomModel().setHeadingPenalty(100)); - EdgeIteratorState edge = graph.edge(1, 2).setDistance(10).setWayGeometry(Helper.createPointList(51, 0, 51, 1)); - GHUtility.setSpeed(10, 10, accessEnc, avSpeedEnc, edge); + CustomModel customModel = createSpeedCustomModel(avSpeedEnc).setHeadingPenalty(100); + Weighting weighting = createWeighting(customModel); + EdgeIteratorState edge = graph.edge(1, 2) + .set(avSpeedEnc, 10, 10) + .setDistance(10).setWayGeometry(Helper.createPointList(51, 0, 51, 1)); VirtualEdgeIteratorState virtEdge = new VirtualEdgeIteratorState(edge.getEdgeKey(), 99, 5, 6, edge.getDistance(), edge.getFlags(), edge.getKeyValues(), edge.fetchWayGeometry(FetchMode.PILLAR_ONLY), false); - double time = instance.calcEdgeWeight(virtEdge, false); + double time = weighting.calcEdgeWeight(virtEdge, false); virtEdge.setUnfavored(true); // heading penalty on edge - assertEquals(time + 100, instance.calcEdgeWeight(virtEdge, false), 1e-8); + assertEquals(time + 100, weighting.calcEdgeWeight(virtEdge, false), 1e-8); // only after setting it virtEdge.setUnfavored(true); - assertEquals(time + 100, instance.calcEdgeWeight(virtEdge, true), 1e-8); + assertEquals(time + 100, weighting.calcEdgeWeight(virtEdge, true), 1e-8); // but not after releasing it virtEdge.setUnfavored(false); - assertEquals(time, instance.calcEdgeWeight(virtEdge, true), 1e-8); + assertEquals(time, weighting.calcEdgeWeight(virtEdge, true), 1e-8); // test default penalty virtEdge.setUnfavored(true); - instance = CustomModelParser.createFastestWeighting(accessEnc, avSpeedEnc, encodingManager); - assertEquals(time + Parameters.Routing.DEFAULT_HEADING_PENALTY, instance.calcEdgeWeight(virtEdge, false), 1e-8); + customModel = createSpeedCustomModel(avSpeedEnc); + weighting = createWeighting(customModel); + assertEquals(time + Parameters.Routing.DEFAULT_HEADING_PENALTY, weighting.calcEdgeWeight(virtEdge, false), 1e-8); } @Test public void testSpeed0() { EdgeIteratorState edge = graph.edge(0, 1).setDistance(10); - Weighting instance = CustomModelParser.createFastestWeighting(accessEnc, avSpeedEnc, encodingManager); + CustomModel customModel = createSpeedCustomModel(avSpeedEnc); + Weighting weighting = createWeighting(customModel); edge.set(avSpeedEnc, 0); - assertEquals(1.0 / 0, instance.calcEdgeWeight(edge, false), 1e-8); + assertEquals(1.0 / 0, weighting.calcEdgeWeight(edge, false), 1e-8); // 0 / 0 returns NaN but calcWeight should not return NaN! edge.setDistance(0); - assertEquals(1.0 / 0, instance.calcEdgeWeight(edge, false), 1e-8); + assertEquals(1.0 / 0, weighting.calcEdgeWeight(edge, false), 1e-8); } @Test public void testTime() { - BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 4, 2, true); - EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + EncodingManager em = EncodingManager.start().add(speedEnc).build(); BaseGraph g = new BaseGraph.Builder(em).create(); - Weighting w = CustomModelParser.createFastestWeighting(accessEnc, speedEnc, encodingManager); - EdgeIteratorState edge = g.edge(0, 1).setDistance(100_000); - GHUtility.setSpeed(15, 10, accessEnc, speedEnc, edge); + EdgeIteratorState edge = g.edge(0, 1).set(speedEnc, 15, 10).setDistance(100_000); + CustomModel customModel = createSpeedCustomModel(speedEnc); + Weighting w = CustomModelParser.createWeighting(em, NO_TURN_COST_PROVIDER, customModel); assertEquals(375 * 60 * 1000, w.calcEdgeMillis(edge, false)); assertEquals(600 * 60 * 1000, w.calcEdgeMillis(edge, true)); } @@ -408,10 +412,10 @@ public void testTime() { @Test public void calcWeightAndTime_withTurnCosts() { BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); - Weighting weighting = CustomModelParser.createWeighting(accessEnc, avSpeedEnc, null, encodingManager, - new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage()), new CustomModel()); - GHUtility.setSpeed(60, true, true, accessEnc, avSpeedEnc, graph.edge(0, 1).setDistance(100)); - EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, avSpeedEnc, graph.edge(1, 2).setDistance(100)); + CustomModel customModel = createSpeedCustomModel(avSpeedEnc); + Weighting weighting = CustomModelParser.createWeighting(encodingManager, new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage()), customModel); + graph.edge(0, 1).set(avSpeedEnc, 60, 60).setDistance(100); + EdgeIteratorState edge = graph.edge(1, 2).set(avSpeedEnc, 60, 60).setDistance(100); setTurnRestriction(graph, 0, 1, 2); assertTrue(Double.isInfinite(GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0))); assertEquals(Long.MAX_VALUE, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0)); @@ -420,45 +424,30 @@ public void calcWeightAndTime_withTurnCosts() { @Test public void calcWeightAndTime_uTurnCosts() { BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); - Weighting weighting = CustomModelParser.createWeighting(accessEnc, avSpeedEnc, null, - encodingManager, new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage(), 40), new CustomModel()); - EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, avSpeedEnc, graph.edge(0, 1).setDistance(100)); + CustomModel customModel = createSpeedCustomModel(avSpeedEnc); + Weighting weighting = CustomModelParser.createWeighting(encodingManager, new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage(), 40), customModel); + EdgeIteratorState edge = graph.edge(0, 1).set(avSpeedEnc, 60, 60).setDistance(100); assertEquals(6 + 40, GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6); assertEquals((6 + 40) * 1000, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6); } - @Test - public void calcWeightAndTime_withTurnCosts_shortest() { - BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); - Weighting weighting = new ShortestWeighting(accessEnc, avSpeedEnc, - new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage())); - GHUtility.setSpeed(60, true, true, accessEnc, avSpeedEnc, graph.edge(0, 1).setDistance(100)); - EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, avSpeedEnc, graph.edge(1, 2).setDistance(100)); - setTurnRestriction(graph, 0, 1, 2); - assertTrue(Double.isInfinite(GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0))); - assertEquals(Long.MAX_VALUE, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0)); - } - @Test public void testDestinationTag() { - BooleanEncodedValue carAccessEnc = new SimpleBooleanEncodedValue("car_access", true); DecimalEncodedValue carSpeedEnc = new DecimalEncodedValueImpl("car_speed", 5, 5, false); - BooleanEncodedValue bikeAccessEnc = new SimpleBooleanEncodedValue("bike_access", true); DecimalEncodedValue bikeSpeedEnc = new DecimalEncodedValueImpl("bike_speed", 4, 2, false); - EncodingManager em = EncodingManager.start().add(carAccessEnc).add(carSpeedEnc).add(bikeAccessEnc).add(bikeSpeedEnc).build(); + EncodingManager em = EncodingManager.start().add(carSpeedEnc).add(bikeSpeedEnc).add(RoadAccess.create()).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); EdgeIteratorState edge = graph.edge(0, 1).setDistance(1000); - edge.set(carAccessEnc, true, true); - edge.set(bikeAccessEnc, true, true); edge.set(carSpeedEnc, 60); edge.set(bikeSpeedEnc, 18); EnumEncodedValue roadAccessEnc = em.getEnumEncodedValue(RoadAccess.KEY, RoadAccess.class); - Weighting weighting = CustomModelParser.createWeighting(carAccessEnc, carSpeedEnc, null, em, - TurnCostProvider.NO_TURN_COST_PROVIDER, new CustomModel().addToPriority(If("road_access == DESTINATION", MULTIPLY, ".1"))); + CustomModel customModel = createSpeedCustomModel(carSpeedEnc) + .addToPriority(If("road_access == DESTINATION", MULTIPLY, ".1")); + Weighting weighting = CustomModelParser.createWeighting(em, NO_TURN_COST_PROVIDER, customModel); - Weighting bikeWeighting = CustomModelParser.createWeighting(bikeAccessEnc, bikeSpeedEnc, null, em, - TurnCostProvider.NO_TURN_COST_PROVIDER, new CustomModel()); + CustomModel bikeCustomModel = createSpeedCustomModel(bikeSpeedEnc); + Weighting bikeWeighting = CustomModelParser.createWeighting(em, NO_TURN_COST_PROVIDER, bikeCustomModel); edge.set(roadAccessEnc, RoadAccess.YES); assertEquals(60, weighting.calcEdgeWeight(edge, false), 1.e-6); @@ -472,24 +461,22 @@ public void testDestinationTag() { @Test public void testPrivateTag() { - BooleanEncodedValue carAccessEnc = new SimpleBooleanEncodedValue("car_access", true); DecimalEncodedValue carSpeedEnc = new DecimalEncodedValueImpl("car_speed", 5, 5, false); - BooleanEncodedValue bikeAccessEnc = new SimpleBooleanEncodedValue("bike_access", true); DecimalEncodedValue bikeSpeedEnc = new DecimalEncodedValueImpl("bike_speed", 4, 2, false); - EncodingManager em = EncodingManager.start().add(carAccessEnc).add(carSpeedEnc).add(bikeAccessEnc).add(bikeSpeedEnc).build(); + EncodingManager em = EncodingManager.start().add(carSpeedEnc).add(bikeSpeedEnc).add(RoadAccess.create()).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); EdgeIteratorState edge = graph.edge(0, 1).setDistance(1000); - edge.set(carAccessEnc, true, true); - edge.set(bikeAccessEnc, true, true); edge.set(carSpeedEnc, 60); edge.set(bikeSpeedEnc, 18); EnumEncodedValue roadAccessEnc = em.getEnumEncodedValue(RoadAccess.KEY, RoadAccess.class); - Weighting weighting = CustomModelParser.createWeighting(carAccessEnc, carSpeedEnc, null, em, - TurnCostProvider.NO_TURN_COST_PROVIDER, new CustomModel().addToPriority(If("road_access == PRIVATE", MULTIPLY, ".1"))); + CustomModel customModel = createSpeedCustomModel(carSpeedEnc) + .addToPriority(If("road_access == PRIVATE", MULTIPLY, ".1")); + Weighting weighting = CustomModelParser.createWeighting(em, NO_TURN_COST_PROVIDER, customModel); - Weighting bikeWeighting = CustomModelParser.createWeighting(bikeAccessEnc, bikeSpeedEnc, null, em, - TurnCostProvider.NO_TURN_COST_PROVIDER, new CustomModel().addToPriority(If("road_access == PRIVATE", MULTIPLY, "0.8333"))); + customModel = createSpeedCustomModel(bikeSpeedEnc) + .addToPriority(If("road_access == PRIVATE", MULTIPLY, "0.8333")); + Weighting bikeWeighting = CustomModelParser.createWeighting(em, NO_TURN_COST_PROVIDER, customModel); ReaderWay way = new ReaderWay(1); way.setTag("highway", "secondary"); @@ -503,8 +490,4 @@ public void testPrivateTag() { // private should influence bike only slightly assertEquals(240, bikeWeighting.calcEdgeWeight(edge, false), .01); } - - private void setTurnRestriction(Graph graph, int from, int via, int to) { - graph.getTurnCostStorage().set(turnRestrictionEnc, getEdge(graph, from, via).getEdge(), via, getEdge(graph, via, to).getEdge(), true); - } } diff --git a/core/src/test/java/com/graphhopper/routing/weighting/custom/FindMinMaxTest.java b/core/src/test/java/com/graphhopper/routing/weighting/custom/FindMinMaxTest.java index 1d0452710e7..16ca32eb019 100644 --- a/core/src/test/java/com/graphhopper/routing/weighting/custom/FindMinMaxTest.java +++ b/core/src/test/java/com/graphhopper/routing/weighting/custom/FindMinMaxTest.java @@ -3,6 +3,7 @@ import com.graphhopper.json.MinMax; import com.graphhopper.json.Statement; import com.graphhopper.routing.ev.EncodedValueLookup; +import com.graphhopper.routing.ev.RoadEnvironment; import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.util.CustomModel; import org.junit.jupiter.api.BeforeEach; @@ -10,15 +11,13 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; import java.util.List; import static com.graphhopper.json.Statement.*; import static com.graphhopper.json.Statement.Op.LIMIT; import static com.graphhopper.json.Statement.Op.MULTIPLY; import static com.graphhopper.routing.weighting.custom.FindMinMax.findMinMax; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.*; class FindMinMaxTest { @@ -26,7 +25,7 @@ class FindMinMaxTest { @BeforeEach void setup() { - lookup = new EncodingManager.Builder().build(); + lookup = new EncodingManager.Builder().add(RoadEnvironment.create()).build(); } @Test @@ -42,21 +41,21 @@ public void testCheck() { public void testFindMax() { List statements = new ArrayList<>(); statements.add(If("true", LIMIT, "100")); - assertEquals(100, findMinMax(new HashSet<>(), new MinMax(0, 120), statements, lookup).max); + assertEquals(100, findMinMax(new MinMax(0, 120), statements, lookup).max); statements.add(Else(LIMIT, "20")); - assertEquals(100, findMinMax(new HashSet<>(), new MinMax(0, 120), statements, lookup).max); + assertEquals(100, findMinMax(new MinMax(0, 120), statements, lookup).max); statements = new ArrayList<>(); statements.add(If("road_environment == BRIDGE", LIMIT, "85")); statements.add(Else(LIMIT, "100")); - assertEquals(100, findMinMax(new HashSet<>(), new MinMax(0, 120), statements, lookup).max); + assertEquals(100, findMinMax(new MinMax(0, 120), statements, lookup).max); // find bigger speed than stored max_speed (30) in server-side custom_models statements = new ArrayList<>(); statements.add(If("true", MULTIPLY, "2")); statements.add(If("true", LIMIT, "35")); - assertEquals(35, findMinMax(new HashSet<>(), new MinMax(0, 30), statements, lookup).max); + assertEquals(35, findMinMax(new MinMax(0, 30), statements, lookup).max); } @Test @@ -67,25 +66,24 @@ public void findMax_limitAndMultiply() { ElseIf("road_class == PRIMARY", LIMIT, "30"), Else(LIMIT, "3") ); - assertEquals(140, findMinMax(new HashSet<>(), new MinMax(0, 140), statements, lookup).max); + assertEquals(140, findMinMax(new MinMax(0, 140), statements, lookup).max); } @Test public void testFindMaxPriority() { List statements = new ArrayList<>(); statements.add(If("true", MULTIPLY, "2")); - assertEquals(2, findMinMax(new HashSet<>(), new MinMax(0, 1), statements, lookup).max); + assertEquals(2, findMinMax(new MinMax(0, 1), statements, lookup).max); - statements = new ArrayList<>(); - statements.add(If("true", MULTIPLY, "0.5")); - assertEquals(0.5, findMinMax(new HashSet<>(), new MinMax(0, 1), statements, lookup).max); + List statements2 = new ArrayList<>(); + statements2.add(If("true", MULTIPLY, "0.5")); + assertEquals(0.5, findMinMax(new MinMax(0, 1), statements2, lookup).max); - statements = new ArrayList<>(); - statements.add(If("road_class == MOTORWAY", MULTIPLY, "0.5")); - statements.add(Else(MULTIPLY, "-0.5")); - MinMax minMax = findMinMax(new HashSet<>(), new MinMax(1, 1), statements, lookup); - assertEquals(-0.5, minMax.min); - assertEquals(0.5, minMax.max); + List statements3 = new ArrayList<>(); + statements3.add(If("road_class == MOTORWAY", MULTIPLY, "0.5")); + statements3.add(Else(MULTIPLY, "-0.5")); + IllegalArgumentException m = assertThrows(IllegalArgumentException.class, () -> findMinMax(new MinMax(1, 1), statements3, lookup)); + assertTrue(m.getMessage().startsWith("statement resulted in negative value")); } @Test @@ -97,9 +95,9 @@ public void findMax_multipleBlocks() { ElseIf("road_environment == BRIDGE", LIMIT, "50"), Else(MULTIPLY, "0.8") ); - assertEquals(120, findMinMax(new HashSet<>(), new MinMax(0, 150), statements, lookup).max); - assertEquals(80, findMinMax(new HashSet<>(), new MinMax(0, 100), statements, lookup).max); - assertEquals(60, findMinMax(new HashSet<>(), new MinMax(0, 60), statements, lookup).max); + assertEquals(120, findMinMax(new MinMax(0, 150), statements, lookup).max); + assertEquals(80, findMinMax(new MinMax(0, 100), statements, lookup).max); + assertEquals(60, findMinMax(new MinMax(0, 60), statements, lookup).max); statements = Arrays.asList( If("road_class == TERTIARY", MULTIPLY, "0.2"), @@ -108,7 +106,7 @@ public void findMax_multipleBlocks() { If("road_environment == TUNNEL", MULTIPLY, "0.8"), ElseIf("road_environment == BRIDGE", LIMIT, "30") ); - assertEquals(40, findMinMax(new HashSet<>(), new MinMax(0, 150), statements, lookup).max); - assertEquals(40, findMinMax(new HashSet<>(), new MinMax(0, 40), statements, lookup).max); + assertEquals(40, findMinMax(new MinMax(0, 150), statements, lookup).max); + assertEquals(40, findMinMax(new MinMax(0, 40), statements, lookup).max); } -} \ No newline at end of file +} diff --git a/core/src/test/java/com/graphhopper/routing/weighting/custom/ValueExpressionVisitorTest.java b/core/src/test/java/com/graphhopper/routing/weighting/custom/ValueExpressionVisitorTest.java index 03b03ed6204..ce7440699bc 100644 --- a/core/src/test/java/com/graphhopper/routing/weighting/custom/ValueExpressionVisitorTest.java +++ b/core/src/test/java/com/graphhopper/routing/weighting/custom/ValueExpressionVisitorTest.java @@ -9,11 +9,9 @@ import org.junit.jupiter.api.Test; import java.util.Arrays; -import java.util.HashSet; import java.util.Set; -import static com.graphhopper.routing.weighting.custom.ValueExpressionVisitor.findMinMax; -import static com.graphhopper.routing.weighting.custom.ValueExpressionVisitor.parse; +import static com.graphhopper.routing.weighting.custom.ValueExpressionVisitor.*; import static org.junit.jupiter.api.Assertions.*; class ValueExpressionVisitorTest { @@ -68,25 +66,48 @@ public void isValidAndSimpleCondition() { @Test public void testErrors() { - Set objs = new HashSet<>(); DecimalEncodedValue prio1 = new DecimalEncodedValueImpl("my_priority", 5, 1, false); IntEncodedValueImpl prio2 = new IntEncodedValueImpl("my_priority2", 5, -5, false, false); EncodedValueLookup lookup = new EncodingManager.Builder().add(prio1).add(prio2).build(); - String msg = assertThrows(IllegalArgumentException.class, () -> findMinMax(objs, "unknown*3", lookup)).getMessage(); + String msg = assertThrows(IllegalArgumentException.class, () -> findMinMax("unknown*3", lookup)).getMessage(); assertTrue(msg.contains("'unknown' not available"), msg); - msg = assertThrows(IllegalArgumentException.class, () -> findMinMax(objs, "my_priority - my_priority2 * 3", lookup)).getMessage(); + msg = assertThrows(IllegalArgumentException.class, () -> findMinMax("my_priority - my_priority2 * 3", lookup)).getMessage(); assertTrue(msg.contains("a single EncodedValue"), msg); // unary minus is also a minus operator - msg = assertThrows(IllegalArgumentException.class, () -> findMinMax(objs, "-my_priority + my_priority2 * 3", lookup)).getMessage(); + msg = assertThrows(IllegalArgumentException.class, () -> findMinMax("-my_priority + my_priority2 * 3", lookup)).getMessage(); assertTrue(msg.contains("a single EncodedValue"), msg); - msg = assertThrows(IllegalArgumentException.class, () -> findMinMax(objs, "1/my_priority", lookup)).getMessage(); + msg = assertThrows(IllegalArgumentException.class, () -> findMinMax("1/my_priority", lookup)).getMessage(); assertTrue(msg.contains("invalid operation '/'"), msg); - msg = assertThrows(IllegalArgumentException.class, () -> findMinMax(objs, "my_priority*my_priority2 * 3", lookup)).getMessage(); + msg = assertThrows(IllegalArgumentException.class, () -> findMinMax("my_priority*my_priority2 * 3", lookup)).getMessage(); assertTrue(msg.contains("Currently only a single EncodedValue is allowed on the right-hand side"), msg); + + msg = assertThrows(IllegalArgumentException.class, () -> findVariables("unknown*3", lookup)).getMessage(); + assertTrue(msg.contains("'unknown' not available"), msg); + + msg = assertThrows(IllegalArgumentException.class, () -> findVariables("my_priority - my_priority2 * 3", lookup)).getMessage(); + assertTrue(msg.contains("a single EncodedValue"), msg); + // unary minus is also a minus operator + msg = assertThrows(IllegalArgumentException.class, () -> findVariables("-my_priority + my_priority2 * 3", lookup)).getMessage(); + assertTrue(msg.contains("a single EncodedValue"), msg); + + msg = assertThrows(IllegalArgumentException.class, () -> findVariables("1/my_priority", lookup)).getMessage(); + assertTrue(msg.contains("invalid operation '/'"), msg); + + msg = assertThrows(IllegalArgumentException.class, () -> findVariables("my_priority*my_priority2 * 3", lookup)).getMessage(); + assertTrue(msg.contains("Currently only a single EncodedValue is allowed on the right-hand side"), msg); + + msg = assertThrows(IllegalArgumentException.class, () -> findVariables("my_prio*my_priority2 * 3", lookup)).getMessage(); + assertEquals("'my_prio' not available", msg); + + msg = assertThrows(IllegalArgumentException.class, () -> findVariables("-0.5", lookup)).getMessage(); + assertEquals("illegal expression as it can result in a negative weight: -0.5", msg); + + msg = assertThrows(IllegalArgumentException.class, () -> findVariables("-my_priority", lookup)).getMessage(); + assertEquals("illegal expression as it can result in a negative weight: -my_priority", msg); } @Test @@ -102,9 +123,22 @@ public void runMaxMin() { assertInterval(-52, 10, "-2*my_priority2", lookup); } + @Test + public void runVariables() { + DecimalEncodedValue prio1 = new DecimalEncodedValueImpl("my_priority", 5, 1, false); + IntEncodedValueImpl prio2 = new IntEncodedValueImpl("my_priority2", 5, -5, false, false); + EncodedValueLookup lookup = new EncodingManager.Builder().add(prio1).add(prio2).build(); + + assertEquals(Set.of(), findVariables("2", lookup)); + assertEquals(Set.of("my_priority"), findVariables("2*my_priority", lookup)); + + Exception ex = assertThrows(IllegalArgumentException.class, () -> findVariables("-2*my_priority", lookup)); + assertTrue(ex.getMessage().contains("illegal expression as it can result in a negative weight")); + } + void assertInterval(double min, double max, String expression, EncodedValueLookup lookup) { - MinMax minmax = findMinMax(new HashSet<>(), expression, lookup); + MinMax minmax = findMinMax(expression, lookup); assertEquals(min, minmax.min, 0.1, expression); assertEquals(max, minmax.max, 0.1, expression); } -} \ No newline at end of file +} diff --git a/core/src/test/java/com/graphhopper/search/KVStorageTest.java b/core/src/test/java/com/graphhopper/search/KVStorageTest.java index 1c1a3fe167d..8c3820ac013 100644 --- a/core/src/test/java/com/graphhopper/search/KVStorageTest.java +++ b/core/src/test/java/com/graphhopper/search/KVStorageTest.java @@ -400,6 +400,6 @@ public void testMax() { long pointer = Integer.MAX_VALUE; int storedPointer = (int) (pointer + 100); assertTrue(storedPointer < 0); - assertEquals(pointer + 100, BitUtil.toUnsignedLong(storedPointer)); + assertEquals(pointer + 100, Integer.toUnsignedLong(storedPointer)); } } diff --git a/core/src/test/java/com/graphhopper/storage/AbstractGraphStorageTester.java b/core/src/test/java/com/graphhopper/storage/AbstractGraphStorageTester.java index c0edccbbcbf..f580cfde7ad 100644 --- a/core/src/test/java/com/graphhopper/storage/AbstractGraphStorageTester.java +++ b/core/src/test/java/com/graphhopper/storage/AbstractGraphStorageTester.java @@ -17,10 +17,7 @@ */ package com.graphhopper.storage; -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValueImpl; -import com.graphhopper.routing.ev.SimpleBooleanEncodedValue; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.AccessFilter; import com.graphhopper.routing.util.EdgeFilter; import com.graphhopper.routing.util.EncodingManager; @@ -57,6 +54,7 @@ protected EncodingManager createEncodingManager() { return new EncodingManager.Builder() .add(carAccessEnc).add(carSpeedEnc) .add(footAccessEnc).add(footSpeedEnc) + .add(RoadClass.create()) .build(); } diff --git a/core/src/test/java/com/graphhopper/storage/BaseGraphWithTurnCostsTest.java b/core/src/test/java/com/graphhopper/storage/BaseGraphWithTurnCostsTest.java index addeff1bf63..68df0eec0bb 100644 --- a/core/src/test/java/com/graphhopper/storage/BaseGraphWithTurnCostsTest.java +++ b/core/src/test/java/com/graphhopper/storage/BaseGraphWithTurnCostsTest.java @@ -18,6 +18,7 @@ package com.graphhopper.storage; import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.RoadClass; import com.graphhopper.routing.ev.TurnCost; import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.search.KVStorage.KeyValue; @@ -44,6 +45,7 @@ protected EncodingManager createEncodingManager() { return EncodingManager.start() .add(carAccessEnc).add(carSpeedEnc).addTurnCostEncodedValue(turnCostEnc) .add(footAccessEnc).add(footSpeedEnc) + .add(RoadClass.create()) .build(); } diff --git a/core/src/test/java/com/graphhopper/util/BitUtilLittleTest.java b/core/src/test/java/com/graphhopper/util/BitUtilLittleTest.java index a8cd91a01b4..71cadf3f09a 100644 --- a/core/src/test/java/com/graphhopper/util/BitUtilLittleTest.java +++ b/core/src/test/java/com/graphhopper/util/BitUtilLittleTest.java @@ -67,20 +67,20 @@ public void testCountBitValue() { @Test public void testUnsignedConversions() { - long l = BitUtil.toUnsignedLong(-1); + long l = Integer.toUnsignedLong(-1); assertEquals(4294967295L, l); assertEquals(-1, BitUtil.toSignedInt(l)); int intVal = Integer.MAX_VALUE; - long maxInt = (long) intVal; + long maxInt = intVal; assertEquals(intVal, BitUtil.toSignedInt(maxInt)); intVal++; - maxInt = BitUtil.toUnsignedLong(intVal); + maxInt = Integer.toUnsignedLong(intVal); assertEquals(intVal, BitUtil.toSignedInt(maxInt)); intVal++; - maxInt = BitUtil.toUnsignedLong(intVal); + maxInt = Integer.toUnsignedLong(intVal); assertEquals(intVal, BitUtil.toSignedInt(maxInt)); assertEquals(0xFFFFffffL, (1L << 32) - 1); diff --git a/core/src/test/java/com/graphhopper/util/GHUtilityTest.java b/core/src/test/java/com/graphhopper/util/GHUtilityTest.java index f9cbb1ac2c6..eb2f2d746ff 100644 --- a/core/src/test/java/com/graphhopper/util/GHUtilityTest.java +++ b/core/src/test/java/com/graphhopper/util/GHUtilityTest.java @@ -35,94 +35,6 @@ * @author Peter Karich */ public class GHUtilityTest { - private final BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); - private final DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); - private final EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).build(); - - BaseGraph createGraph() { - return new BaseGraph.Builder(encodingManager).create(); - } - - // 7 8\ - // | \ | 2 - // | 5 | | - // 3 4 | | - // 6 \1 - // ______/ - // 0/ - Graph initUnsorted(Graph g, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { - NodeAccess na = g.getNodeAccess(); - na.setNode(0, 0, 1); - na.setNode(1, 2.5, 4.5); - na.setNode(2, 4.5, 4.5); - na.setNode(3, 3, 0.5); - na.setNode(4, 2.8, 2.8); - na.setNode(5, 4.2, 1.6); - na.setNode(6, 2.3, 2.2); - na.setNode(7, 5, 1.5); - na.setNode(8, 4.6, 4); - GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(8, 2).setDistance(0.5)); - GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(7, 3).setDistance(2.1)); - GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(1, 0).setDistance(3.9)); - GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(7, 5).setDistance(0.7)); - GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(1, 2).setDistance(1.9)); - GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(8, 1).setDistance(2.05)); - return g; - } - - double getLengthOfAllEdges(Graph graph) { - double distance = 0; - DistanceCalc calc = new DistanceCalcEuclidean(); - AllEdgesIterator iter = graph.getAllEdges(); - while (iter.next()) { - // This is meant to verify that all of the same edges (including tower nodes) - // are included in the copied graph. Can not use iter.getDistance() since it - // does not verify new geometry. See #1732 - distance += calc.calcDistance(iter.fetchWayGeometry(FetchMode.ALL)); - } - return distance; - } - - @Test - public void testSort() { - Graph g = initUnsorted(createGraph(), accessEnc, speedEnc); - Graph newG = GHUtility.sortDFS(g, createGraph()); - assertEquals(g.getNodes(), newG.getNodes()); - assertEquals(g.getEdges(), newG.getEdges()); - NodeAccess na = newG.getNodeAccess(); - assertEquals(0, na.getLat(0), 1e-4); // 0 - assertEquals(2.5, na.getLat(1), 1e-4); // 1 - assertEquals(4.5, na.getLat(2), 1e-4); // 2 - assertEquals(4.6, na.getLat(3), 1e-4); // 8 - assertEquals(3.0, na.getLat(4), 1e-4); // 3 - assertEquals(5.0, na.getLat(5), 1e-4); // 7 - assertEquals(4.2, na.getLat(6), 1e-4); // 5 - assertEquals(getLengthOfAllEdges(g), getLengthOfAllEdges(newG), 1e-4); - - // 0 => 1 - assertEquals(0, newG.getEdgeIteratorState(0, Integer.MIN_VALUE).getAdjNode()); - assertEquals(1, newG.getEdgeIteratorState(0, Integer.MIN_VALUE).getBaseNode()); - - // 1 => 3 (was 8) - assertEquals(1, newG.getEdgeIteratorState(1, Integer.MIN_VALUE).getAdjNode()); - assertEquals(3, newG.getEdgeIteratorState(1, Integer.MIN_VALUE).getBaseNode()); - - // 2 => 1 - assertEquals(2, newG.getEdgeIteratorState(2, Integer.MIN_VALUE).getAdjNode()); - assertEquals(1, newG.getEdgeIteratorState(2, Integer.MIN_VALUE).getBaseNode()); - } - - @Test - public void testSortDirected() { - Graph g = createGraph(); - NodeAccess na = g.getNodeAccess(); - na.setNode(0, 0, 1); - na.setNode(1, 2.5, 2); - na.setNode(2, 3.5, 3); - GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(0, 1).setDistance(1.1)); - GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(2, 1).setDistance(1.1)); - GHUtility.sortDFS(g, createGraph()); - } @Test public void testEdgeStuff() { diff --git a/core/src/test/java/com/graphhopper/util/InstructionListTest.java b/core/src/test/java/com/graphhopper/util/InstructionListTest.java index 26717a1e23e..eed8946f506 100644 --- a/core/src/test/java/com/graphhopper/util/InstructionListTest.java +++ b/core/src/test/java/com/graphhopper/util/InstructionListTest.java @@ -24,9 +24,10 @@ import com.graphhopper.routing.Path; import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.PriorityCode; import com.graphhopper.routing.util.TraversalMode; -import com.graphhopper.routing.weighting.*; +import com.graphhopper.routing.weighting.SpeedWeighting; +import com.graphhopper.routing.weighting.TurnCostProvider; +import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.routing.weighting.custom.CustomModelParser; import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.Graph; @@ -56,7 +57,8 @@ public class InstructionListTest { @BeforeEach public void setUp() { speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, true); - carManager = EncodingManager.start().add(speedEnc).build(); + carManager = EncodingManager.start().add(speedEnc).add(Roundabout.create()) + .add(MaxSpeed.create()).add(RoadClass.create()).add(RoadClassLink.create()).build(); } private static List getTurnDescriptions(InstructionList instructionList) { @@ -299,7 +301,8 @@ public void testNoInstructionIfSlightTurnAndAlternativeIsSharp2() { @Test public void testNoInstructionIfSlightTurnAndAlternativeIsSharp3() { DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 4, 2, true); - EncodingManager tmpEM = new EncodingManager.Builder().add(speedEnc).build(); + EncodingManager tmpEM = new EncodingManager.Builder().add(speedEnc).add(RoadClass.create()) + .add(RoadClassLink.create()).add(Roundabout.create()).add(MaxSpeed.create()).build(); EnumEncodedValue rcEV = tmpEM.getEnumEncodedValue(RoadClass.KEY, RoadClass.class); BaseGraph g = new BaseGraph.Builder(tmpEM).create(); // real world example: https://graphhopper.com/maps/?point=48.411549,15.599567&point=48.411663%2C15.600527&profile=bike @@ -337,7 +340,7 @@ public void testNoInstructionIfSlightTurnAndAlternativeIsSharp3() { @Test public void testInstructionIfTurn() { DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 4, 2, true); - EncodingManager tmpEM = new EncodingManager.Builder().add(speedEnc).build(); + EncodingManager tmpEM = new EncodingManager.Builder().add(speedEnc).add(RoadClass.create()).add(RoadClassLink.create()).add(Roundabout.create()).add(MaxSpeed.create()).build(); EnumEncodedValue rcEV = tmpEM.getEnumEncodedValue(RoadClass.KEY, RoadClass.class); BaseGraph g = new BaseGraph.Builder(tmpEM).create(); // real world example: https://graphhopper.com/maps/?point=48.412169%2C15.604888&point=48.412251%2C15.60543&profile=bike @@ -373,10 +376,9 @@ public void testInstructionIfTurn() { @Test public void testInstructionIfSlightTurn() { - BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 4, 1, false); - DecimalEncodedValue priorityEnc = new DecimalEncodedValueImpl("priority", 4, PriorityCode.getFactor(1), false); - EncodingManager tmpEM = new EncodingManager.Builder().add(accessEnc).add(speedEnc).add(priorityEnc).build(); + EncodingManager tmpEM = new EncodingManager.Builder().add(speedEnc) + .add(Roundabout.create()).add(RoadClass.create()).add(RoadClassLink.create()).add(MaxSpeed.create()).build(); BaseGraph g = new BaseGraph.Builder(tmpEM).create(); // real world example: https://graphhopper.com/maps/?point=43.729379,7.417697&point=43.729798,7.417263&profile=foot // From 4 to 3 and 4 to 1 @@ -396,20 +398,19 @@ public void testInstructionIfSlightTurn() { na.setNode(4, 43.729476, 7.417633); // default is priority=0 so set it to 1 - GHUtility.setSpeed(5, true, true, accessEnc, speedEnc, g.edge(1, 2).setDistance(20). - setKeyValues(createKV(STREET_NAME, "myroad")).set(priorityEnc, 1)); - GHUtility.setSpeed(5, true, true, accessEnc, speedEnc, g.edge(2, 3).setDistance(20). - setKeyValues(createKV(STREET_NAME, "myroad")).set(priorityEnc, 1)); + g.edge(1, 2).setDistance(20).set(speedEnc, 5). + setKeyValues(createKV(STREET_NAME, "myroad")); + g.edge(2, 3).setDistance(20).set(speedEnc, 5). + setKeyValues(createKV(STREET_NAME, "myroad")); PointList pointList = new PointList(); pointList.add(43.729627, 7.41749); - GHUtility.setSpeed(5, true, true, accessEnc, speedEnc, g.edge(2, 4).setDistance(20). - setKeyValues(createKV(STREET_NAME, "myroad")).set(priorityEnc, 1).setWayGeometry(pointList)); + g.edge(2, 4).setDistance(20).set(speedEnc, 5). + setKeyValues(createKV(STREET_NAME, "myroad")).setWayGeometry(pointList); - Weighting weighting = CustomModelParser.createWeighting(accessEnc, speedEnc, - priorityEnc, tmpEM, DefaultTurnCostProvider.NO_TURN_COST_PROVIDER, - new CustomModel().setDistanceInfluence(0d)); + Weighting weighting = new SpeedWeighting(speedEnc); Path p = new Dijkstra(g, weighting, tMode).calcPath(4, 3); assertTrue(p.isFound()); + assertEquals(IntArrayList.from(4, 2, 3), p.calcNodes()); InstructionList wayList = InstructionsFromEdges.calcInstructions(p, g, weighting, tmpEM, usTR); List tmpList = getTurnDescriptions(wayList); assertEquals(Arrays.asList("continue onto myroad", "keep right onto myroad", "arrive at destination"), tmpList); @@ -417,6 +418,7 @@ public void testInstructionIfSlightTurn() { assertEquals(20, wayList.get(1).getDistance()); p = new Dijkstra(g, weighting, tMode).calcPath(4, 1); + assertEquals(IntArrayList.from(4, 2, 1), p.calcNodes()); wayList = InstructionsFromEdges.calcInstructions(p, g, weighting, tmpEM, usTR); tmpList = getTurnDescriptions(wayList); assertEquals(Arrays.asList("continue onto myroad", "keep left onto myroad", "arrive at destination"), tmpList); @@ -428,7 +430,8 @@ public void testInstructionIfSlightTurn() { public void testInstructionWithHighlyCustomProfileWithRoadsBase() { BooleanEncodedValue roadsAccessEnc = new SimpleBooleanEncodedValue("access", true); DecimalEncodedValue roadsSpeedEnc = new DecimalEncodedValueImpl("speed", 7, 2, true); - EncodingManager tmpEM = EncodingManager.start().add(roadsAccessEnc).add(roadsSpeedEnc).build(); + EncodingManager tmpEM = EncodingManager.start().add(roadsAccessEnc).add(roadsSpeedEnc) + .add(RoadClass.create()).add(Roundabout.create()).add(RoadClassLink.create()).add(MaxSpeed.create()).build(); EnumEncodedValue rcEV = tmpEM.getEnumEncodedValue(RoadClass.KEY, RoadClass.class); BaseGraph g = new BaseGraph.Builder(tmpEM).create(); // real world example: https://graphhopper.com/maps/?point=55.691214%2C12.57065&point=55.689957%2C12.570387 @@ -453,9 +456,11 @@ public void testInstructionWithHighlyCustomProfileWithRoadsBase() { g.edge(2, 5).setDistance(10).set(roadsSpeedEnc, 10, 10).set(roadsAccessEnc, true, true).set(rcEV, RoadClass.PEDESTRIAN); CustomModel customModel = new CustomModel(); + customModel.addToSpeed(Statement.If("true", Statement.Op.LIMIT, "speed")); customModel.addToPriority(Statement.If("road_class == PEDESTRIAN", Statement.Op.MULTIPLY, "0")); - Weighting weighting = CustomModelParser.createWeighting(roadsAccessEnc, roadsSpeedEnc, null, tmpEM, TurnCostProvider.NO_TURN_COST_PROVIDER, customModel); + Weighting weighting = CustomModelParser.createWeighting(tmpEM, TurnCostProvider.NO_TURN_COST_PROVIDER, customModel); Path p = new Dijkstra(g, weighting, tMode).calcPath(3, 4); + assertEquals(IntArrayList.from(3, 2, 4), p.calcNodes()); InstructionList wayList = InstructionsFromEdges.calcInstructions(p, g, weighting, tmpEM, usTR); List tmpList = getTurnDescriptions(wayList); assertEquals(Arrays.asList("continue", "keep right", "arrive at destination"), tmpList); @@ -499,6 +504,7 @@ public void testFind() { Weighting weighting = new SpeedWeighting(speedEnc); Path p = new Dijkstra(g, weighting, tMode).calcPath(1, 5); + assertEquals(IntArrayList.from(1, 2, 3, 4, 5), p.calcNodes()); InstructionList wayList = InstructionsFromEdges.calcInstructions(p, g, weighting, carManager, usTR); // query on first edge, get instruction for second edge @@ -514,6 +520,86 @@ public void testFind() { assertNull(Instructions.find(wayList, 50.8, 50.25, 1000)); } + @Test + public void testSplitWays() { + DecimalEncodedValue roadsSpeedEnc = new DecimalEncodedValueImpl("speed", 7, 2, true); + EncodingManager tmpEM = EncodingManager.start().add(roadsSpeedEnc). + add(RoadClass.create()).add(Roundabout.create()).add(RoadClassLink.create()). + add(MaxSpeed.create()).add(Lanes.create()).build(); + IntEncodedValue lanesEnc = tmpEM.getIntEncodedValue(Lanes.KEY); + BaseGraph g = new BaseGraph.Builder(tmpEM).create(); + // real world example: https://graphhopper.com/maps/?point=43.626238%2C-79.715268&point=43.624647%2C-79.713204&profile=car + // + // 1 3 + // \ | + // 2 + // \ + // 4 + + NodeAccess na = g.getNodeAccess(); + na.setNode(1, 43.626246, -79.71522); + na.setNode(2, 43.625503, -79.714228); + na.setNode(3, 43.626285, -79.714974); + na.setNode(4, 43.625129, -79.713692); + + PointList list = new PointList(); + list.add(43.62549, -79.714292); + g.edge(1, 2).setKeyValues(createKV(STREET_NAME, "main")).setWayGeometry(list). + setDistance(110).set(roadsSpeedEnc, 50, 0).set(lanesEnc, 2); + g.edge(2, 3).setKeyValues(createKV(STREET_NAME, "main")). + setDistance(110).set(roadsSpeedEnc, 50, 0).set(lanesEnc, 3); + g.edge(2, 4).setKeyValues(createKV(STREET_NAME, "main")). + setDistance(80).set(roadsSpeedEnc, 50, 50).set(lanesEnc, 5); + + Weighting weighting = new SpeedWeighting(roadsSpeedEnc); + Path p = new Dijkstra(g, weighting, tMode).calcPath(1, 4); + InstructionList wayList = InstructionsFromEdges.calcInstructions(p, g, weighting, tmpEM, usTR); + List tmpList = getTurnDescriptions(wayList); + assertEquals(Arrays.asList("continue onto main", "arrive at destination"), tmpList); + + // Other roads should not influence instructions. Example: https://www.openstreetmap.org/node/392106581 + na.setNode(5, 43.625666,-79.714048); + g.edge(2, 5).setDistance(80).set(roadsSpeedEnc, 50, 50).set(lanesEnc, 5); + + p = new Dijkstra(g, weighting, tMode).calcPath(1, 4); + wayList = InstructionsFromEdges.calcInstructions(p, g, weighting, tmpEM, usTR); + tmpList = getTurnDescriptions(wayList); + assertEquals(Arrays.asList("continue onto main", "arrive at destination"), tmpList); + } + + @Test + public void testNotSplitWays() { + DecimalEncodedValue roadsSpeedEnc = new DecimalEncodedValueImpl("speed", 7, 2, true); + EncodingManager tmpEM = EncodingManager.start().add(roadsSpeedEnc). + add(RoadClass.create()).add(Roundabout.create()).add(RoadClassLink.create()). + add(MaxSpeed.create()).add(Lanes.create()).build(); + IntEncodedValue lanesEnc = tmpEM.getIntEncodedValue(Lanes.KEY); + BaseGraph g = new BaseGraph.Builder(tmpEM).create(); + // real world example: https://graphhopper.com/maps/?point=51.425484%2C14.223298&point=51.42523%2C14.222864&profile=car + // 3 + // | + // 1-2-4 + + NodeAccess na = g.getNodeAccess(); + na.setNode(1, 51.42523, 14.222864); + na.setNode(2, 51.425256, 14.22325); + na.setNode(3, 51.425397, 14.223266); + na.setNode(4, 51.425273, 14.223427); + + g.edge(1, 2).setKeyValues(createKV(STREET_NAME, "dresdener")). + setDistance(110).set(roadsSpeedEnc, 50, 50).set(lanesEnc, 2); + g.edge(2, 3).setKeyValues(createKV(STREET_NAME, "dresdener")). + setDistance(110).set(roadsSpeedEnc, 50, 50).set(lanesEnc, 3); + g.edge(2, 4).setKeyValues(createKV(STREET_NAME, "main")). + setDistance(80).set(roadsSpeedEnc, 50, 50).set(lanesEnc, 5); + + Weighting weighting = new SpeedWeighting(roadsSpeedEnc); + Path p = new Dijkstra(g, weighting, tMode).calcPath(3, 1); + InstructionList wayList = InstructionsFromEdges.calcInstructions(p, g, weighting, tmpEM, usTR); + List tmpList = getTurnDescriptions(wayList); + assertEquals(Arrays.asList("continue onto dresdener", "turn right onto dresdener", "arrive at destination"), tmpList); + } + private void compare(List> expected, List> actual) { for (int i = 0; i < expected.size(); i++) { List e = expected.get(i); diff --git a/core/src/test/java/com/graphhopper/util/PathSimplificationTest.java b/core/src/test/java/com/graphhopper/util/PathSimplificationTest.java index 378908cfcca..b3316851bec 100644 --- a/core/src/test/java/com/graphhopper/util/PathSimplificationTest.java +++ b/core/src/test/java/com/graphhopper/util/PathSimplificationTest.java @@ -21,13 +21,10 @@ import com.graphhopper.routing.Dijkstra; import com.graphhopper.routing.InstructionsFromEdges; import com.graphhopper.routing.Path; -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValueImpl; -import com.graphhopper.routing.ev.SimpleBooleanEncodedValue; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.routing.util.TraversalMode; -import com.graphhopper.routing.weighting.ShortestWeighting; +import com.graphhopper.routing.weighting.SpeedWeighting; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.NodeAccess; @@ -55,9 +52,9 @@ public class PathSimplificationTest { @Test public void testScenario() { - BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); - EncodingManager carManager = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + EncodingManager carManager = EncodingManager.start().add(speedEnc) + .add(Roundabout.create()).add(RoadClass.create()).add(RoadClassLink.create()).add(MaxSpeed.create()).build(); BaseGraph g = new BaseGraph.Builder(carManager).create(); // 0-1-2 // | | | @@ -78,19 +75,19 @@ public void testScenario() { na.setNode(7, 1.0, 1.1); na.setNode(8, 1.0, 1.2); - GHUtility.setSpeed(9, true, true, accessEnc, speedEnc, g.edge(0, 1).setDistance(10000)).setKeyValues(createKV(STREET_NAME, "0-1")); - GHUtility.setSpeed(9, true, true, accessEnc, speedEnc, g.edge(1, 2).setDistance(11000)).setKeyValues(createKV(STREET_NAME, "1-2")); + g.edge(0, 1).set(speedEnc, 9).setDistance(10000).setKeyValues(createKV(STREET_NAME, "0-1")); + g.edge(1, 2).set(speedEnc, 9).setDistance(11000).setKeyValues(createKV(STREET_NAME, "1-2")); - GHUtility.setSpeed(18, true, true, accessEnc, speedEnc, g.edge(0, 3).setDistance(11000)); - GHUtility.setSpeed(18, true, true, accessEnc, speedEnc, g.edge(1, 4).setDistance(10000)).setKeyValues(createKV(STREET_NAME, "1-4")); - GHUtility.setSpeed(18, true, true, accessEnc, speedEnc, g.edge(2, 5).setDistance(11000)).setKeyValues(createKV(STREET_NAME, "5-2")); + g.edge(0, 3).set(speedEnc, 18).setDistance(11000); + g.edge(1, 4).set(speedEnc, 18).setDistance(10000).setKeyValues(createKV(STREET_NAME, "1-4")); + g.edge(2, 5).set(speedEnc, 18).setDistance(11000).setKeyValues(createKV(STREET_NAME, "5-2")); - GHUtility.setSpeed(27, true, true, accessEnc, speedEnc, g.edge(3, 6).setDistance(11000)).setKeyValues(createKV(STREET_NAME, "3-6")); - GHUtility.setSpeed(27, true, true, accessEnc, speedEnc, g.edge(4, 7).setDistance(10000)).setKeyValues(createKV(STREET_NAME, "4-7")); - GHUtility.setSpeed(27, true, true, accessEnc, speedEnc, g.edge(5, 8).setDistance(10000)).setKeyValues(createKV(STREET_NAME, "5-8")); + g.edge(3, 6).set(speedEnc, 27).setDistance(11000).setKeyValues(createKV(STREET_NAME, "3-6")); + g.edge(4, 7).set(speedEnc, 27).setDistance(10000).setKeyValues(createKV(STREET_NAME, "4-7")); + g.edge(5, 8).set(speedEnc, 27).setDistance(10000).setKeyValues(createKV(STREET_NAME, "5-8")); - GHUtility.setSpeed(36, true, true, accessEnc, speedEnc, g.edge(6, 7).setDistance(11000)).setKeyValues(createKV(STREET_NAME, "6-7")); - EdgeIteratorState tmpEdge = GHUtility.setSpeed(36, true, true, accessEnc, speedEnc, g.edge(7, 8).setDistance(10000)); + g.edge(6, 7).setDistance(11000).set(speedEnc, 36).setKeyValues(createKV(STREET_NAME, "6-7")); + EdgeIteratorState tmpEdge = g.edge(7, 8).set(speedEnc, 36).setDistance(10000); PointList list = new PointList(); list.add(1.0, 1.15); list.add(1.0, 1.16); @@ -98,8 +95,8 @@ public void testScenario() { tmpEdge.setKeyValues(createKV(STREET_NAME, "7-8")); // missing edge name - GHUtility.setSpeed(45, true, true, accessEnc, speedEnc, g.edge(9, 10).setDistance(10000)); - tmpEdge = GHUtility.setSpeed(45, true, true, accessEnc, speedEnc, g.edge(8, 9).setDistance(20000)); + g.edge(9, 10).set(speedEnc, 45).setDistance(10000); + tmpEdge = g.edge(8, 9).set(speedEnc, 45).setDistance(20000); list.clear(); list.add(1.0, 1.3); list.add(1.0, 1.3001); @@ -109,11 +106,11 @@ public void testScenario() { tmpEdge.setWayGeometry(list); // Path is: [0 0-1, 3 1-4, 6 4-7, 9 7-8, 11 8-9, 10 9-10] - Weighting weighting = new ShortestWeighting(accessEnc, speedEnc); + Weighting weighting = new SpeedWeighting(speedEnc); Path p = new Dijkstra(g, weighting, tMode).calcPath(0, 10); InstructionList wayList = InstructionsFromEdges.calcInstructions(p, g, weighting, carManager, usTR); Map> details = PathDetailsFromEdges.calcDetails(p, carManager, weighting, - Arrays.asList(AVERAGE_SPEED), new PathDetailsBuilderFactory(), 0, g); + List.of(AVERAGE_SPEED), new PathDetailsBuilderFactory(), 0, g); PointList points = p.calcPoints(); PointList waypoints = new PointList(2, g.getNodeAccess().is3D()); diff --git a/docs/core/custom-models.md b/docs/core/custom-models.md index 4788ea9b48e..9d153f8dde1 100644 --- a/docs/core/custom-models.md +++ b/docs/core/custom-models.md @@ -100,7 +100,7 @@ There are also some that take on a numeric value, like: - curvature: "beeline distance" / edge_distance (0..1) e.g. a curvy road is smaller than 1 - hike_rating, horse_rating, mtb_rating: a number from 0 to 6 for the `sac_scale` in OSM, e.g. 0 means "missing", 1 means "hiking", 2 means "mountain_hiking" and so on - lanes: number of lanes -- max_slope: an unsigned decimal for the maximum slope (100 * "elevation change / distance_i") of an edge with `sum(distance_i)=edge_distance`. Important for longer road segments where ups (or downs) can be much bigger than the average_slope. +- max_slope: a signed decimal for the maximum slope (100 * "elevation change / distance_i") of an edge with `sum(distance_i)=edge_distance`. Important for longer road segments where ups (or downs) can be much bigger than the average_slope. - max_speed: the speed limit from a sign (km/h) - max_height (meter), max_width (meter), max_length (meter) - max_weight (ton), max_axle_load (in tons) diff --git a/docs/core/profiles.md b/docs/core/profiles.md index d0c3b8abad0..efb29bb5d2d 100644 --- a/docs/core/profiles.md +++ b/docs/core/profiles.md @@ -3,35 +3,33 @@ GraphHopper lets you customize how different kinds of roads shall be prioritized during its route calculations. For example when travelling long distances with a car you typically want to use the highway to minimize your travelling time. However, if you are going by bike you certainly do not want to use the highway and rather take some shorter route, -use designated bike lanes and so on. GraphHopper provides built-in vehicle types that cover some standard use cases, +use designated bike lanes and so on. GraphHopper provides built-in vehicle profiles that cover some standard use cases, which can be modified by a custom model for fine-grained control over GraphHopper's road prioritization to e.g. change the travelling speed for certain road types. -A profile is defined by a vehicle, its turn restrictions and a custom model. All profiles are specified +A profile is defined by a custom model and its turn restrictions. All profiles are specified in the 'profiles' section of `config.yml` and there has to be at least one profile. Here is an example: ```yaml profiles: - name: car - vehicle: car - custom_model_files: [] + custom_model_files: [car.json] - name: my_bike - vehicle: bike custom_model_files: [bike_elevation.json] ``` -The vehicle field must correspond to one of GraphHopper's built-in vehicle types: +By choosing a custom model file GraphHopper determines the accessibility and a default travel speed for the different road types. -- foot -- wheelchair -- bike -- racingbike -- mtb -- car +Another important profile setting is the `turn_costs` configuration. Use this to enable turn restrictions for each profile: -By choosing a vehicle GraphHopper determines the accessibility and a default travel speed for the different road types. +```yaml +profiles: + - name: car + turn_costs: + vehicle_types: [motorcar, motor_vehicle] + custom_model_files: [car.json] +``` -Another important profile setting is `turn_costs: true/false`. Use this to enable turn restrictions for each profile. You can learn more about this setting [here](./turn-restrictions.md) The profile name is used to select the profile when executing routing queries. To do this use the `profile` request diff --git a/docs/core/translations.md b/docs/core/translations.md index c18415ab5e8..ddfda3d3903 100644 --- a/docs/core/translations.md +++ b/docs/core/translations.md @@ -3,7 +3,7 @@ You can help improve GraphHopper by adding your language! We have a dedicated [forum for translations](https://discuss.graphhopper.com/c/developers/translations) in case you are unsure or want to discuss before changing things. -See [this spreadsheet](https://docs.google.com/spreadsheets/d/10HKSFmxGVEIO92loVQetVmjXT0qpf3EA2jxuQSSYTdU/edit?pli=1#gid=0) +See [this spreadsheet](https://docs.google.com/spreadsheets/d/18z00Rbt6QvLIkayEV9P89vW9oU0QbTVsjRk9nz1CeFY/edit#gid=0) and add a column for your language. Revisit it regularly to update or add new items. And see your language live at GraphHopper Maps e.g. explicitly specify the locale via: [https://graphhopper.com/maps/?point=40.979898%2C-3.164062&point=39.909736%2C-2.8125**&locale=de**](https://graphhopper.com/maps/?point=40.979898%2C-3.164062&point=39.909736%2C-2.8125&locale=de) diff --git a/docs/core/turn-restrictions.md b/docs/core/turn-restrictions.md index 63cee67b389..10bd5081538 100644 --- a/docs/core/turn-restrictions.md +++ b/docs/core/turn-restrictions.md @@ -11,26 +11,37 @@ Without turn restrictions the route will look like: ![turn with turn restrictions](./images/turn-restrictions-correct.png) -Turn restrictions have to be enabled on a vehicle basis. To enable it for one vehicle add -`|turn_costs=true` in the config, for example: `graph.vehicles=car|turn_costs=true` -and set `turn_costs: true` it for the profile too. +To enable turn restrictions for a profile set the turn_costs configuration like this: + + +```yaml +profiles: + - name: car + turn_costs: + restrictions: [motorcar, motor_vehicle] + ... +``` + +When using the Java API you can create the encoding manager like: + +```java +DecimalEncodedValue speedEnc = VehicleSpeed.create("car", 5, 5, true); +DecimalEncodedValue turnCostEnc = TurnCost.create("car", maxTurnCosts); +EncodingManager encodingManager = new EncodingManager.Builder().add(speedEnc).addTurnCostEncodedValue(turnCostEnc).build(); +``` + +Where maxTurnCosts=1 means the turn can either be legal or forbidden. -When using the Java API you can either create the encoding manager like `EncodingManager.create("car|turn_costs=true")` or -`new EncodingManager.Builder().add(new CarFlagEncoder(5, 5, 1)` where the last parameter of `CarFlagEncoder` represents -the maximum turn costs (a value of 1 means the turn can either be legal or forbidden). To enable turn restrictions when using the 'speed mode' additional graph preparation is required, because turn restrictions require edge-based (vs. node-based) traversal of the graph. You have to configure the profiles for which the graph preparation should be run using e.g. `profiles_ch`, just like when you use the 'speed mode' without turn restrictions. You can also specify a time penalty for taking u-turns in the profile (turning from one road back to the same road at a junction). -Note, that this time-penalty only works reasonably when your weighting is time-based (like "fastest"). To use u-turn -costs with speed mode you need to specify the time penalty for each u-turn (again in the profile configuration): - `u_turn_costs: 60`. See `config-example.yml` for further details regarding these configurations. -If you prepare multiple 'speed mode' profiles you have to specify which -one to use at request time: Use the `edge_based=true/false` parameter to enforce edge-based or node-based routing and -the `u_turn_costs` parameter to specify the u-turn costs (only needed if there are multiple edge-based 'speed mode' -profiles with different u-turn costs). To disable the 'speed mode' per request you can add `ch.disable=true` and choose -the value of `u_turn_costs` freely. +Note, that this time-penalty only works reasonably when your weighting is time-based (like "fastest"). To use u-turn +costs with speed mode you need to specify the time penalty for each u-turn in the turn_costs configuration: +`u_turn_costs: 60`. See `config-example.yml` for further details regarding these configurations. + +To disable the 'speed mode' per request you can add `ch.disable=true` and choose the value of `u_turn_costs` freely in the request. While OSM data only contains turn *restrictions*, the GraphHopper routing engine can also deal with turn *costs*, i.e. you can specify custom turn costs for each turn at each junction. See [this experimental branch](https://github.com/graphhopper/graphhopper/tree/turn_costs_calc). diff --git a/docs/core/weighting.md b/docs/core/weighting.md index d8a9c9eac6d..4f264dbb5b4 100644 --- a/docs/core/weighting.md +++ b/docs/core/weighting.md @@ -1,6 +1,6 @@ ## Weighting -Instead of creating a new Weighting implementation is is highly recommended to use the CustomWeighting instead, which is explained in +Instead of creating a new Weighting implementation it is highly recommended to use the CustomWeighting instead, which is explained in the [profiles](profiles.md) and [custom models](custom-models.md) section. A weighting calculates the "weight" for an edge. The weight of an edge reflects the cost of travelling along this edge. @@ -24,4 +24,4 @@ If you only want to change small parts of an existing weighting, it might be a g a sample can be found in the [AvoidEdgesWeighting](https://github.com/graphhopper/graphhopper/blob/master/core/src/main/java/com/graphhopper/routing/weighting/AvoidEdgesWeighting.java). If your weights change on a per-request base you cannot use the 'speed mode', but have to use the 'hybrid mode' or 'flexible mode' (more details [here](https://github.com/graphhopper/graphhopper#technical-overview)). If you haven't disabled the 'speed mode' in your config, you have to disable it for the requests by appending `ch.disable=true` -in the request url. \ No newline at end of file +in the request url. diff --git a/docs/migration/config-migration-08-09.md b/docs/migration/config-migration-08-09.md new file mode 100644 index 00000000000..313e7e0a7d1 --- /dev/null +++ b/docs/migration/config-migration-08-09.md @@ -0,0 +1,136 @@ +# vehicle parameter + +The `vehicle` parameter was replaced by explicit statements in the custom model. So instead of: + +```yml +profiles: + - name: car + vehicle: car + custom_model: { + "distance_influence": 10 + } +``` + +You write: + +```yml +profiles: + - name: car + custom_model: { + "priority": [ + { "if": "!car_access", "multiply_by": "0" } + ], + "speed": [ + { "if": "true", "limit_to": "car_average_speed" } + ], + "distance_influence": 10 + } +``` + +Or for vehicles with a priority encoded value you write: + +```yml +profiles: + - name: foot + custom_model: { + "priority": [ + { "if": "foot_access", "multiply_by": "foot_priority" }, + { "else": "", "multiply_by": "0" } + ], + "speed": [ + { "if": "true", "limit_to": "foot_average_speed" } + ] + } +``` + +This looks more verbose but for the standard vehicles we have created default custom model files. So instead of the above custom model for car you can also just write: + +```yml +profiles: + - name: car + custom_model_files: [car.json] +``` + +With turn costs this looks now: + +```yml +profiles: + - name: car + turn_costs: + restrictions: [motorcar, motor_vehicle] + u_turn_costs: 60 + custom_model_files: [car.json] +``` + + +Furthermore the new approach is more flexible. A profile that previously required the special vehicle `roads` (to get unlimited priority and speed) always created the encoded values roads_access and roads_average_speed and used unnecessary memory and introduced unnecessary limitations. +Now these artificial encoded values are no longer necessary and you could write custom models even without any encoded values and only one unconditional or if-else block is necessary for `speed`: + +```yml +profiles: + - name: foot + custom_model: { + "priority": [ + { "if": "road_class == MOTORWAY", "multiply_by": "0" } + ], + "speed": [ + { "if": "true", "limit_to": "6" } + ] + } +``` + +# graph.encoded_values + +All encoded values that are used in a custom models must be listed here. + +If you used a property like block_private=false for e.g. the `car` vehicle, you can now use this property for the encoded value `car_access`: + +``` + graph.encoded_values: car_access|block_private=false +``` + +# shortest and fastest weighting + +Both weightings were replaced by the custom model. Instead of `weighting: fastest` you now use the default custom weighting as +explained in the previous section. + +Instead of `weighting: shortest` you now use a custom weighting with a high `distance_influence`: + +```yml +profiles: + - name: car + custom_model: { + "priority": [ + { "if": "!car_access", "multiply_by": "0" } + ], + "speed": [ + { "if": "true", "limit_to": "car_average_speed" } + ], + "distance_influence": 200 + } +``` + +# temporal conditional access restrictions + +`car_access`, `bike_access` and `foot_access` do no longer include the conditional +access restrictions by default. If you want the old behaviour e.g. for car you need +to add the following statement in the `priority` section of your custom model: + +```json +{ "if": "car_temporal_access == NO", "multiply_by": "0" } +``` + +Depending on the use case e.g. for foot it might make more sense to use the +new default and show the conditional restriction value via the new path details +`access_conditional`, `vehicle_conditional` etc. built from the OSM tags +`access:conditional`, `vehicle:conditional` etc. +See how we utilized this for [GraphHopper Maps](https://graphhopper.com/maps/?point=50.909136%2C14.213924&point=50.90918%2C14.213549&profile=foot) +with a separate route hint (icon below the route distance). + +# max_slope + +Is now a signed value. To get the previous behavious use: + +``` +Math.abs(max_slope) +``` \ No newline at end of file diff --git a/docs/web/api-doc.md b/docs/web/api-doc.md index f6eb4e079d0..968af1f6a48 100644 --- a/docs/web/api-doc.md +++ b/docs/web/api-doc.md @@ -33,22 +33,23 @@ Please note that unlike to the GET endpoint, points are specified in `[longitude All official parameters are shown in the following table - Parameter | Default | Description -:----------------|:--------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - point | - | Specify multiple points for which the route should be calculated. The order is important. Specify at least two points. - locale | en | The locale of the resulting turn instructions. E.g. `pt_PT` for Portuguese or `de` for German - instructions | true | If instruction should be calculated and returned - profile | - | The profile to be used for the route calculation. - elevation | false | If `true` a third dimension - the elevation - is included in the polyline or in the GeoJson. IMPORTANT: If enabled you have to use a modified version of the decoding method or set points_encoded to `false`. See the points_encoded attribute for more details. Additionally a request can fail if the vehicle does not support elevation. See the features object for every vehicle. - points_encoded | true | If `false` the coordinates in `point` and `snapped_waypoints` are returned as array using the order [lon,lat,elevation] for every point. If `true` the coordinates will be encoded as string leading to less bandwidth usage. You'll need a special handling for the decoding of this string on the client-side. We provide open source code in [Java](https://github.com/graphhopper/graphhopper/blob/d70b63660ac5200b03c38ba3406b8f93976628a6/web/src/main/java/com/graphhopper/http/WebHelper.java#L43) and [JavaScript](https://github.com/graphhopper/graphhopper/blob/d70b63660ac5200b03c38ba3406b8f93976628a6/web/src/main/webapp/js/ghrequest.js#L139). It is especially important to use no 3rd party client if you set `elevation=true`! - debug | false | If true, the output will be formatted. - calc_points | true | If the points for the route should be calculated at all printing out only distance and time. - point_hint | - | Optional parameter. Specifies a hint for each `point` parameter to prefer a certain street for the closest location lookup. E.g. if there is an address or house with two or more neighboring streets you can control for which street the closest location is looked up. - snap_prevention | - | Optional parameter to avoid snapping to a certain road class or road environment. Current supported values: `motorway`, `trunk`, `ferry`, `tunnel`, `bridge` and `ford`. Multiple values are specified like `snap_prevention=ferry&snap_prevention=motorway` - details | - | Optional parameter. You can request additional details for the route: `average_speed`, `street_name`, `edge_id`, `road_class`, `road_environment`, `max_speed` and `time` (and see which other values are configured in `graph.encoded_values`). Multiple values are specified like `details=average_speed&details=time`. The returned format for one detail segment is `[fromRef, toRef, value]`. The `ref` references the points of the response. Value can also be `null` if the property does not exist for one detail segment. - curbside | any | Optional parameter applicable to edge-based routing only. It specifies on which side a query point should be relative to the driver when she leaves/arrives at a start/target/via point. Possible values: right, left, any. Specify for every point parameter. See similar heading parameter. - force_curbside | true | Optional parameter. If it is set to true there will be an exception in case the curbside parameters cannot be fulfilled (e.g. specifying the wrong side for one-ways). - timeout_ms | infinity| Optional parameter. Limits the request runtime to the minimum between the given value in milli-seconds and the server-side timeout configuration + Parameter | Default | Description +:----------------|:---------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + point | - | Specify multiple points for which the route should be calculated. The order is important. Specify at least two points. + locale | en | The locale of the resulting turn instructions. E.g. `pt_PT` for Portuguese or `de` for German + instructions | true | If instruction should be calculated and returned + profile | - | The profile to be used for the route calculation. + elevation | false | If `true` a third dimension - the elevation - is included in the polyline or in the GeoJson. IMPORTANT: If enabled you have to use a modified version of the decoding method or set points_encoded to `false`. See the points_encoded attribute for more details. Additionally a request can fail if the vehicle does not support elevation. See the features object for every vehicle. + points_encoded | true | If `false` the coordinates in `point` and `snapped_waypoints` are returned as array using the order [lon,lat,elevation] for every point. If `true` the coordinates will be encoded as string leading to less bandwidth usage. You'll need a special handling for the decoding of this string on the client-side. We provide open source code in [Java](https://github.com/graphhopper/graphhopper/blob/d70b63660ac5200b03c38ba3406b8f93976628a6/web/src/main/java/com/graphhopper/http/WebHelper.java#L43) and [JavaScript](https://github.com/graphhopper/graphhopper/blob/d70b63660ac5200b03c38ba3406b8f93976628a6/web/src/main/webapp/js/ghrequest.js#L139). It is especially important to use no 3rd party client if you set `elevation=true`! + points_encoded_encoded |1e5| Used in case `points_encoded=true` to encode the `points` string into an array of coordinates. + debug | false | If true, the output will be formatted. + calc_points | true | If the points for the route should be calculated at all printing out only distance and time. + point_hint | - | Optional parameter. Specifies a hint for each `point` parameter to prefer a certain street for the closest location lookup. E.g. if there is an address or house with two or more neighboring streets you can control for which street the closest location is looked up. + snap_prevention | - | Optional parameter to avoid snapping to a certain road class or road environment. Current supported values: `motorway`, `trunk`, `ferry`, `tunnel`, `bridge` and `ford`. Multiple values are specified like `snap_prevention=ferry&snap_prevention=motorway` + details | - | Optional parameter. You can request additional details for the route: `average_speed`, `street_name`, `edge_id`, `road_class`, `road_environment`, `max_speed` and `time` (and see which other values are configured in `graph.encoded_values`). Multiple values are specified like `details=average_speed&details=time`. The returned format for one detail segment is `[fromRef, toRef, value]`. The `ref` references the points of the response. Value can also be `null` if the property does not exist for one detail segment. + curbside | any | Optional parameter applicable to edge-based routing only. It specifies on which side a query point should be relative to the driver when she leaves/arrives at a start/target/via point. Possible values: right, left, any. Specify for every point parameter. See similar heading parameter. + force_curbside | true | Optional parameter. If it is set to true there will be an exception in case the curbside parameters cannot be fulfilled (e.g. specifying the wrong side for one-ways). + timeout_ms | infinity | Optional parameter. Limits the request runtime to the minimum between the given value in milli-seconds and the server-side timeout configuration ### Hybrid diff --git a/example/pom.xml b/example/pom.xml index b218431b05e..8e51adccb02 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -5,7 +5,7 @@ 4.0.0 graphhopper-example - 8.0-osm-reader-callbacks + 9.1-osm-reader-callbacks-SNAPSHOT jar GraphHopper Example Isochrone calculation with GraphHopper @@ -13,7 +13,7 @@ com.graphhopper graphhopper-parent - 8.0-osm-reader-callbacks + 9.1-osm-reader-callbacks-SNAPSHOT diff --git a/example/src/main/java/com/graphhopper/example/HeadingExample.java b/example/src/main/java/com/graphhopper/example/HeadingExample.java index 863b7b3738e..e6b7a0716aa 100644 --- a/example/src/main/java/com/graphhopper/example/HeadingExample.java +++ b/example/src/main/java/com/graphhopper/example/HeadingExample.java @@ -4,7 +4,6 @@ import com.graphhopper.GHResponse; import com.graphhopper.GraphHopper; import com.graphhopper.config.CHProfile; - import com.graphhopper.config.Profile; import com.graphhopper.util.CustomModel; import com.graphhopper.util.Parameters; @@ -13,6 +12,7 @@ import java.util.Arrays; import static com.graphhopper.json.Statement.If; +import static com.graphhopper.json.Statement.Op.LIMIT; import static com.graphhopper.json.Statement.Op.MULTIPLY; public class HeadingExample { @@ -34,9 +34,12 @@ static GraphHopper createGraphHopperInstance(String ghLoc) { GraphHopper hopper = new GraphHopper(); hopper.setOSMFile(ghLoc); hopper.setGraphHopperLocation("target/heading-graph-cache"); - hopper.setProfiles(new Profile("car").setCustomModel(new CustomModel(). - addToPriority(If("road_access == DESTINATION", MULTIPLY, "0.1"))). - setVehicle("car").setTurnCosts(false)); + hopper.setEncodedValuesString("car_access, road_access, car_average_speed"); + hopper.setProfiles(new Profile("car"). + setCustomModel(new CustomModel(). + addToSpeed(If("true", LIMIT, "car_average_speed")). + addToPriority(If("!car_access", MULTIPLY, "0")). + addToPriority(If("road_access == DESTINATION", MULTIPLY, "0.1")))); hopper.getCHPreparationHandler().setCHProfiles(new CHProfile("car")); hopper.importOrLoad(); return hopper; diff --git a/example/src/main/java/com/graphhopper/example/IsochroneExample.java b/example/src/main/java/com/graphhopper/example/IsochroneExample.java index 27b9af0d24c..bb2b76ab9ea 100644 --- a/example/src/main/java/com/graphhopper/example/IsochroneExample.java +++ b/example/src/main/java/com/graphhopper/example/IsochroneExample.java @@ -3,15 +3,15 @@ import com.graphhopper.GraphHopper; import com.graphhopper.config.Profile; import com.graphhopper.isochrone.algorithm.ShortestPathTree; -import com.graphhopper.routing.ev.*; +import com.graphhopper.routing.ev.Subnetwork; import com.graphhopper.routing.querygraph.QueryGraph; import com.graphhopper.routing.util.DefaultSnapFilter; import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.routing.util.TraversalMode; import com.graphhopper.routing.weighting.Weighting; -import com.graphhopper.routing.weighting.custom.CustomModelParser; - import com.graphhopper.storage.index.Snap; +import com.graphhopper.util.GHUtility; +import com.graphhopper.util.PMap; import java.util.concurrent.atomic.AtomicInteger; @@ -21,11 +21,9 @@ public static void main(String[] args) { GraphHopper hopper = createGraphHopperInstance(relDir + "core/files/andorra.osm.pbf"); // get encoder from GraphHopper instance EncodingManager encodingManager = hopper.getEncodingManager(); - BooleanEncodedValue accessEnc = encodingManager.getBooleanEncodedValue(VehicleAccess.key("car")); - DecimalEncodedValue speedEnc = encodingManager.getDecimalEncodedValue(VehicleSpeed.key("car")); // snap some GPS coordinates to the routing graph and build a query graph - Weighting weighting = CustomModelParser.createFastestWeighting(accessEnc, speedEnc, encodingManager); + Weighting weighting = hopper.createWeighting(hopper.getProfile("car"), new PMap()); Snap snap = hopper.getLocationIndex().findClosest(42.508679, 1.532078, new DefaultSnapFilter(weighting, encodingManager.getBooleanEncodedValue(Subnetwork.key("car")))); QueryGraph queryGraph = QueryGraph.create(hopper.getBaseGraph(), snap); @@ -51,7 +49,7 @@ static GraphHopper createGraphHopperInstance(String ghLoc) { GraphHopper hopper = new GraphHopper(); hopper.setOSMFile(ghLoc); hopper.setGraphHopperLocation("target/isochrone-graph-cache"); - hopper.setProfiles(new Profile("car").setVehicle("car").setTurnCosts(false)); + hopper.setProfiles(new Profile("car").setCustomModel(GHUtility.loadCustomModelFromJar("car.json"))); hopper.importOrLoad(); return hopper; } diff --git a/example/src/main/java/com/graphhopper/example/LocationIndexExample.java b/example/src/main/java/com/graphhopper/example/LocationIndexExample.java index 1c0729af0ad..eb8ac266b90 100644 --- a/example/src/main/java/com/graphhopper/example/LocationIndexExample.java +++ b/example/src/main/java/com/graphhopper/example/LocationIndexExample.java @@ -9,6 +9,7 @@ import com.graphhopper.storage.index.LocationIndexTree; import com.graphhopper.storage.index.Snap; import com.graphhopper.util.EdgeIteratorState; +import com.graphhopper.util.GHUtility; public class LocationIndexExample { public static void main(String[] args) { @@ -19,7 +20,8 @@ public static void main(String[] args) { public static void graphhopperLocationIndex(String relDir) { GraphHopper hopper = new GraphHopper(); - hopper.setProfiles(new Profile("car").setVehicle("car")); + hopper.setEncodedValuesString("car_access, car_average_speed"); + hopper.setProfiles(new Profile("car").setCustomModel(GHUtility.loadCustomModelFromJar("car.json"))); hopper.setOSMFile(relDir + "core/files/andorra.osm.pbf"); hopper.setGraphHopperLocation("./target/locationindex-graph-cache"); hopper.importOrLoad(); diff --git a/example/src/main/java/com/graphhopper/example/LowLevelAPIExample.java b/example/src/main/java/com/graphhopper/example/LowLevelAPIExample.java index 57a4444c185..d6bf663132b 100644 --- a/example/src/main/java/com/graphhopper/example/LowLevelAPIExample.java +++ b/example/src/main/java/com/graphhopper/example/LowLevelAPIExample.java @@ -24,6 +24,10 @@ import com.graphhopper.util.Helper; import com.graphhopper.util.PMap; +import static com.graphhopper.json.Statement.If; +import static com.graphhopper.json.Statement.Op.LIMIT; +import static com.graphhopper.json.Statement.Op.MULTIPLY; + /** * Use this example to gain access to the low level API of GraphHopper. * If you want to keep using the GraphHopper class but want to customize the internal EncodingManager @@ -79,7 +83,8 @@ public static void createAndSaveGraph() { Snap fromSnap = index.findClosest(15.15, 20.20, EdgeFilter.ALL_EDGES); Snap toSnap = index.findClosest(15.25, 20.21, EdgeFilter.ALL_EDGES); QueryGraph queryGraph = QueryGraph.create(graph, fromSnap, toSnap); - Weighting weighting = CustomModelParser.createWeighting(accessEnc, speedEnc, null, em, TurnCostProvider.NO_TURN_COST_PROVIDER, new CustomModel()); + Weighting weighting = CustomModelParser.createWeighting(em, TurnCostProvider.NO_TURN_COST_PROVIDER, + new CustomModel().addToPriority(If("!" + accessEnc.getName(), MULTIPLY, "0")).addToSpeed(If("true", LIMIT, speedEnc.getName()))); Path path = new Dijkstra(queryGraph, weighting, TraversalMode.NODE_BASED).calcPath(fromSnap.getClosestNode(), toSnap.getClosestNode()); assert Helper.round(path.getDistance(), -2) == 1500; @@ -94,12 +99,13 @@ public static void useContractionHierarchiesToMakeQueriesFaster() { BooleanEncodedValue accessEnc = VehicleAccess.create("car"); DecimalEncodedValue speedEnc = VehicleSpeed.create("car", 7, 2, false); EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); - Weighting weighting = CustomModelParser.createWeighting(accessEnc, speedEnc, null, em, TurnCostProvider.NO_TURN_COST_PROVIDER, new CustomModel()); - CHConfig chConfig = CHConfig.nodeBased("my_profile", weighting); BaseGraph graph = new BaseGraph.Builder(em) .setDir(new RAMDirectory(graphLocation, true)) .create(); graph.flush(); + Weighting weighting = CustomModelParser.createWeighting(em, TurnCostProvider.NO_TURN_COST_PROVIDER, + new CustomModel().addToPriority(If("!" + accessEnc.getName(), MULTIPLY, "0")).addToSpeed(If("true", LIMIT, speedEnc.getName()))); + CHConfig chConfig = CHConfig.nodeBased("my_profile", weighting); // Set node coordinates and build location index NodeAccess na = graph.getNodeAccess(); diff --git a/example/src/main/java/com/graphhopper/example/RoutingExample.java b/example/src/main/java/com/graphhopper/example/RoutingExample.java index a1cfb314014..4dcf5b2aeca 100644 --- a/example/src/main/java/com/graphhopper/example/RoutingExample.java +++ b/example/src/main/java/com/graphhopper/example/RoutingExample.java @@ -35,8 +35,10 @@ static GraphHopper createGraphHopperInstance(String ghLoc) { // specify where to store graphhopper files hopper.setGraphHopperLocation("target/routing-graph-cache"); + // add all encoded values that are used in the custom model, these are also available as path details or for client-side custom models + hopper.setEncodedValuesString("car_access, car_average_speed"); // see docs/core/profiles.md to learn more about profiles - hopper.setProfiles(new Profile("car").setVehicle("car").setTurnCosts(false)); + hopper.setProfiles(new Profile("car").setCustomModel(GHUtility.loadCustomModelFromJar("car.json"))); // this enables speed mode for the profile we called car hopper.getCHPreparationHandler().setCHProfiles(new CHProfile("car")); @@ -74,7 +76,7 @@ public static void routing(GraphHopper hopper) { // System.out.println("distance " + instruction.getDistance() + " for instruction: " + instruction.getTurnDescription(tr)); } assert il.size() == 6; - assert Helper.round(path.getDistance(), -2) == 900; + assert Helper.round(path.getDistance(), -2) == 600; } public static void speedModeVersusFlexibleMode(GraphHopper hopper) { @@ -83,7 +85,7 @@ public static void speedModeVersusFlexibleMode(GraphHopper hopper) { GHResponse res = hopper.route(req); if (res.hasErrors()) throw new RuntimeException(res.getErrors().toString()); - assert Helper.round(res.getBest().getDistance(), -2) == 900; + assert Helper.round(res.getBest().getDistance(), -2) == 600; } public static void alternativeRoute(GraphHopper hopper) { @@ -107,8 +109,8 @@ public static void customizableRouting(String ghLoc) { GraphHopper hopper = new GraphHopper(); hopper.setOSMFile(ghLoc); hopper.setGraphHopperLocation("target/routing-custom-graph-cache"); - CustomModel serverSideCustomModel = new CustomModel(); - hopper.setProfiles(new Profile("car_custom").setCustomModel(serverSideCustomModel).setVehicle("car")); + hopper.setEncodedValuesString("car_access, car_average_speed"); + hopper.setProfiles(new Profile("car_custom").setCustomModel(GHUtility.loadCustomModelFromJar("car.json"))); // The hybrid mode uses the "landmark algorithm" and is up to 15x faster than the flexible mode (Dijkstra). // Still it is slower than the speed mode ("contraction hierarchies algorithm") ... @@ -126,19 +128,19 @@ public static void customizableRouting(String ghLoc) { assert Math.round(res.getBest().getTime() / 1000d) == 94; - // 2. now avoid primary roads and reduce maximum speed, see docs/core/custom-models.md for an in-depth explanation + // 2. now avoid the secondary road and reduce the maximum speed, see docs/core/custom-models.md for an in-depth explanation // and also the blog posts https://www.graphhopper.com/?s=customizable+routing CustomModel model = new CustomModel(); - model.addToPriority(If("road_class == PRIMARY", MULTIPLY, "0.5")); + model.addToPriority(If("road_class == SECONDARY", MULTIPLY, "0.5")); - // unconditional limit to 100km/h - model.addToPriority(If("true", LIMIT, "100")); + // unconditional limit to 20km/h + model.addToSpeed(If("true", LIMIT, "30")); req.setCustomModel(model); res = hopper.route(req); if (res.hasErrors()) throw new RuntimeException(res.getErrors().toString()); - assert Math.round(res.getBest().getTime() / 1000d) == 164; + assert Math.round(res.getBest().getTime() / 1000d) == 184; } } diff --git a/example/src/main/java/com/graphhopper/example/RoutingExampleTC.java b/example/src/main/java/com/graphhopper/example/RoutingExampleTC.java index 4cda68fb83e..f9556de4062 100644 --- a/example/src/main/java/com/graphhopper/example/RoutingExampleTC.java +++ b/example/src/main/java/com/graphhopper/example/RoutingExampleTC.java @@ -7,9 +7,12 @@ import com.graphhopper.ResponsePath; import com.graphhopper.config.CHProfile; import com.graphhopper.config.Profile; +import com.graphhopper.config.TurnCostsConfig; +import com.graphhopper.util.GHUtility; import com.graphhopper.util.Parameters; import java.util.Arrays; +import java.util.List; import static com.graphhopper.util.Parameters.Curbsides.CURBSIDE_ANY; import static com.graphhopper.util.Parameters.Curbsides.CURBSIDE_RIGHT; @@ -23,7 +26,7 @@ public static void main(String[] args) { GraphHopper hopper = createGraphHopperInstance(relDir + "core/files/andorra.osm.pbf"); routeWithTurnCosts(hopper); routeWithTurnCostsAndCurbsides(hopper); - routeWithTurnCostsAndOtherUTurnCosts(hopper); + routeWithTurnCostsAndCurbsidesAndOtherUTurnCosts(hopper); } public static void routeWithTurnCosts(GraphHopper hopper) { @@ -36,10 +39,10 @@ public static void routeWithTurnCostsAndCurbsides(GraphHopper hopper) { GHRequest req = new GHRequest(42.50822, 1.533966, 42.506899, 1.525372). setCurbsides(Arrays.asList(CURBSIDE_ANY, CURBSIDE_RIGHT)). setProfile("car"); - route(hopper, req, 1729, 110_800); + route(hopper, req, 1370, 128_000); } - public static void routeWithTurnCostsAndOtherUTurnCosts(GraphHopper hopper) { + public static void routeWithTurnCostsAndCurbsidesAndOtherUTurnCosts(GraphHopper hopper) { GHRequest req = new GHRequest(42.50822, 1.533966, 42.506899, 1.525372) .setCurbsides(Arrays.asList(CURBSIDE_ANY, CURBSIDE_RIGHT)) // to change u-turn costs per request we have to disable CH. otherwise the u-turn costs we set per request @@ -67,12 +70,12 @@ static GraphHopper createGraphHopperInstance(String ghLoc) { GraphHopper hopper = new GraphHopper(); hopper.setOSMFile(ghLoc); hopper.setGraphHopperLocation("target/routing-tc-graph-cache"); - Profile profile = new Profile("car").setVehicle("car") - // enabling turn costs means OSM turn restriction constraints like 'no_left_turn' will be taken into account - .setTurnCosts(true) - // we can also set u_turn_costs (in seconds). by default no u-turns are allowed, but with this setting - // we will consider u-turns at all junctions with a 40s time penalty - .putHint("u_turn_costs", 40); + // add all encoded values that are used in the custom model, these are also available as path details or for client-side custom models + hopper.setEncodedValuesString("car_access, car_average_speed"); + Profile profile = new Profile("car").setCustomModel(GHUtility.loadCustomModelFromJar("car.json")) + // enabling turn costs means OSM turn restriction constraints like 'no_left_turn' will be taken into account for the specified access restrictions + // we can also set u_turn_costs (in seconds). i.e. we will consider u-turns at all junctions with a 40s time penalty + .setTurnCostsConfig(new TurnCostsConfig(List.of("motorcar", "motor_vehicle"), 40)); hopper.setProfiles(profile); // enable CH for our profile. since turn costs are enabled this will take more time and memory to prepare than // without turn costs. diff --git a/map-matching/pom.xml b/map-matching/pom.xml index db078dc0dca..4dbdd7e9256 100644 --- a/map-matching/pom.xml +++ b/map-matching/pom.xml @@ -3,14 +3,14 @@ 4.0.0 com.graphhopper graphhopper-map-matching - 8.0-osm-reader-callbacks + 9.1-osm-reader-callbacks-SNAPSHOT jar GraphHopper Map Matching com.graphhopper graphhopper-parent - 8.0-osm-reader-callbacks + 9.1-osm-reader-callbacks-SNAPSHOT diff --git a/map-matching/src/main/java/com/graphhopper/matching/MapMatching.java b/map-matching/src/main/java/com/graphhopper/matching/MapMatching.java index 876ef1fc6b1..d273396c2c6 100644 --- a/map-matching/src/main/java/com/graphhopper/matching/MapMatching.java +++ b/map-matching/src/main/java/com/graphhopper/matching/MapMatching.java @@ -69,7 +69,7 @@ public class MapMatching { private final BaseGraph graph; private final Router router; private final LocationIndexTree locationIndex; - private double measurementErrorSigma = 50.0; + private double measurementErrorSigma = 10.0; private double transitionProbabilityBeta = 2.0; private final DistanceCalc distanceCalc = new DistancePlaneProjection(); private QueryGraph queryGraph; @@ -436,6 +436,10 @@ static class Label { } private List> computeViterbiSequence(List timeSteps, boolean ignoreErrors) { + if (timeSteps.isEmpty()) { + return Collections.emptyList(); + } + final HmmProbabilities probabilities = new HmmProbabilities(measurementErrorSigma, transitionProbabilityBeta); final Map labels = new HashMap<>(); Map, Path> roadPaths = new HashMap<>(); diff --git a/navigation/pom.xml b/navigation/pom.xml index 05d0a3f8cc7..bc741f54c53 100644 --- a/navigation/pom.xml +++ b/navigation/pom.xml @@ -5,14 +5,14 @@ 4.0.0 graphhopper-nav - 8.0-osm-reader-callbacks + 9.1-osm-reader-callbacks-SNAPSHOT jar GraphHopper Navigation com.graphhopper graphhopper-parent - 8.0-osm-reader-callbacks + 9.1-osm-reader-callbacks-SNAPSHOT diff --git a/navigation/src/main/java/com/graphhopper/navigation/NavigateResponseConverter.java b/navigation/src/main/java/com/graphhopper/navigation/NavigateResponseConverter.java index ada50511e59..16fc171a03d 100644 --- a/navigation/src/main/java/com/graphhopper/navigation/NavigateResponseConverter.java +++ b/navigation/src/main/java/com/graphhopper/navigation/NavigateResponseConverter.java @@ -17,31 +17,50 @@ */ package com.graphhopper.navigation; +import static com.graphhopper.util.Parameters.Details.INTERSECTION; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; import com.graphhopper.GHResponse; import com.graphhopper.ResponsePath; import com.graphhopper.jackson.ResponsePathSerializer; -import com.graphhopper.util.*; +import com.graphhopper.util.Helper; +import com.graphhopper.util.Instruction; +import com.graphhopper.util.InstructionList; +import com.graphhopper.util.PointList; +import com.graphhopper.util.RoundaboutInstruction; +import com.graphhopper.util.TranslationMap; +import com.graphhopper.util.details.IntersectionValues; import com.graphhopper.util.details.PathDetail; - -import java.util.*; - -import static com.graphhopper.util.Parameters.Details.INTERSECTION; +import com.graphhopper.util.shapes.GHPoint3D; public class NavigateResponseConverter { - + private static final Logger LOGGER = LoggerFactory.getLogger(NavigateResponseConverter.class); private static final int VOICE_INSTRUCTION_MERGE_TRESHHOLD = 100; /** * Converts a GHResponse into a json that follows the Mapbox API specification */ - public static ObjectNode convertFromGHResponse(GHResponse ghResponse, TranslationMap translationMap, Locale locale, DistanceConfig distanceConfig) { + public static ObjectNode convertFromGHResponse(GHResponse ghResponse, TranslationMap translationMap, Locale locale, + DistanceConfig distanceConfig) { ObjectNode json = JsonNodeFactory.instance.objectNode(); if (ghResponse.hasErrors()) - throw new IllegalStateException("If the response has errors, you should use the method NavigateResponseConverter#convertFromGHResponseError"); + throw new IllegalStateException( + "If the response has errors, you should use the method NavigateResponseConverter#convertFromGHResponseError"); PointList waypoints = ghResponse.getBest().getWaypoints(); @@ -71,7 +90,8 @@ public static ObjectNode convertFromGHResponse(GHResponse ghResponse, Translatio return json; } - private static void putRouteInformation(ObjectNode pathJson, ResponsePath path, int routeNr, TranslationMap translationMap, Locale locale, DistanceConfig distanceConfig) { + private static void putRouteInformation(ObjectNode pathJson, ResponsePath path, int routeNr, + TranslationMap translationMap, Locale locale, DistanceConfig distanceConfig) { InstructionList instructions = path.getInstructions(); pathJson.put("geometry", ResponsePathSerializer.encodePolyline(path.getPoints(), false, 1e6)); @@ -95,7 +115,8 @@ private static void putRouteInformation(ObjectNode pathJson, ResponsePath path, if (instruction.getSign() != Instruction.REACHED_VIA && instruction.getSign() != Instruction.FINISH) { pointIndexTo += instructions.get(i).getPoints().size(); } - putInstruction(path.getPoints(), instructions, i, locale, translationMap, instructionJson, isFirstInstructionOfLeg, distanceConfig, intersectionDetails, pointIndexFrom, pointIndexTo); + putInstruction(path.getPoints(), instructions, i, locale, translationMap, instructionJson, + isFirstInstructionOfLeg, distanceConfig, intersectionDetails, pointIndexFrom, pointIndexTo); pointIndexFrom = pointIndexTo; time += instruction.getTime(); distance += instruction.getDistance(); @@ -122,7 +143,8 @@ private static void putRouteInformation(ObjectNode pathJson, ResponsePath path, } private static void putLegInformation(ObjectNode legJson, ResponsePath path, int i, long time, double distance) { - // TODO: Improve path descriptions, so that every path has a description, not just alternative routes + // TODO: Improve path descriptions, so that every path has a description, not + // just alternative routes String summary; if (!path.getDescription().isEmpty()) summary = String.join(",", path.getDescription()); @@ -136,51 +158,146 @@ private static void putLegInformation(ObjectNode legJson, ResponsePath path, int legJson.put("distance", Helper.round(distance, 1)); } - private static ObjectNode putInstruction(PointList points, InstructionList instructions, int instructionIndex, Locale locale, - TranslationMap translationMap, ObjectNode instructionJson, boolean isFirstInstructionOfLeg, - DistanceConfig distanceConfig, List intersectionDetails, int pointIndexFrom, - int pointIndexTo) { - Instruction instruction = instructions.get(instructionIndex); - ArrayNode intersections = instructionJson.putArray("intersections"); + /** + * filter the IntersectionDetails. + * + * first job is to find the interesting part in the interSectionDetails based on + * pointIndexFrom and pointIndexTo. + * + * Next job is to eleminate intersections colocated in the same point + * since Mapbox chokes on geometries with intersections lying ontop of + * each other. + * + * These type of intersections is used for barrier nodes + * + * We look for intersections in the lists and merge these adjacent, colocated + * intersection into each other taking the edges from both intersections and + * removing the connecting zero length edge. + * Care has to be taken that the result is sorted by bearing + */ + private static List filterIntersectionDetails(PointList points, List intersectionDetails, + int pointIndexFrom, int pointIndexTo) { + List list = new ArrayList<>(); + // job1: find out the interesting part of the intersectionDetails for (PathDetail intersectionDetail : intersectionDetails) { - if (intersectionDetail.getFirst() >= pointIndexTo) { + int first = intersectionDetail.getFirst(); + if (first >= pointIndexTo) { break; } - if (intersectionDetail.getFirst() >= pointIndexFrom) { - ObjectNode intersection = intersections.addObject(); - Map intersectionValue = (Map) intersectionDetail.getValue(); - // Location - ArrayNode locationArray = intersection.putArray("location"); - locationArray.add(Helper.round6(points.getLon(intersectionDetail.getFirst()))); - locationArray.add(Helper.round6(points.getLat(intersectionDetail.getFirst()))); - // Entry - List entries = (List) intersectionValue.getOrDefault("entries", Collections.emptyList()); - ArrayNode entryArray = intersection.putArray("entry"); - for (Boolean entry : entries) { - entryArray.add(entry); - } - // Bearings - List bearingsList = (List) intersectionValue.getOrDefault("bearings", Collections.emptyList()); - ArrayNode bearingsrray = intersection.putArray("bearings"); - for (Integer bearing : bearingsList) { - bearingsrray.add(bearing); - } - // in - if (intersectionValue.containsKey("in")) { - intersection.put("in", (int) intersectionValue.get("in")); - } - // out - if (intersectionValue.containsKey("out")) { - intersection.put("out", (int) intersectionValue.get("out")); - } + if (first >= pointIndexFrom) { + list.add(intersectionDetail); + } + } + // nothing to be done for job 2. Either no entry or only one + if (list.size() < 2) { + return list; + } + + // Now look for adjacent intersections colocated + GHPoint3D intersectionPoint = points.get(list.get(0).getFirst()); + List duplicates = new ArrayList<>(); + for (int i = 1; i < list.size(); i++) { + GHPoint3D currentIntersectionPoint = points.get(list.get(i).getFirst()); + if (intersectionPoint.equals(currentIntersectionPoint)) { + duplicates.add(i - 1); // store the first index of the duplicate } + intersectionPoint = currentIntersectionPoint; } - //Make pointList mutable + // now iterate backwards over all duplicates, since we will remove entries from + // list + for (int dup = duplicates.size() - 1; dup >= 0; dup--) { + int i = duplicates.get(dup); + // member i and i+1 are on the same point + // out edge of (i) points to in edge of (i+1) + // ... -------> intersection[i].out --------> intersection[i+1].in -----------> + // + // Create a new PathDetail for merging both intersections into one + // ... -------> intersection[i] ------> + try { + final Map intersectionMap = (Map) list.get(i).getValue(); + final List intersectionValueList = IntersectionValues.createList(intersectionMap); + + final Map nextIntersectionMap = (Map) list.get(i + 1).getValue(); + final List nextIntersectionValueList = IntersectionValues + .createList(nextIntersectionMap); + + // merge both Lists while + final List mergedInterSectionValueList = Stream.concat( + // removing out from Intersection + intersectionValueList.stream().filter(x -> !x.out), + // removing in from nextIntersection + nextIntersectionValueList.stream().filter(x -> !x.in)). + // sort the merged list by bearing + sorted((x, y) -> Integer.compare(x.bearing, y.bearing)). + // create the result list + collect(Collectors.toList()); + + // remove the duplicate Intersection from the Path (we are at "i" currently) + list.remove(i + 1); + + Map mergedIntersection = IntersectionValues + .createIntersection(mergedInterSectionValueList); + PathDetail mergedPathDetail = new PathDetail(mergedIntersection); + mergedPathDetail.setFirst(list.get(i).getFirst()); + // and replace the intersection with the merged one + list.set(i, mergedPathDetail); + } catch (ClassCastException e) { + LOGGER.warn( "Exception :" + e); + continue; + } + } + + return list; + } + + private static ObjectNode putInstruction(PointList points, InstructionList instructions, int instructionIndex, + Locale locale, + TranslationMap translationMap, ObjectNode instructionJson, boolean isFirstInstructionOfLeg, + DistanceConfig distanceConfig, List intersectionDetails, int pointIndexFrom, + int pointIndexTo) { + Instruction instruction = instructions.get(instructionIndex); + ArrayNode intersections = instructionJson.putArray("intersections"); + + // preprocess intersectionDetails + List filteredIntersectionDetails = filterIntersectionDetails(points, intersectionDetails, + pointIndexFrom, pointIndexTo); + + for (PathDetail intersectionDetail : filteredIntersectionDetails) { + ObjectNode intersection = intersections.addObject(); + Map intersectionValue = (Map) intersectionDetail.getValue(); + // Location + ArrayNode locationArray = intersection.putArray("location"); + locationArray.add(Helper.round6(points.getLon(intersectionDetail.getFirst()))); + locationArray.add(Helper.round6(points.getLat(intersectionDetail.getFirst()))); + // Entry + List entries = (List) intersectionValue.getOrDefault("entries", Collections.emptyList()); + ArrayNode entryArray = intersection.putArray("entry"); + for (Boolean entry : entries) { + entryArray.add(entry); + } + // Bearings + List bearingsList = (List) intersectionValue.getOrDefault("bearings", + Collections.emptyList()); + ArrayNode bearingsrray = intersection.putArray("bearings"); + for (Integer bearing : bearingsList) { + bearingsrray.add(bearing); + } + // in + if (intersectionValue.containsKey("in")) { + intersection.put("in", (int) intersectionValue.get("in")); + } + // out + if (intersectionValue.containsKey("out")) { + intersection.put("out", (int) intersectionValue.get("out")); + } + } + + // Make pointList mutable PointList pointList = instruction.getPoints().clone(false); - if (instructionIndex + 2 < instructions.size()) { + if (instructionIndex + 1 < instructions.size()) { // Add the first point of the next instruction PointList nextPoints = instructions.get(instructionIndex + 1).getPoints(); pointList.add(nextPoints.getLat(0), nextPoints.getLon(0), nextPoints.getEle(0)); @@ -191,7 +308,8 @@ private static ObjectNode putInstruction(PointList points, InstructionList instr if (intersections.size() == 0) { // this is the fallback if we don't have any intersections. - // this can happen for via points or finish instructions or when no intersection details have been requested + // this can happen for via points or finish instructions or when no intersection + // details have been requested ObjectNode intersection = intersections.addObject(); intersection.putArray("entry"); intersection.putArray("bearings"); @@ -220,36 +338,41 @@ private static ObjectNode putInstruction(PointList points, InstructionList instr // Voice and banner instructions are empty for the last element if (instructionIndex + 1 < instructions.size()) { - putVoiceInstructions(instructions, distance, instructionIndex, locale, translationMap, voiceInstructions, distanceConfig); + putVoiceInstructions(instructions, distance, instructionIndex, locale, translationMap, voiceInstructions, + distanceConfig); putBannerInstructions(instructions, distance, instructionIndex, locale, translationMap, bannerInstructions); } return instructionJson; } - private static void putVoiceInstructions(InstructionList instructions, double distance, int index, Locale locale, TranslationMap translationMap, - ArrayNode voiceInstructions, DistanceConfig distanceConfig) { + private static void putVoiceInstructions(InstructionList instructions, double distance, int index, Locale locale, + TranslationMap translationMap, + ArrayNode voiceInstructions, DistanceConfig distanceConfig) { /* - A VoiceInstruction Object looks like this - { - distanceAlongGeometry: 40.9, - announcement: "Exit the traffic circle", - ssmlAnnouncement: "Exit the traffic circle", - } - */ + * A VoiceInstruction Object looks like this + * { + * distanceAlongGeometry: 40.9, + * announcement: "Exit the traffic circle", + * ssmlAnnouncement: "Exit the traffic circle", + * } + */ Instruction nextInstruction = instructions.get(index + 1); String turnDescription = nextInstruction.getTurnDescription(translationMap.getWithFallBack(locale)); String thenVoiceInstruction = getThenVoiceInstructionpart(instructions, index, locale, translationMap); - List voiceValues = distanceConfig.getVoiceInstructionsForDistance(distance, turnDescription, thenVoiceInstruction); + List voiceValues = distanceConfig + .getVoiceInstructionsForDistance(distance, turnDescription, thenVoiceInstruction); for (VoiceInstructionConfig.VoiceInstructionValue voiceValue : voiceValues) { putSingleVoiceInstruction(voiceValue.spokenDistance, voiceValue.turnDescription, voiceInstructions); } // Speak 80m instructions 80 before the turn - // Note: distanceAlongGeometry: "how far from the upcoming maneuver the voice instruction should begin" + // Note: distanceAlongGeometry: "how far from the upcoming maneuver the voice + // instruction should begin" double distanceAlongGeometry = Helper.round(Math.min(distance, 80), 1); // Special case for the arrive instruction @@ -259,28 +382,36 @@ private static void putVoiceInstructions(InstructionList instructions, double di putSingleVoiceInstruction(distanceAlongGeometry, turnDescription + thenVoiceInstruction, voiceInstructions); } - private static void putSingleVoiceInstruction(double distanceAlongGeometry, String turnDescription, ArrayNode voiceInstructions) { + private static void putSingleVoiceInstruction(double distanceAlongGeometry, String turnDescription, + ArrayNode voiceInstructions) { ObjectNode voiceInstruction = voiceInstructions.addObject(); voiceInstruction.put("distanceAlongGeometry", distanceAlongGeometry); - //TODO: ideally, we would even generate instructions including the instructions after the next like turn left **then** turn right + // TODO: ideally, we would even generate instructions including the instructions + // after the next like turn left **then** turn right voiceInstruction.put("announcement", turnDescription); - voiceInstruction.put("ssmlAnnouncement", "" + turnDescription + ""); + voiceInstruction.put("ssmlAnnouncement", "" + + turnDescription + ""); } /** - * For close turns, it is important to announce the next turn in the earlier instruction. - * e.g.: instruction i+1= turn right, instruction i+2=turn left, with instruction i+1 distance < VOICE_INSTRUCTION_MERGE_TRESHHOLD + * For close turns, it is important to announce the next turn in the earlier + * instruction. + * e.g.: instruction i+1= turn right, instruction i+2=turn left, with + * instruction i+1 distance < VOICE_INSTRUCTION_MERGE_TRESHHOLD * The voice instruction should be like "turn right, then turn left" *

- * For instruction i+1 distance > VOICE_INSTRUCTION_MERGE_TRESHHOLD an empty String will be returned + * For instruction i+1 distance > VOICE_INSTRUCTION_MERGE_TRESHHOLD an empty + * String will be returned */ - private static String getThenVoiceInstructionpart(InstructionList instructions, int index, Locale locale, TranslationMap translationMap) { + private static String getThenVoiceInstructionpart(InstructionList instructions, int index, Locale locale, + TranslationMap translationMap) { if (instructions.size() > index + 2) { Instruction firstInstruction = instructions.get(index + 1); if (firstInstruction.getDistance() < VOICE_INSTRUCTION_MERGE_TRESHHOLD) { Instruction secondInstruction = instructions.get(index + 2); if (secondInstruction.getSign() != Instruction.REACHED_VIA) - return ", " + translationMap.getWithFallBack(locale).tr("navigate.then") + " " + secondInstruction.getTurnDescription(translationMap.getWithFallBack(locale)); + return ", " + translationMap.getWithFallBack(locale).tr("navigate.then") + " " + + secondInstruction.getTurnDescription(translationMap.getWithFallBack(locale)); } } @@ -288,31 +419,34 @@ private static String getThenVoiceInstructionpart(InstructionList instructions, } /** - * Banner instructions are the turn instructions that are shown to the user in the top bar. + * Banner instructions are the turn instructions that are shown to the user in + * the top bar. *

- * Between two instructions we can show multiple banner instructions, you can control when they pop up using distanceAlongGeometry. + * Between two instructions we can show multiple banner instructions, you can + * control when they pop up using distanceAlongGeometry. */ - private static void putBannerInstructions(InstructionList instructions, double distance, int index, Locale locale, TranslationMap translationMap, ArrayNode bannerInstructions) { + private static void putBannerInstructions(InstructionList instructions, double distance, int index, Locale locale, + TranslationMap translationMap, ArrayNode bannerInstructions) { /* - A BannerInstruction looks like this - distanceAlongGeometry: 107, - primary: { - text: "Lichtensteinstraße", - components: [ - { - text: "Lichtensteinstraße", - type: "text", - } - ], - type: "turn", - modifier: "right", - }, - secondary: null, + * A BannerInstruction looks like this + * distanceAlongGeometry: 107, + * primary: { + * text: "Lichtensteinstraße", + * components: [ + * { + * text: "Lichtensteinstraße", + * type: "text", + * } + * ], + * type: "turn", + * modifier: "right", + * }, + * secondary: null, */ ObjectNode bannerInstruction = bannerInstructions.addObject(); - //Show from the beginning + // Show from the beginning bannerInstruction.put("distanceAlongGeometry", distance); ObjectNode primary = bannerInstruction.putObject("primary"); @@ -327,14 +461,16 @@ private static void putBannerInstructions(InstructionList instructions, double d } } - private static void putSingleBannerInstruction(Instruction instruction, Locale locale, TranslationMap translationMap, ObjectNode singleBannerInstruction) { + private static void putSingleBannerInstruction(Instruction instruction, Locale locale, + TranslationMap translationMap, ObjectNode singleBannerInstruction) { String bannerInstructionName = instruction.getName(); if (bannerInstructionName.isEmpty()) { // Fix for final instruction and for instructions without name bannerInstructionName = instruction.getTurnDescription(translationMap.getWithFallBack(locale)); // Uppercase first letter - // TODO: should we do this for all cases? Then we might change the spelling of street names though + // TODO: should we do this for all cases? Then we might change the spelling of + // street names though bannerInstructionName = Helper.firstBig(bannerInstructionName); } @@ -363,7 +499,8 @@ private static void putSingleBannerInstruction(Instruction instruction, Locale l } } - private static void putManeuver(Instruction instruction, ObjectNode instructionJson, Locale locale, TranslationMap translationMap, boolean isFirstInstructionOfLeg) { + private static void putManeuver(Instruction instruction, ObjectNode instructionJson, Locale locale, + TranslationMap translationMap, boolean isFirstInstructionOfLeg) { ObjectNode maneuver = instructionJson.putObject("maneuver"); maneuver.put("bearing_after", 0); maneuver.put("bearing_before", 0); @@ -391,7 +528,8 @@ private static void putManeuver(Instruction instruction, ObjectNode instructionJ * roundabout (enter roundabout, maneuver contains also the exit number) * arrive (last instruction and waypoints) *

- * You can find all maneuver types at: https://www.mapbox.com/api-documentation/#maneuver-types + * You can find all maneuver types at: + * https://www.mapbox.com/api-documentation/#maneuver-types */ private static String getTurnType(Instruction instruction, boolean isFirstInstructionOfLeg) { if (isFirstInstructionOfLeg) { @@ -412,7 +550,8 @@ private static String getTurnType(Instruction instruction, boolean isFirstInstru /** * No modifier values for arrive and depart *

- * Find modifier values here: https://www.mapbox.com/api-documentation/#stepmaneuver-object + * Find modifier values here: + * https://www.mapbox.com/api-documentation/#stepmaneuver-object */ private static String getModifier(Instruction instruction) { switch (instruction.getSign()) { @@ -437,7 +576,8 @@ private static String getModifier(Instruction instruction) { case Instruction.TURN_SHARP_RIGHT: return "sharp right"; case Instruction.USE_ROUNDABOUT: - // TODO: This might be an issue in left-handed traffic, because there it schould be left + // TODO: This might be an issue in left-handed traffic, because there it schould + // be left return "right"; default: return null; diff --git a/navigation/src/test/java/com/graphhopper/navigation/NavigateResponseConverterTest.java b/navigation/src/test/java/com/graphhopper/navigation/NavigateResponseConverterTest.java index c5dea1f271c..2abf9529783 100644 --- a/navigation/src/test/java/com/graphhopper/navigation/NavigateResponseConverterTest.java +++ b/navigation/src/test/java/com/graphhopper/navigation/NavigateResponseConverterTest.java @@ -5,9 +5,11 @@ import com.graphhopper.GHRequest; import com.graphhopper.GHResponse; import com.graphhopper.GraphHopper; -import com.graphhopper.config.Profile; +import com.graphhopper.jackson.ResponsePathSerializer; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.util.Helper; import com.graphhopper.util.Parameters; +import com.graphhopper.util.PointList; import com.graphhopper.util.TranslationMap; import com.graphhopper.util.shapes.GHPoint; import org.junit.jupiter.api.AfterAll; @@ -26,7 +28,6 @@ public class NavigateResponseConverterTest { private static final String graphFolder = "target/graphhopper-test-car"; private static final String osmFile = "../core/files/andorra.osm.gz"; private static GraphHopper hopper; - private static final String vehicle = "car"; private static final String profile = "my_car"; private final TranslationMap trMap = hopper.getTranslationMap(); @@ -41,7 +42,8 @@ public static void beforeClass() { setOSMFile(osmFile). setStoreOnFlush(true). setGraphHopperLocation(graphFolder). - setProfiles(new Profile(profile).setVehicle(vehicle).setTurnCosts(false)). + setEncodedValuesString("car_access, car_average_speed"). + setProfiles(TestProfiles.accessAndSpeed(profile, "car")). importOrLoad(); } @@ -53,8 +55,7 @@ public static void afterClass() { @Test public void basicTest() { - GHResponse rsp = hopper.route(new GHRequest(42.554851, 1.536198, 42.510071, 1.548128). - setProfile(profile)); + GHResponse rsp = hopper.route(new GHRequest(42.554851, 1.536198, 42.510071, 1.548128).setProfile(profile)); ObjectNode json = NavigateResponseConverter.convertFromGHResponse(rsp, trMap, Locale.ENGLISH, distanceConfig); @@ -76,7 +77,8 @@ public void basicTest() { JsonNode step = steps.get(0); JsonNode maneuver = step.get("maneuver"); // Intersection coordinates should be equal to maneuver coordinates - assertEquals(maneuver.get("location").get(0).asDouble(), step.get("intersections").get(0).get("location").get(0).asDouble(), .00001); + assertEquals(maneuver.get("location").get(0).asDouble(), + step.get("intersections").get(0).get("location").get(0).asDouble(), .00001); assertEquals("depart", maneuver.get("type").asText()); assertEquals("straight", maneuver.get("modifier").asText()); @@ -120,11 +122,32 @@ public void basicTest() { } + @Test + public void arriveGeometryTest() { + + GHResponse rsp = hopper.route(new GHRequest(42.554851, 1.536198, 42.510071, 1.548128).setProfile(profile)); + + ObjectNode json = NavigateResponseConverter.convertFromGHResponse(rsp, trMap, Locale.ENGLISH, distanceConfig); + + JsonNode steps = json.get("routes").get(0).get("legs").get(0).get("steps"); + + // Step 17 is the last before arrival + JsonNode step = steps.get(17); + + PointList expectedArrivePointList = rsp.getBest().getInstructions().get(17).getPoints().clone(false); + PointList ghArrive = rsp.getBest().getInstructions().get(18).getPoints(); + // We expect that the Mapbox compatible response builds the geometry to the + // arrival coordinate + expectedArrivePointList.add(ghArrive); + String encodedExpected = ResponsePathSerializer.encodePolyline(expectedArrivePointList, false, 1e6); + + assertEquals(encodedExpected, step.get("geometry").asText()); + } + @Test public void voiceInstructionsTest() { - GHResponse rsp = hopper.route(new GHRequest(42.554851, 1.536198, 42.510071, 1.548128). - setProfile(profile)); + GHResponse rsp = hopper.route(new GHRequest(42.554851, 1.536198, 42.510071, 1.548128).setProfile(profile)); ObjectNode json = NavigateResponseConverter.convertFromGHResponse(rsp, trMap, Locale.ENGLISH, distanceConfig); @@ -137,7 +160,8 @@ public void voiceInstructionsTest() { assertEquals(2, voiceInstructions.size()); JsonNode voiceInstruction = voiceInstructions.get(0); assertEquals(200, voiceInstruction.get("distanceAlongGeometry").asDouble(), 1); - assertEquals("In 200 meters At roundabout, take exit 2 onto CS-340, then At roundabout, take exit 2 onto CG-3", voiceInstruction.get("announcement").asText()); + assertEquals("In 200 meters At roundabout, take exit 2 onto CS-340, then At roundabout, take exit 2 onto CG-3", + voiceInstruction.get("announcement").asText()); // Step 14 is over 3km long step = steps.get(14); @@ -155,8 +179,7 @@ public void voiceInstructionsTest() { @Test public void voiceInstructionsImperialTest() { - GHResponse rsp = hopper.route(new GHRequest(42.554851, 1.536198, 42.510071, 1.548128). - setProfile(profile)); + GHResponse rsp = hopper.route(new GHRequest(42.554851, 1.536198, 42.510071, 1.548128).setProfile(profile)); ObjectNode json = NavigateResponseConverter.convertFromGHResponse(rsp, trMap, Locale.ENGLISH, new DistanceConfig(DistanceUtils.Unit.IMPERIAL, trMap, Locale.ENGLISH)); @@ -171,7 +194,8 @@ public void voiceInstructionsImperialTest() { assertEquals(2, voiceInstructions.size()); JsonNode voiceInstruction = voiceInstructions.get(0); assertEquals(200, voiceInstruction.get("distanceAlongGeometry").asDouble(), 1); - assertEquals("In 600 feet At roundabout, take exit 2 onto CS-340, then At roundabout, take exit 2 onto CG-3", voiceInstruction.get("announcement").asText()); + assertEquals("In 600 feet At roundabout, take exit 2 onto CS-340, then At roundabout, take exit 2 onto CG-3", + voiceInstruction.get("announcement").asText()); // Step 14 is over 3km long step = steps.get(14); @@ -191,8 +215,8 @@ public void voiceInstructionsImperialTest() { @Disabled public void alternativeRoutesTest() { - GHResponse rsp = hopper.route(new GHRequest(42.554851, 1.536198, 42.510071, 1.548128). - setProfile(profile).setAlgorithm(Parameters.Algorithms.ALT_ROUTE)); + GHResponse rsp = hopper.route(new GHRequest(42.554851, 1.536198, 42.510071, 1.548128).setProfile(profile) + .setAlgorithm(Parameters.Algorithms.ALT_ROUTE)); assertEquals(2, rsp.getAll().size()); @@ -208,8 +232,7 @@ public void alternativeRoutesTest() { @Test public void voiceInstructionTranslationTest() { - GHResponse rsp = hopper.route(new GHRequest(42.554851, 1.536198, 42.510071, 1.548128). - setProfile(profile)); + GHResponse rsp = hopper.route(new GHRequest(42.554851, 1.536198, 42.510071, 1.548128).setProfile(profile)); ObjectNode json = NavigateResponseConverter.convertFromGHResponse(rsp, trMap, Locale.ENGLISH, distanceConfig); @@ -217,8 +240,8 @@ public void voiceInstructionTranslationTest() { JsonNode voiceInstruction = steps.get(14).get("voiceInstructions").get(0); assertEquals("In 2 kilometers keep right", voiceInstruction.get("announcement").asText()); - rsp = hopper.route(new GHRequest(42.554851, 1.536198, 42.510071, 1.548128). - setProfile(profile).setLocale(Locale.GERMAN)); + rsp = hopper.route( + new GHRequest(42.554851, 1.536198, 42.510071, 1.548128).setProfile(profile).setLocale(Locale.GERMAN)); DistanceConfig distanceConfigGerman = new DistanceConfig(DistanceUtils.Unit.METRIC, trMap, Locale.GERMAN); @@ -232,8 +255,7 @@ public void voiceInstructionTranslationTest() { @Test public void roundaboutDegreesTest() { - GHResponse rsp = hopper.route(new GHRequest(42.554851, 1.536198, 42.510071, 1.548128). - setProfile(profile)); + GHResponse rsp = hopper.route(new GHRequest(42.554851, 1.536198, 42.510071, 1.548128).setProfile(profile)); ObjectNode json = NavigateResponseConverter.convertFromGHResponse(rsp, trMap, Locale.ENGLISH, distanceConfig); @@ -252,8 +274,8 @@ public void roundaboutDegreesTest() { @Test public void intersectionTest() { - GHResponse rsp = hopper.route(new GHRequest(42.554851, 1.536198, 42.510071, 1.548128). - setProfile(profile).setPathDetails(Collections.singletonList("intersection"))); + GHResponse rsp = hopper.route(new GHRequest(42.554851, 1.536198, 42.510071, 1.548128).setProfile(profile) + .setPathDetails(Collections.singletonList("intersection"))); ObjectNode json = NavigateResponseConverter.convertFromGHResponse(rsp, trMap, Locale.ENGLISH, distanceConfig); @@ -280,6 +302,46 @@ public void intersectionTest() { assertEquals(42.556444, location.get(1).asDouble(), .000001); } + @Test + public void barrierTest() { + // There is a barrier https://www.openstreetmap.org/node/2206610569 on the route + GHResponse rsp = hopper.route(new GHRequest(42.601991, 1.687227, 42.601616, 1.687888).setProfile(profile) + .setPathDetails(Collections.singletonList("intersection"))); + + ObjectNode json = NavigateResponseConverter.convertFromGHResponse(rsp, trMap, Locale.ENGLISH, distanceConfig); + + JsonNode steps = json.get("routes").get(0).get("legs").get(0).get("steps"); + + JsonNode step = steps.get(1); + + JsonNode intersection = step.get("intersections").get(1); + + // checking order of entries + assertEquals(0, intersection.get("out").asInt()); + + JsonNode location = intersection.get("location"); + // The location of the barrier + assertEquals(location.get(0).asDouble(), 1.6878903, .000001); + assertEquals(location.get(1).asDouble(), 42.601764, .000001); + + int inPosition = intersection.get("in").asInt(); + int outPosition = intersection.get("out").asInt(); + JsonNode entry = intersection.get("entry"); + assertEquals(false, entry.get(inPosition).asBoolean()); + assertEquals(true, entry.get(outPosition).asBoolean()); + + JsonNode bearings = intersection.get("bearings"); + double inBearing = bearings.get(inPosition).asDouble(); + double outBearing = bearings.get(outPosition).asDouble(); + + // and these should be the bearings + assertEquals(353, inBearing); + assertEquals(171, outBearing); + + // and no additional intersection + assertEquals(step.get("intersections").size(), 2); + } + @Test public void testMultipleWaypoints() { @@ -294,7 +356,7 @@ public void testMultipleWaypoints() { ObjectNode json = NavigateResponseConverter.convertFromGHResponse(rsp, trMap, Locale.ENGLISH, distanceConfig); - //Check that all waypoints are there and in the right order + // Check that all waypoints are there and in the right order JsonNode waypointsJson = json.get("waypoints"); assertEquals(4, waypointsJson.size()); @@ -310,7 +372,7 @@ public void testMultipleWaypoints() { waypointLoc = waypointsJson.get(3).get("location"); assertEquals(1.527218, waypointLoc.get(0).asDouble(), .00001); - //Check that there are 3 legs + // Check that there are 3 legs JsonNode route = json.get("routes").get(0); JsonNode legs = route.get("legs"); assertEquals(3, legs.size()); @@ -333,15 +395,15 @@ public void testMultipleWaypoints() { assertEquals("arrive", maneuver.get("type").asText()); } - // Check if the duration and distance of the legs sum up to the overall route distance and duration + // Check if the duration and distance of the legs sum up to the overall route + // distance and duration assertEquals(route.get("duration").asDouble(), duration, 1); assertEquals(route.get("distance").asDouble(), distance, 1); } @Test public void testError() { - GHResponse rsp = hopper.route(new GHRequest(42.554851, 111.536198, 42.510071, 1.548128). - setProfile(profile)); + GHResponse rsp = hopper.route(new GHRequest(42.554851, 111.536198, 42.510071, 1.548128).setProfile(profile)); ObjectNode json = NavigateResponseConverter.convertFromGHResponseError(rsp); diff --git a/pom.xml b/pom.xml index 0e08b5e1d8e..24da031f647 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.graphhopper graphhopper-parent GraphHopper Parent Project - 8.0-osm-reader-callbacks + 9.1-osm-reader-callbacks-SNAPSHOT pom https://www.graphhopper.com 2012 @@ -16,7 +16,7 @@ UTF-8 UTF-8 - 1.8 + 17 true - 1.8 - 1.8 + 17 diff --git a/reader-gtfs/config-example-pt.yml b/reader-gtfs/config-example-pt.yml index fad859855fc..e2973fb6312 100644 --- a/reader-gtfs/config-example-pt.yml +++ b/reader-gtfs/config-example-pt.yml @@ -6,8 +6,13 @@ graphhopper: profiles: - name: foot - vehicle: foot - weighting: custom + custom_model_files: + - foot.json + # optionally add + # - foot_elevation.json + + import.osm.ignored_highways: motorway,trunk + graph.encoded_values: foot_access, foot_average_speed, hike_rating, foot_priority server: application_connectors: diff --git a/reader-gtfs/pom.xml b/reader-gtfs/pom.xml index 996a3b9cad9..5d637097afc 100644 --- a/reader-gtfs/pom.xml +++ b/reader-gtfs/pom.xml @@ -4,14 +4,14 @@ 4.0.0 graphhopper-reader-gtfs - 8.0-osm-reader-callbacks + 9.1-osm-reader-callbacks-SNAPSHOT jar GraphHopper Reader for Gtfs Data com.graphhopper graphhopper-parent - 8.0-osm-reader-callbacks + 9.1-osm-reader-callbacks-SNAPSHOT diff --git a/reader-gtfs/src/main/java/com/conveyal/gtfs/model/Entity.java b/reader-gtfs/src/main/java/com/conveyal/gtfs/model/Entity.java index ed8bf0110a8..aae6fb04ddd 100644 --- a/reader-gtfs/src/main/java/com/conveyal/gtfs/model/Entity.java +++ b/reader-gtfs/src/main/java/com/conveyal/gtfs/model/Entity.java @@ -136,9 +136,9 @@ protected int getIntField(String column, boolean required, int min, int max, int } else try { val = Integer.parseInt(str); if (mapping != null) { - Integer mappedVal = mapping.get(new Integer(val)); + Integer mappedVal = mapping.get(val); if (mappedVal != null) - val = mappedVal.intValue(); + val = mappedVal; } checkRangeInclusive(min, max, val); } catch (NumberFormatException nfe) { diff --git a/reader-gtfs/src/main/java/com/graphhopper/gtfs/GraphHopperGtfs.java b/reader-gtfs/src/main/java/com/graphhopper/gtfs/GraphHopperGtfs.java index d71ce8732ee..8812922f913 100644 --- a/reader-gtfs/src/main/java/com/graphhopper/gtfs/GraphHopperGtfs.java +++ b/reader-gtfs/src/main/java/com/graphhopper/gtfs/GraphHopperGtfs.java @@ -58,7 +58,6 @@ protected void importOSM() { super.importOSM(); } else { createBaseGraphAndProperties(); - writeEncodingManagerToProperties(); } } diff --git a/reader-gtfs/src/test/java/com/graphhopper/AnotherAgencyIT.java b/reader-gtfs/src/test/java/com/graphhopper/AnotherAgencyIT.java index 768a53156bd..e413ad8908f 100644 --- a/reader-gtfs/src/test/java/com/graphhopper/AnotherAgencyIT.java +++ b/reader-gtfs/src/test/java/com/graphhopper/AnotherAgencyIT.java @@ -18,8 +18,8 @@ package com.graphhopper; -import com.graphhopper.config.Profile; import com.graphhopper.gtfs.*; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.util.Helper; import com.graphhopper.util.TranslationMap; import org.junit.jupiter.api.AfterAll; @@ -57,9 +57,10 @@ public static void init() { ghConfig.putObject("import.osm.ignored_highways", ""); ghConfig.putObject("datareader.file", "files/beatty.osm"); ghConfig.putObject("gtfs.file", "files/sample-feed,files/another-sample-feed"); - ghConfig.setProfiles(Arrays.asList( - new Profile("foot").setVehicle("foot"), - new Profile("car").setVehicle("car"))); + ghConfig.putObject("graph.encoded_values", "foot_access, foot_priority, foot_average_speed, car_access, car_average_speed"); + ghConfig.setProfiles(List.of( + TestProfiles.accessSpeedAndPriority("foot"), + TestProfiles.accessAndSpeed("car"))); Helper.removeDir(new File(GRAPH_LOC)); graphHopperGtfs = new GraphHopperGtfs(ghConfig); graphHopperGtfs.init(ghConfig); diff --git a/reader-gtfs/src/test/java/com/graphhopper/ExtendedRouteTypeIT.java b/reader-gtfs/src/test/java/com/graphhopper/ExtendedRouteTypeIT.java index 50c8d374bd5..d23e91a5b13 100644 --- a/reader-gtfs/src/test/java/com/graphhopper/ExtendedRouteTypeIT.java +++ b/reader-gtfs/src/test/java/com/graphhopper/ExtendedRouteTypeIT.java @@ -18,11 +18,11 @@ package com.graphhopper; -import com.graphhopper.config.Profile; import com.graphhopper.gtfs.GraphHopperGtfs; import com.graphhopper.gtfs.PtRouter; import com.graphhopper.gtfs.PtRouterImpl; import com.graphhopper.gtfs.Request; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.util.Helper; import com.graphhopper.util.TranslationMap; import org.junit.jupiter.api.AfterAll; @@ -32,7 +32,7 @@ import java.io.File; import java.time.LocalDateTime; import java.time.ZoneId; -import java.util.Arrays; +import java.util.List; import static com.graphhopper.gtfs.GtfsHelper.time; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -51,9 +51,11 @@ public static void init() { ghConfig.putObject("graph.location", GRAPH_LOC); ghConfig.putObject("gtfs.file", "files/another-sample-feed-extended-route-type.zip"); ghConfig.putObject("import.osm.ignored_highways", ""); - ghConfig.setProfiles(Arrays.asList( - new Profile("foot").setVehicle("foot"), - new Profile("car").setVehicle("car"))); + ghConfig.putObject("graph.encoded_values", "foot_access, foot_priority, foot_average_speed, car_access, car_average_speed"); + ghConfig.setProfiles(List.of( + TestProfiles.accessSpeedAndPriority("foot"), + TestProfiles.accessAndSpeed("car"))); + Helper.removeDir(new File(GRAPH_LOC)); graphHopperGtfs = new GraphHopperGtfs(ghConfig); graphHopperGtfs.init(ghConfig); diff --git a/reader-gtfs/src/test/java/com/graphhopper/FreeWalkIT.java b/reader-gtfs/src/test/java/com/graphhopper/FreeWalkIT.java index 1fb95478659..e6edc0ef08d 100644 --- a/reader-gtfs/src/test/java/com/graphhopper/FreeWalkIT.java +++ b/reader-gtfs/src/test/java/com/graphhopper/FreeWalkIT.java @@ -18,8 +18,8 @@ package com.graphhopper; -import com.graphhopper.config.Profile; import com.graphhopper.gtfs.*; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.util.Helper; import com.graphhopper.util.TranslationMap; import org.junit.jupiter.api.AfterAll; @@ -34,6 +34,7 @@ import java.time.LocalTime; import java.time.ZoneId; import java.util.Arrays; +import java.util.List; import java.util.stream.Collectors; import static com.graphhopper.gtfs.GtfsHelper.time; @@ -60,9 +61,11 @@ public static void init() { // TODO: here it is instantiated directly. Refactor by having only one Router but two Solvers, similar // TODO: to the street router. ghConfig.putObject("gtfs.free_walk", true); - ghConfig.setProfiles(Arrays.asList( - new Profile("foot").setVehicle("foot"), - new Profile("car").setVehicle("car"))); + ghConfig.putObject("graph.encoded_values", "foot_access, foot_priority, foot_average_speed, car_access, car_average_speed"); + ghConfig.setProfiles(List.of( + TestProfiles.accessSpeedAndPriority("foot"), + TestProfiles.accessAndSpeed("car"))); + Helper.removeDir(new File(GRAPH_LOC)); graphHopperGtfs = new GraphHopperGtfs(ghConfig); graphHopperGtfs.init(ghConfig); @@ -122,7 +125,7 @@ public void testFastWalking() { assertThat(walkSolution.getLegs().get(0).getDepartureTime().toInstant().atZone(zoneId).toLocalTime()) .isEqualTo(LocalTime.parse("06:40")); assertThat(walkSolution.getLegs().get(0).getArrivalTime().toInstant().atZone(zoneId).toLocalTime()) - .isEqualTo(LocalTime.parse("06:41:07.025")); + .isEqualTo(LocalTime.parse("06:41:07.031")); assertThat(walkSolution.getLegs().size()).isEqualTo(1); assertThat(walkSolution.getNumChanges()).isEqualTo(-1); } diff --git a/reader-gtfs/src/test/java/com/graphhopper/GraphHopperGtfsIT.java b/reader-gtfs/src/test/java/com/graphhopper/GraphHopperGtfsIT.java index 5c575665243..eefa71eb1d2 100644 --- a/reader-gtfs/src/test/java/com/graphhopper/GraphHopperGtfsIT.java +++ b/reader-gtfs/src/test/java/com/graphhopper/GraphHopperGtfsIT.java @@ -19,8 +19,8 @@ package com.graphhopper; import com.conveyal.gtfs.model.Stop; -import com.graphhopper.config.Profile; import com.graphhopper.gtfs.*; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.util.Helper; import com.graphhopper.util.Instruction; import com.graphhopper.util.TranslationMap; @@ -58,9 +58,11 @@ public static void init() { ghConfig.putObject("graph.location", GRAPH_LOC); ghConfig.putObject("import.osm.ignored_highways", ""); ghConfig.putObject("gtfs.file", "files/sample-feed"); - ghConfig.setProfiles(Arrays.asList( - new Profile("foot").setVehicle("foot"), - new Profile("car").setVehicle("car"))); + ghConfig.putObject("graph.encoded_values", "foot_access, foot_priority, foot_average_speed, car_access, car_average_speed"); + ghConfig.setProfiles(List.of( + TestProfiles.accessSpeedAndPriority("foot"), + TestProfiles.accessAndSpeed("car"))); + Helper.removeDir(new File(GRAPH_LOC)); graphHopperGtfs = new GraphHopperGtfs(ghConfig); graphHopperGtfs.init(ghConfig); diff --git a/reader-gtfs/src/test/java/com/graphhopper/GraphHopperMultimodalIT.java b/reader-gtfs/src/test/java/com/graphhopper/GraphHopperMultimodalIT.java index ee7ea2f010b..fe37fb73e9d 100644 --- a/reader-gtfs/src/test/java/com/graphhopper/GraphHopperMultimodalIT.java +++ b/reader-gtfs/src/test/java/com/graphhopper/GraphHopperMultimodalIT.java @@ -20,6 +20,7 @@ import com.graphhopper.config.Profile; import com.graphhopper.gtfs.*; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.routing.ev.BooleanEncodedValue; import com.graphhopper.routing.ev.Subnetwork; import com.graphhopper.routing.util.AllEdgesIterator; @@ -60,11 +61,11 @@ public static void init() { ghConfig.putObject("import.osm.ignored_highways", ""); ghConfig.putObject("gtfs.file", "files/sample-feed"); ghConfig.putObject("graph.location", GRAPH_LOC); - Profile carLocal = new Profile("car_custom"); - carLocal.setVehicle("car"); + ghConfig.putObject("graph.encoded_values", "foot_access, foot_priority, foot_average_speed, car_access, car_average_speed"); + Profile carLocal = TestProfiles.accessAndSpeed("car_custom", "car"); ghConfig.setProfiles(Arrays.asList( - new Profile("foot").setVehicle("foot"), - new Profile("car_default").setVehicle("car"), + TestProfiles.accessSpeedAndPriority("foot"), + TestProfiles.accessAndSpeed("car_default", "car"), carLocal)); Helper.removeDir(new File(GRAPH_LOC)); graphHopperGtfs = new GraphHopperGtfs(ghConfig); @@ -109,7 +110,7 @@ public void testDepartureTimeOfAccessLegInProfileQuery() { assertThat(firstTransitSolution.getLegs().get(0).getArrivalTime().toInstant()) .isEqualTo(firstTransitSolution.getLegs().get(1).getDepartureTime().toInstant()); assertThat(firstTransitSolution.getLegs().get(2).getArrivalTime().toInstant().atZone(zoneId).toLocalTime()) - .isEqualTo(LocalTime.parse("06:52:02.641")); + .isEqualTo(LocalTime.parse("06:52:02.741")); // I like walking exactly as I like riding a bus (per travel time unit) // Now we get a walk solution which arrives earlier than the transit solutions. @@ -147,16 +148,16 @@ public void testDepartureTimeOfAccessLeg() { assertThat(firstTransitSolution.getLegs().get(0).getArrivalTime().toInstant()) .isEqualTo(firstTransitSolution.getLegs().get(1).getDepartureTime().toInstant()); assertThat(firstTransitSolution.getLegs().get(2).getArrivalTime().toInstant().atZone(zoneId).toLocalTime()) - .isEqualTo(LocalTime.parse("06:52:02.641")); + .isEqualTo(LocalTime.parse("06:52:02.741")); - double EXPECTED_TOTAL_WALKING_DISTANCE = 496.96631386761055; + double EXPECTED_TOTAL_WALKING_DISTANCE = 497.1043138676106; assertThat(firstTransitSolution.getLegs().get(0).distance + firstTransitSolution.getLegs().get(2).distance) .isEqualTo(EXPECTED_TOTAL_WALKING_DISTANCE); List distances = firstTransitSolution.getPathDetails().get("distance"); assertThat(distances.stream().mapToDouble(d -> (double) d.getValue()).sum()) .isEqualTo(EXPECTED_TOTAL_WALKING_DISTANCE); // Also total walking distance -- PathDetails only cover access/egress for now assertThat(distances.get(0).getFirst()).isEqualTo(0); // PathDetails start and end with PointList - assertThat(distances.get(distances.size() - 1).getLast()).isEqualTo(10); + assertThat(distances.get(distances.size() - 1).getLast()).isEqualTo(12); List accessDistances = ((Trip.WalkLeg) firstTransitSolution.getLegs().get(0)).details.get("distance"); assertThat(accessDistances.get(0).getFirst()).isEqualTo(0); @@ -164,7 +165,7 @@ public void testDepartureTimeOfAccessLeg() { List egressDistances = ((Trip.WalkLeg) firstTransitSolution.getLegs().get(2)).details.get("distance"); assertThat(egressDistances.get(0).getFirst()).isEqualTo(0); - assertThat(egressDistances.get(egressDistances.size() - 1).getLast()).isEqualTo(5); + assertThat(egressDistances.get(egressDistances.size() - 1).getLast()).isEqualTo(7); ResponsePath walkSolution = response.getAll().stream().filter(p -> p.getLegs().size() == 1).findFirst().get(); assertThat(walkSolution.getLegs().get(0).getDepartureTime().toInstant().atZone(zoneId).toLocalTime()) @@ -172,7 +173,7 @@ public void testDepartureTimeOfAccessLeg() { // In principle, this would dominate the transit solution, since it's faster, but // walking gets a penalty. assertThat(walkSolution.getLegs().get(0).getArrivalTime().toInstant().atZone(zoneId).toLocalTime()) - .isEqualTo(LocalTime.parse("06:51:10.306")); + .isEqualTo(LocalTime.parse("06:51:10.361")); assertThat(walkSolution.getLegs().size()).isEqualTo(1); assertThat(walkSolution.getNumChanges()).isEqualTo(-1); @@ -213,7 +214,7 @@ public void testFastWalking() { assertThat(walkSolution.getLegs().get(0).getDepartureTime().toInstant().atZone(zoneId).toLocalTime()) .isEqualTo(LocalTime.parse("06:40")); assertThat(walkSolution.getLegs().get(0).getArrivalTime().toInstant().atZone(zoneId).toLocalTime()) - .isEqualTo(LocalTime.parse("06:41:07.025")); + .isEqualTo(LocalTime.parse("06:41:07.031")); assertThat(walkSolution.getLegs().size()).isEqualTo(1); assertThat(walkSolution.getNumChanges()).isEqualTo(-1); } @@ -234,7 +235,7 @@ public void testFastWalkingInProfileQuery() { assertThat(walkSolution.getLegs().get(0).getDepartureTime().toInstant().atZone(zoneId).toLocalTime()) .isEqualTo(LocalTime.parse("06:40")); assertThat(walkSolution.getLegs().get(0).getArrivalTime().toInstant().atZone(zoneId).toLocalTime()) - .isEqualTo(LocalTime.parse("06:41:07.025")); + .isEqualTo(LocalTime.parse("06:41:07.031")); assertThat(walkSolution.getLegs().size()).isEqualTo(1); assertThat(walkSolution.getNumChanges()).isEqualTo(-1); } diff --git a/reader-gtfs/src/test/java/com/graphhopper/RealtimeIT.java b/reader-gtfs/src/test/java/com/graphhopper/RealtimeIT.java index 1624eb34801..6f0c00c327f 100644 --- a/reader-gtfs/src/test/java/com/graphhopper/RealtimeIT.java +++ b/reader-gtfs/src/test/java/com/graphhopper/RealtimeIT.java @@ -19,11 +19,11 @@ package com.graphhopper; import com.google.transit.realtime.GtfsRealtime; -import com.graphhopper.config.Profile; import com.graphhopper.gtfs.GraphHopperGtfs; import com.graphhopper.gtfs.PtRouter; import com.graphhopper.gtfs.PtRouterImpl; import com.graphhopper.gtfs.Request; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.util.Helper; import com.graphhopper.util.TranslationMap; import org.junit.jupiter.api.AfterAll; @@ -33,7 +33,7 @@ import java.io.File; import java.math.BigDecimal; import java.time.*; -import java.util.Arrays; +import java.util.List; import static com.google.transit.realtime.GtfsRealtime.TripDescriptor.ScheduleRelationship.ADDED; import static com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate.ScheduleRelationship.SCHEDULED; @@ -54,9 +54,11 @@ public static void init() { ghConfig.putObject("gtfs.file", "files/sample-feed"); ghConfig.putObject("graph.location", GRAPH_LOC); ghConfig.putObject("import.osm.ignored_highways", ""); - ghConfig.setProfiles(Arrays.asList( - new Profile("foot").setVehicle("foot"), - new Profile("car").setVehicle("car"))); + ghConfig.putObject("graph.encoded_values", "foot_access, foot_priority, foot_average_speed, car_access, car_average_speed"); + ghConfig.setProfiles(List.of( + TestProfiles.accessSpeedAndPriority("foot"), + TestProfiles.accessAndSpeed("car"))); + Helper.removeDir(new File(GRAPH_LOC)); graphHopperGtfs = new GraphHopperGtfs(ghConfig); graphHopperGtfs.init(ghConfig); diff --git a/reader-gtfs/src/test/java/com/graphhopper/gtfs/analysis/AnalysisTest.java b/reader-gtfs/src/test/java/com/graphhopper/gtfs/analysis/AnalysisTest.java index e8d6e6f81c8..c1c396cd643 100644 --- a/reader-gtfs/src/test/java/com/graphhopper/gtfs/analysis/AnalysisTest.java +++ b/reader-gtfs/src/test/java/com/graphhopper/gtfs/analysis/AnalysisTest.java @@ -19,17 +19,16 @@ package com.graphhopper.gtfs.analysis; import com.graphhopper.GraphHopperConfig; -import com.graphhopper.config.Profile; import com.graphhopper.gtfs.GraphHopperGtfs; import com.graphhopper.gtfs.GtfsStorage; import com.graphhopper.gtfs.PtGraph; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.util.Helper; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import java.io.File; -import java.util.Arrays; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -46,9 +45,10 @@ public static void init() { ghConfig.putObject("datareader.file", "files/beatty.osm"); ghConfig.putObject("gtfs.file", "files/sample-feed,files/another-sample-feed"); ghConfig.putObject("import.osm.ignored_highways", ""); - ghConfig.setProfiles(Arrays.asList( - new Profile("foot").setVehicle("foot"), - new Profile("car").setVehicle("car"))); + ghConfig.putObject("graph.encoded_values", "foot_access, foot_priority, foot_average_speed, car_access, car_average_speed"); + ghConfig.setProfiles(List.of( + TestProfiles.accessSpeedAndPriority("foot"), + TestProfiles.accessAndSpeed("car"))); Helper.removeDir(new File(GRAPH_LOC)); graphHopperGtfs = new GraphHopperGtfs(ghConfig); graphHopperGtfs.init(ghConfig); diff --git a/tools/pom.xml b/tools/pom.xml index d01401dfbf6..5e4200cff1f 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -10,7 +10,7 @@ com.graphhopper graphhopper-parent - 8.0-osm-reader-callbacks + 9.1-osm-reader-callbacks-SNAPSHOT package diff --git a/tools/src/main/java/com/graphhopper/tools/CHImportTest.java b/tools/src/main/java/com/graphhopper/tools/CHImportTest.java index af5b15c5811..335f2471bca 100644 --- a/tools/src/main/java/com/graphhopper/tools/CHImportTest.java +++ b/tools/src/main/java/com/graphhopper/tools/CHImportTest.java @@ -46,7 +46,7 @@ public static void main(String[] args) { GraphHopperConfig config = new GraphHopperConfig(map); config.putObject("datareader.file", map.getString("pbf", "map-matching/files/leipzig_germany.osm.pbf")); config.putObject("graph.location", map.getString("gh", "ch-import-test-gh")); - config.setProfiles(Arrays.asList(new Profile(vehicle).setVehicle(vehicle))); + config.setProfiles(Arrays.asList(new Profile(vehicle))); config.setCHProfiles(Collections.singletonList(new CHProfile(vehicle))); config.putObject(CHParameters.PERIODIC_UPDATES, map.getInt("periodic", 0)); config.putObject(CHParameters.LAST_LAZY_NODES_UPDATES, map.getInt("lazy", 100)); diff --git a/tools/src/main/java/com/graphhopper/tools/CHMeasurement.java b/tools/src/main/java/com/graphhopper/tools/CHMeasurement.java index b8d810d8241..225714e270a 100644 --- a/tools/src/main/java/com/graphhopper/tools/CHMeasurement.java +++ b/tools/src/main/java/com/graphhopper/tools/CHMeasurement.java @@ -23,8 +23,8 @@ import com.graphhopper.GraphHopperConfig; import com.graphhopper.config.CHProfile; import com.graphhopper.config.LMProfile; - -import com.graphhopper.config.Profile; +import com.graphhopper.config.TurnCostsConfig; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.storage.Graph; import com.graphhopper.storage.NodeAccess; import com.graphhopper.util.*; @@ -88,9 +88,8 @@ private static void testPerformanceAutomaticNodeOrdering(String[] args) { final GraphHopper graphHopper = new GraphHopper(); String profile = "car_profile"; if (withTurnCosts) { - ghConfig.putObject("graph.vehicles", "car|turn_costs=true"); ghConfig.setProfiles(Collections.singletonList( - new Profile(profile).setVehicle("car").setTurnCosts(true).putHint(Parameters.Routing.U_TURN_COSTS, uTurnCosts) + TestProfiles.accessAndSpeed(profile, "car").setTurnCostsConfig(new TurnCostsConfig(List.of("motorcar", "motor_vehicle"), uTurnCosts)) )); ghConfig.setCHProfiles(Collections.singletonList( new CHProfile(profile) @@ -102,9 +101,8 @@ private static void testPerformanceAutomaticNodeOrdering(String[] args) { ghConfig.putObject("prepare.lm.landmarks", landmarks); } } else { - ghConfig.putObject("graph.vehicles", "car"); ghConfig.setProfiles(Collections.singletonList( - new Profile(profile).setVehicle("car").setTurnCosts(false) + TestProfiles.accessAndSpeed(profile, "car") )); } diff --git a/tools/src/main/java/com/graphhopper/tools/GraphSpeedMeasurement.java b/tools/src/main/java/com/graphhopper/tools/GraphSpeedMeasurement.java index 15210770e47..f24b20a0410 100644 --- a/tools/src/main/java/com/graphhopper/tools/GraphSpeedMeasurement.java +++ b/tools/src/main/java/com/graphhopper/tools/GraphSpeedMeasurement.java @@ -20,14 +20,16 @@ import com.graphhopper.GraphHopper; import com.graphhopper.GraphHopperConfig; -import com.graphhopper.config.Profile; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.storage.BaseGraph; -import com.graphhopper.util.*; +import com.graphhopper.util.EdgeExplorer; +import com.graphhopper.util.EdgeIterator; +import com.graphhopper.util.MiniPerfTest; +import com.graphhopper.util.PMap; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Random; import java.util.stream.Collectors; @@ -44,9 +46,9 @@ public static void main(String[] strs) { .putObject("graph.location", args.getString("location", "graph-speed-measurement") + "-" + speedBits + "-gh") .putObject("graph.dataaccess", args.getString("da", "RAM_STORE")) .putObject("import.osm.ignored_highways", "") - .putObject("graph.vehicles", String.format("roads|speed_bits=%d,car|speed_bits=%d,bike|speed_bits=%d,foot|speed_bits=%d", speedBits, speedBits, speedBits, speedBits)) - .setProfiles(Arrays.asList( - new Profile("car").setCustomModel(new CustomModel()).setVehicle("roads") + .putObject("graph.encoded_values", String.format("car_average_speed|speed_bits=%d,bike_average_speed|speed_bits=%d,foot_average_speed|speed_bits=%d", speedBits, speedBits, speedBits)) + .setProfiles(List.of( + TestProfiles.accessAndSpeed("car") )); GraphHopper hopper = new GraphHopper() .init(ghConfig) diff --git a/tools/src/main/java/com/graphhopper/tools/Measurement.java b/tools/src/main/java/com/graphhopper/tools/Measurement.java index 2554d348531..81e54156cea 100644 --- a/tools/src/main/java/com/graphhopper/tools/Measurement.java +++ b/tools/src/main/java/com/graphhopper/tools/Measurement.java @@ -25,14 +25,15 @@ import com.graphhopper.config.CHProfile; import com.graphhopper.config.LMProfile; import com.graphhopper.config.Profile; +import com.graphhopper.config.TurnCostsConfig; import com.graphhopper.jackson.Jackson; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.routing.ch.PrepareContractionHierarchies; import com.graphhopper.routing.ev.*; import com.graphhopper.routing.lm.LMConfig; import com.graphhopper.routing.lm.PrepareLandmarks; import com.graphhopper.routing.util.*; import com.graphhopper.routing.weighting.Weighting; - import com.graphhopper.routing.weighting.custom.CustomWeighting; import com.graphhopper.storage.*; import com.graphhopper.storage.index.LocationIndex; @@ -61,7 +62,6 @@ import static com.graphhopper.util.GHUtility.readCountries; import static com.graphhopper.util.Helper.*; import static com.graphhopper.util.Parameters.Algorithms.ALT_ROUTE; -import static com.graphhopper.util.Parameters.Routing.U_TURN_COSTS; /** * Used to run performance benchmarks for routing and other functionalities of GraphHopper @@ -175,7 +175,7 @@ protected void importOSM() { BaseGraph g = hopper.getBaseGraph(); EncodingManager encodingManager = hopper.getEncodingManager(); BooleanEncodedValue accessEnc = encodingManager.getBooleanEncodedValue(VehicleAccess.key(vehicle)); - boolean withTurnCosts = encodingManager.hasTurnEncodedValue(TurnRestriction.key(vehicle)); + boolean withTurnCosts = encodingManager.hasTurnEncodedValue(TurnRestriction.key("profile_tc")); StopWatch sw = new StopWatch().start(); try { @@ -295,6 +295,7 @@ protected void importOSM() { private GraphHopperConfig createConfigFromArgs(PMap args) { GraphHopperConfig ghConfig = new GraphHopperConfig(args); vehicle = args.getString("measurement.vehicle", "car"); + ghConfig.putObject("graph.encoded_values", ghConfig.getString("graph.encoded_values", "") + ", " + VehicleAccess.key(vehicle) + "," + VehicleSpeed.key(vehicle)); boolean turnCosts = args.getBool("measurement.turn_costs", false); int uTurnCosts = args.getInt("measurement.u_turn_costs", 40); String weighting = args.getString("measurement.weighting", "custom"); @@ -303,19 +304,22 @@ private GraphHopperConfig createConfigFromArgs(PMap args) { boolean useLM = args.getBool("measurement.lm", true); String customModelFile = args.getString("measurement.custom_model_file", ""); List profiles = new ArrayList<>(); + if (turnCosts && !vehicle.equals("car")) + throw new IllegalArgumentException("turn costs not yet supported for: " + vehicle); + List restrictionVehicleTypes = List.of("motorcar", "motor_vehicle"); if (!customModelFile.isEmpty()) { if (!weighting.equals(CustomWeighting.NAME)) throw new IllegalArgumentException("To make use of a custom model you need to set measurement.weighting to 'custom'"); // use custom profile(s) as specified in the given custom model file CustomModel customModel = loadCustomModel(customModelFile); - profiles.add(new Profile("profile_no_tc").setCustomModel(customModel).setVehicle(vehicle).setTurnCosts(false)); + profiles.add(new Profile("profile_no_tc").setCustomModel(customModel)); if (turnCosts) - profiles.add(new Profile("profile_tc").setCustomModel(customModel).setVehicle(vehicle).setTurnCosts(true).putHint(U_TURN_COSTS, uTurnCosts)); + profiles.add(new Profile("profile_tc").setCustomModel(customModel).setTurnCostsConfig(new TurnCostsConfig(restrictionVehicleTypes, uTurnCosts))); } else { // use standard profiles - profiles.add(new Profile("profile_no_tc").setVehicle(vehicle).setTurnCosts(false)); + profiles.add(TestProfiles.accessAndSpeed("profile_no_tc", vehicle)); if (turnCosts) - profiles.add(new Profile("profile_tc").setVehicle(vehicle).setTurnCosts(true).putHint(U_TURN_COSTS, uTurnCosts)); + profiles.add(TestProfiles.accessAndSpeed("profile_tc", vehicle).setTurnCostsConfig(new TurnCostsConfig(restrictionVehicleTypes, uTurnCosts))); } ghConfig.setProfiles(profiles); @@ -566,7 +570,8 @@ private void measureRouting(final GraphHopper hopper, final QuerySettings queryS req.setAlgorithm(ALT_ROUTE); if (querySettings.pathDetails) - req.setPathDetails(Arrays.asList(Parameters.Details.AVERAGE_SPEED, Parameters.Details.EDGE_ID, Parameters.Details.STREET_NAME)); + req.setPathDetails(Arrays.asList(Parameters.Details.AVERAGE_SPEED, Parameters.Details.EDGE_ID, + Parameters.Details.STREET_NAME, "access_conditional", "vehicle_conditional", "motor_vehicle_conditional")); if (!querySettings.simplify) req.getHints().putObject(Parameters.Routing.WAY_POINT_MAX_DISTANCE, 0); diff --git a/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java b/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java index e18bdc8edff..696e7c149e5 100644 --- a/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java +++ b/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java @@ -25,6 +25,7 @@ import com.graphhopper.config.CHProfile; import com.graphhopper.config.LMProfile; import com.graphhopper.config.Profile; +import com.graphhopper.config.TurnCostsConfig; import com.graphhopper.routing.*; import com.graphhopper.routing.ev.BooleanEncodedValue; import com.graphhopper.routing.ev.DecimalEncodedValue; @@ -37,7 +38,6 @@ import com.graphhopper.routing.util.AllEdgesIterator; import com.graphhopper.routing.util.EdgeFilter; import com.graphhopper.routing.util.TraversalMode; -import com.graphhopper.routing.weighting.custom.CustomModelParser; import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.NodeAccess; import com.graphhopper.storage.RoutingCHGraph; @@ -58,7 +58,7 @@ import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; -import java.util.Arrays; +import java.util.List; import java.util.Random; /** @@ -99,15 +99,12 @@ public static void main(String[] strs) { args.putObject("datareader.file", args.getString("datareader.file", "core/files/monaco.osm.gz")); args.putObject("graph.location", args.getString("graph.location", "tools/target/mini-graph-ui-gh")); GraphHopperConfig ghConfig = new GraphHopperConfig(args); - ghConfig.setProfiles(Arrays.asList( - new Profile("profile") - .setVehicle("car") - .setTurnCosts(true) - )).putObject("import.osm.ignored_highways", ""); - ghConfig.setCHProfiles(Arrays.asList( + ghConfig.setProfiles(List.of(TestProfiles.accessAndSpeed("profile", "car").setTurnCostsConfig(TurnCostsConfig.car()))). + putObject("import.osm.ignored_highways", ""); + ghConfig.setCHProfiles(List.of( new CHProfile("profile") )); - ghConfig.setLMProfiles(Arrays.asList( + ghConfig.setLMProfiles(List.of( new LMProfile("profile") )); GraphHopper hopper = new GraphHopper().init(ghConfig).importOrLoad(); @@ -119,9 +116,8 @@ public static void main(String[] strs) { public MiniGraphUI(GraphHopper hopper, boolean debug, boolean useCH) { this.graph = hopper.getBaseGraph(); this.na = graph.getNodeAccess(); - String vehicle = hopper.getProfiles().get(0).getVehicle(); - accessEnc = hopper.getEncodingManager().getBooleanEncodedValue(VehicleAccess.key(vehicle)); - avSpeedEnc = hopper.getEncodingManager().getDecimalEncodedValue(VehicleSpeed.key(vehicle)); + accessEnc = hopper.getEncodingManager().getBooleanEncodedValue(VehicleAccess.key("car")); + avSpeedEnc = hopper.getEncodingManager().getDecimalEncodedValue(VehicleSpeed.key("car")); this.useCH = useCH; logger.info("locations:" + graph.getNodes() + ", debug:" + debug); @@ -332,7 +328,7 @@ private RoutingAlgorithm createAlgo(GraphHopper hopper, QueryGraph qGraph) { }; AlgorithmOptions algoOpts = new AlgorithmOptions().setAlgorithm(Algorithms.ASTAR_BI). setTraversalMode(TraversalMode.EDGE_BASED); - return algoFactory.createAlgo(qGraph, CustomModelParser.createFastestWeighting(accessEnc, avSpeedEnc, hopper.getEncodingManager()), algoOpts); + return algoFactory.createAlgo(qGraph, hopper.createWeighting(profile, new PMap()), algoOpts); } } diff --git a/web-api/pom.xml b/web-api/pom.xml index c39aab77dc6..84b36fe4257 100644 --- a/web-api/pom.xml +++ b/web-api/pom.xml @@ -5,14 +5,14 @@ 4.0.0 graphhopper-web-api jar - 8.0-osm-reader-callbacks + 9.1-osm-reader-callbacks-SNAPSHOT GraphHopper Web API JSON Representation of the API classes com.graphhopper graphhopper-parent - 8.0-osm-reader-callbacks + 9.1-osm-reader-callbacks-SNAPSHOT diff --git a/web-api/src/main/java/com/graphhopper/GHResponse.java b/web-api/src/main/java/com/graphhopper/GHResponse.java index ffd99461226..dd826a2409f 100644 --- a/web-api/src/main/java/com/graphhopper/GHResponse.java +++ b/web-api/src/main/java/com/graphhopper/GHResponse.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Locale; /** * Wrapper containing path and error output of GraphHopper. @@ -147,4 +148,11 @@ public void setHints(PMap hints) { public PMap getHints() { return hintsMap; } + + public String getHeader(String key, String defaultValue) { + Object val = hintsMap.getObject(key.toLowerCase(Locale.ROOT), null); + if (val instanceof List && !((List) val).isEmpty()) + return ((List) val).get(0).toString(); + return defaultValue; + } } diff --git a/web-api/src/main/java/com/graphhopper/jackson/GHResponseDeserializer.java b/web-api/src/main/java/com/graphhopper/jackson/GHResponseDeserializer.java deleted file mode 100644 index 0488500fe9f..00000000000 --- a/web-api/src/main/java/com/graphhopper/jackson/GHResponseDeserializer.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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.jackson; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.graphhopper.GHResponse; -import com.graphhopper.ResponsePath; - -import java.io.IOException; - -public class GHResponseDeserializer extends JsonDeserializer { - @Override - public GHResponse deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - GHResponse ghResponse = new GHResponse(); - JsonNode treeNode = p.readValueAsTree(); - for (JsonNode path : treeNode.get("paths")) { - ResponsePath responsePath = ((ObjectMapper) p.getCodec()).convertValue(path, ResponsePath.class); - ghResponse.add(responsePath); - } - return ghResponse; - } -} diff --git a/web-api/src/main/java/com/graphhopper/jackson/GraphHopperModule.java b/web-api/src/main/java/com/graphhopper/jackson/GraphHopperModule.java index b6a6d3fc52d..0ea5d13dfba 100644 --- a/web-api/src/main/java/com/graphhopper/jackson/GraphHopperModule.java +++ b/web-api/src/main/java/com/graphhopper/jackson/GraphHopperModule.java @@ -31,8 +31,6 @@ public class GraphHopperModule extends SimpleModule { public GraphHopperModule() { addDeserializer(Statement.class, new StatementDeserializer()); addSerializer(Statement.class, new StatementSerializer()); - addDeserializer(GHResponse.class, new GHResponseDeserializer()); - addDeserializer(ResponsePath.class, new ResponsePathDeserializer()); addDeserializer(Envelope.class, new JtsEnvelopeDeserializer()); addSerializer(Envelope.class, new JtsEnvelopeSerializer()); addDeserializer(GHPoint.class, new GHPointDeserializer()); diff --git a/web-api/src/main/java/com/graphhopper/jackson/ResponsePathDeserializer.java b/web-api/src/main/java/com/graphhopper/jackson/ResponsePathDeserializerHelper.java similarity index 91% rename from web-api/src/main/java/com/graphhopper/jackson/ResponsePathDeserializer.java rename to web-api/src/main/java/com/graphhopper/jackson/ResponsePathDeserializerHelper.java index d8354190497..caeb9483124 100644 --- a/web-api/src/main/java/com/graphhopper/jackson/ResponsePathDeserializer.java +++ b/web-api/src/main/java/com/graphhopper/jackson/ResponsePathDeserializerHelper.java @@ -17,10 +17,7 @@ */ package com.graphhopper.jackson; -import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.graphhopper.ResponsePath; @@ -29,14 +26,9 @@ import com.graphhopper.util.exceptions.*; import org.locationtech.jts.geom.LineString; -import java.io.IOException; import java.util.*; -public class ResponsePathDeserializer extends JsonDeserializer { - @Override - public ResponsePath deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - return createResponsePath((ObjectMapper) p.getCodec(), p.readValueAsTree(), false, true); - } +public class ResponsePathDeserializerHelper { public static ResponsePath createResponsePath(ObjectMapper objectMapper, JsonNode path, boolean hasElevation, boolean turnDescription) { ResponsePath responsePath = new ResponsePath(); @@ -44,9 +36,15 @@ public static ResponsePath createResponsePath(ObjectMapper objectMapper, JsonNod if (responsePath.hasErrors()) return responsePath; + // Read multiplier from JSON to properly decode the "points" and/or "snapped_waypoints" array. + // Note, in earlier versions the points_encoded was missing from the JSON for calc_points == false (although still required for snapped_waypoints). + double multiplier = 1e5; + if (path.has("points_encoded") && path.get("points_encoded").asBoolean() && path.has("points_encoded_multiplier")) + multiplier = path.get("points_encoded_multiplier").asDouble(); + if (path.has("snapped_waypoints")) { JsonNode snappedWaypoints = path.get("snapped_waypoints"); - PointList snappedPoints = deserializePointList(objectMapper, snappedWaypoints, hasElevation); + PointList snappedPoints = deserializePointList(objectMapper, snappedWaypoints, hasElevation, multiplier); responsePath.setWaypoints(snappedPoints); } @@ -73,7 +71,7 @@ public static ResponsePath createResponsePath(ObjectMapper objectMapper, JsonNod } if (path.has("points")) { - final PointList pointList = deserializePointList(objectMapper, path.get("points"), hasElevation); + final PointList pointList = deserializePointList(objectMapper, path.get("points"), hasElevation, multiplier); responsePath.setPoints(pointList); if (path.has("instructions")) { @@ -177,10 +175,10 @@ public static ResponsePath createResponsePath(ObjectMapper objectMapper, JsonNod return responsePath; } - private static PointList deserializePointList(ObjectMapper objectMapper, JsonNode jsonNode, boolean hasElevation) { + private static PointList deserializePointList(ObjectMapper objectMapper, JsonNode jsonNode, boolean hasElevation, double multiplier) { PointList snappedPoints; if (jsonNode.isTextual()) { - snappedPoints = decodePolyline(jsonNode.asText(), Math.max(10, jsonNode.asText().length() / 4), hasElevation); + snappedPoints = decodePolyline(jsonNode.asText(), Math.max(10, jsonNode.asText().length() / 4), hasElevation, multiplier); } else { LineString lineString = objectMapper.convertValue(jsonNode, LineString.class); snappedPoints = PointList.fromLineString(lineString); @@ -188,7 +186,10 @@ private static PointList deserializePointList(ObjectMapper objectMapper, JsonNod return snappedPoints; } - public static PointList decodePolyline(String encoded, int initCap, boolean is3D) { + public static PointList decodePolyline(String encoded, int initCap, boolean is3D, double multiplier) { + if (multiplier < 1) + throw new IllegalArgumentException("multiplier cannot be smaller than 1 but was " + multiplier + " for polyline " + encoded); + PointList poly = new PointList(initCap, is3D); int index = 0; int len = encoded.length(); @@ -226,9 +227,9 @@ public static PointList decodePolyline(String encoded, int initCap, boolean is3D } while (b >= 0x20); int deltaElevation = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1)); ele += deltaElevation; - poly.add((double) lat / 1e5, (double) lng / 1e5, (double) ele / 100); + poly.add((double) lat / multiplier, (double) lng / multiplier, (double) ele / 100); } else - poly.add((double) lat / 1e5, (double) lng / 1e5); + poly.add((double) lat / multiplier, (double) lng / multiplier); } return poly; } diff --git a/web-api/src/main/java/com/graphhopper/jackson/ResponsePathSerializer.java b/web-api/src/main/java/com/graphhopper/jackson/ResponsePathSerializer.java index ca10633c9e1..9b1144dd360 100644 --- a/web-api/src/main/java/com/graphhopper/jackson/ResponsePathSerializer.java +++ b/web-api/src/main/java/com/graphhopper/jackson/ResponsePathSerializer.java @@ -27,7 +27,6 @@ import com.graphhopper.util.PointList; import java.text.NumberFormat; -import java.util.Arrays; import java.util.List; import java.util.Locale; @@ -43,28 +42,24 @@ */ public class ResponsePathSerializer { - /** - * This includes the required attribution for OpenStreetMap. - * Do not hesitate to mention us and link us in your about page - * https://support.graphhopper.com/support/search/solutions?term=attribution - */ - public static final List COPYRIGHTS = Arrays.asList("GraphHopper", "OpenStreetMap contributors"); + public static String encodePolyline(PointList poly, boolean includeElevation, double multiplier) { + if (multiplier < 1) + throw new IllegalArgumentException("multiplier cannot be smaller than 1 but was " + multiplier + " for polyline"); - public static String encodePolyline(PointList poly, boolean includeElevation, double precision) { StringBuilder sb = new StringBuilder(Math.max(20, poly.size() * 3)); int size = poly.size(); int prevLat = 0; int prevLon = 0; int prevEle = 0; for (int i = 0; i < size; i++) { - int num = (int) Math.floor(poly.getLat(i) * precision); + int num = (int) Math.round(poly.getLat(i) * multiplier); encodeNumber(sb, num - prevLat); prevLat = num; - num = (int) Math.floor(poly.getLon(i) * precision); + num = (int) Math.round(poly.getLon(i) * multiplier); encodeNumber(sb, num - prevLon); prevLon = num; if (includeElevation) { - num = (int) Math.floor(poly.getEle(i) * 100); + num = (int) Math.round(poly.getEle(i) * 100); encodeNumber(sb, num - prevEle); prevEle = num; } @@ -86,16 +81,14 @@ private static void encodeNumber(StringBuilder sb, int num) { sb.append((char) (num)); } - public static ObjectNode jsonObject(GHResponse ghRsp, String osmDate, boolean enableInstructions, - boolean calcPoints, boolean enableElevation, boolean pointsEncoded, double took) { + public record Info(List copyrights, long took, String roadDataTimeStamp) { + } + + public static ObjectNode jsonObject(GHResponse ghRsp, Info info, boolean enableInstructions, + boolean calcPoints, boolean enableElevation, boolean pointsEncoded, double pointsMultiplier) { ObjectNode json = JsonNodeFactory.instance.objectNode(); json.putPOJO("hints", ghRsp.getHints().toMap()); - final ObjectNode info = json.putObject("info"); - info.putPOJO("copyrights", COPYRIGHTS); - info.put("took", Math.round(took)); - if (!osmDate.isEmpty()) - info.put("road_data_timestamp", osmDate); - + json.putPOJO("info", info); ArrayNode jsonPathList = json.putArray("paths"); for (ResponsePath p : ghRsp.getAll()) { ObjectNode jsonPath = jsonPathList.addObject(); @@ -106,10 +99,14 @@ public static ObjectNode jsonObject(GHResponse ghRsp, String osmDate, boolean en if (!p.getDescription().isEmpty()) { jsonPath.putPOJO("description", p.getDescription()); } + + // for points and snapped_waypoints: + jsonPath.put("points_encoded", pointsEncoded); + if (pointsEncoded) jsonPath.put("points_encoded_multiplier", pointsMultiplier); + if (calcPoints) { - jsonPath.put("points_encoded", pointsEncoded); jsonPath.putPOJO("bbox", p.calcBBox2D()); - jsonPath.putPOJO("points", pointsEncoded ? encodePolyline(p.getPoints(), enableElevation, 1e5) : p.getPoints().toLineString(enableElevation)); + jsonPath.putPOJO("points", pointsEncoded ? encodePolyline(p.getPoints(), enableElevation, pointsMultiplier) : p.getPoints().toLineString(enableElevation)); if (enableInstructions) { jsonPath.putPOJO("instructions", p.getInstructions()); } @@ -118,7 +115,7 @@ public static ObjectNode jsonObject(GHResponse ghRsp, String osmDate, boolean en jsonPath.put("ascend", p.getAscend()); jsonPath.put("descend", p.getDescend()); } - jsonPath.putPOJO("snapped_waypoints", pointsEncoded ? encodePolyline(p.getWaypoints(), enableElevation, 1e5) : p.getWaypoints().toLineString(enableElevation)); + jsonPath.putPOJO("snapped_waypoints", pointsEncoded ? encodePolyline(p.getWaypoints(), enableElevation, pointsMultiplier) : p.getWaypoints().toLineString(enableElevation)); if (p.getFare() != null) { jsonPath.put("fare", NumberFormat.getCurrencyInstance(Locale.ROOT).format(p.getFare())); } diff --git a/web-api/src/main/java/com/graphhopper/util/CustomModel.java b/web-api/src/main/java/com/graphhopper/util/CustomModel.java index b7c67ed17f7..843cf94e933 100644 --- a/web-api/src/main/java/com/graphhopper/util/CustomModel.java +++ b/web-api/src/main/java/com/graphhopper/util/CustomModel.java @@ -167,10 +167,10 @@ private String createContentString() { * queryModel is null. */ public static CustomModel merge(CustomModel baseModel, CustomModel queryModel) { - if (queryModel == null) return baseModel; // avoid changing the specified CustomModel via deep copy otherwise the server-side CustomModel would be // modified (same problem if queryModel would be used as target) CustomModel mergedCM = new CustomModel(baseModel); + if (queryModel == null) return mergedCM; if (queryModel.getDistanceInfluence() != null) mergedCM.distanceInfluence = queryModel.distanceInfluence; diff --git a/web-api/src/main/java/com/graphhopper/util/Helper.java b/web-api/src/main/java/com/graphhopper/util/Helper.java index 761abffc9bb..1c009987c2d 100644 --- a/web-api/src/main/java/com/graphhopper/util/Helper.java +++ b/web-api/src/main/java/com/graphhopper/util/Helper.java @@ -413,20 +413,6 @@ public static String underScoreToCamelCase(String key) { return sb.toString(); } - /** - * Equivalent to java 8 String#join - */ - public static String join(String delimiter, List strings) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < strings.size(); i++) { - if (i > 0) { - sb.append(delimiter); - } - sb.append(strings.get(i)); - } - return sb.toString(); - } - /** * parses a string like [a,b,c] */ @@ -459,4 +445,5 @@ public static int staticHashCode(String str) { } return val; } + } diff --git a/web-api/src/main/java/com/graphhopper/util/PMap.java b/web-api/src/main/java/com/graphhopper/util/PMap.java index 83ef579eaf0..fb1ad903cd8 100644 --- a/web-api/src/main/java/com/graphhopper/util/PMap.java +++ b/web-api/src/main/java/com/graphhopper/util/PMap.java @@ -54,7 +54,7 @@ public PMap(String propertiesString) { if (index < 0) continue; - put(s.substring(0, index), s.substring(index + 1)); + putObject(Helper.camelCaseToUnderScore(s.substring(0, index)), Helper.toObject(s.substring(index + 1))); } } @@ -92,17 +92,6 @@ public PMap putAll(PMap map) { return this; } - /** - * @deprecated use {@link #putObject(String, Object)} instead - */ - @Deprecated - public PMap put(String key, String str) { - if (str == null) - throw new NullPointerException("Value cannot be null. Use remove instead."); - map.put(Helper.camelCaseToUnderScore(key), Helper.toObject(str)); - return this; - } - public Object remove(String key) { return map.remove(key); } diff --git a/web-api/src/main/java/com/graphhopper/util/Parameters.java b/web-api/src/main/java/com/graphhopper/util/Parameters.java index 2755cabef29..7b59e08538b 100644 --- a/web-api/src/main/java/com/graphhopper/util/Parameters.java +++ b/web-api/src/main/java/com/graphhopper/util/Parameters.java @@ -199,6 +199,7 @@ public static final class Details { public static final String STREET_REF = "street_ref"; public static final String STREET_DESTINATION = "street_destination"; public static final String STREET_DESTINATION_REF = "street_destination_ref"; + public static final String MOTORWAY_JUNCTION = "motorway_junction"; public static final String AVERAGE_SPEED = "average_speed"; public static final String EDGE_ID = "edge_id"; diff --git a/web-api/src/test/java/com/graphhopper/jackson/PathDetailDeserializerTest.java b/web-api/src/test/java/com/graphhopper/jackson/PathDetailDeserializerTest.java index 7cf953db2b1..a61b8173de9 100644 --- a/web-api/src/test/java/com/graphhopper/jackson/PathDetailDeserializerTest.java +++ b/web-api/src/test/java/com/graphhopper/jackson/PathDetailDeserializerTest.java @@ -1,20 +1,19 @@ package com.graphhopper.jackson; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -import org.junit.jupiter.api.Test; - import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.graphhopper.GHResponse; +import com.graphhopper.ResponsePath; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNull; class PathDetailDeserializerTest { - @Test - public void else_null_detail_average_speed() throws JsonProcessingException { - final ObjectMapper objectMapper = Jackson.newObjectMapper(); - final String result = "{\"hints\":{\"visited_nodes.sum\":90,\"visited_nodes.average\":90},\"info\":{\"copyrights\":[\"GraphHopper\",\"OpenStreetMap contributors\"],\"took\":1},\"paths\":[{\"distance\":9863.287,\"weight\":888.630785,\"time\":740658,\"transfers\":0,\"points_encoded\":false,\"bbox\":[-58.476658,-34.650384,-58.429018,-34.628717],\"points\":{\"type\":\"LineString\",\"coordinates\":[[-58.465985,-34.650384],[-58.465985,-34.650384],[-58.46555,-34.649904],[-58.465416,-34.649686],[-58.464935,-34.649033],[-58.464199,-34.648446],[-58.464014,-34.648262],[-58.463675,-34.647783],[-58.462575,-34.645549],[-58.462426,-34.645311],[-58.462031,-34.644079],[-58.461681,-34.643299],[-58.461561,-34.643099],[-58.461258,-34.642675],[-58.460623,-34.642013],[-58.460372,-34.641799],[-58.458082,-34.640102],[-58.456072,-34.638467],[-58.45555,-34.638104],[-58.453825,-34.637045],[-58.453518,-34.636894],[-58.453287,-34.636799],[-58.452854,-34.636647],[-58.452399,-34.636541],[-58.451783,-34.636477],[-58.451437,-34.636461],[-58.45096,-34.636455],[-58.449346,-34.636492],[-58.448718,-34.636475],[-58.448065,-34.636381],[-58.447576,-34.636252],[-58.447071,-34.636088],[-58.446624,-34.635893],[-58.445336,-34.635105],[-58.443767,-34.633999],[-58.441687,-34.632678],[-58.440299,-34.631866],[-58.439234,-34.631214],[-58.43877,-34.630985],[-58.437805,-34.630657],[-58.436974,-34.630468],[-58.434133,-34.630075],[-58.432415,-34.629923],[-58.430902,-34.629722],[-58.430437,-34.62966],[-58.430367,-34.629977],[-58.429018,-34.629782],[-58.429325,-34.628717],[-58.430635,-34.628917],[-58.430561,-34.62922],[-58.432879,-34.629618],[-58.434549,-34.629933],[-58.436946,-34.630326],[-58.437878,-34.630532],[-58.438762,-34.630822],[-58.439319,-34.63109],[-58.440885,-34.632045],[-58.442483,-34.632958],[-58.443484,-34.633603],[-58.445463,-34.634973],[-58.446519,-34.635643],[-58.446771,-34.635791],[-58.447069,-34.635926],[-58.447557,-34.636096],[-58.448078,-34.636254],[-58.44873,-34.636348],[-58.451367,-34.636297],[-58.452221,-34.636358],[-58.452712,-34.636448],[-58.453267,-34.636623],[-58.453552,-34.63673],[-58.453854,-34.636872],[-58.454214,-34.637075],[-58.455065,-34.637593],[-58.456306,-34.638427],[-58.457424,-34.639367],[-58.45823,-34.640018],[-58.460244,-34.641438],[-58.460783,-34.641844],[-58.461038,-34.642077],[-58.461295,-34.642353],[-58.461474,-34.64258],[-58.461901,-34.643196],[-58.462073,-34.643528],[-58.462269,-34.644017],[-58.462687,-34.645211],[-58.463101,-34.645982],[-58.463506,-34.646562],[-58.463782,-34.646898],[-58.464636,-34.647832],[-58.465174,-34.648395],[-58.4654,-34.648587],[-58.465807,-34.648882],[-58.466103,-34.649053],[-58.466435,-34.6492],[-58.466725,-34.649293],[-58.467056,-34.649351],[-58.467634,-34.649388],[-58.469992,-34.64935],[-58.47232,-34.64918],[-58.473264,-34.649091],[-58.474569,-34.648922],[-58.475036,-34.648843],[-58.475346,-34.648771],[-58.475799,-34.648596],[-58.476658,-34.648084]]},\"instructions\":[{\"distance\":3923.699,\"heading\":90,\"sign\":0,\"interval\":[0,41],\"text\":\"Continue na Autopista Teniente General Luis Dellepiane\",\"time\":228978,\"street_name\":\"Autopista Teniente General Luis Dellepiane\"},{\"distance\":341.449,\"sign\":7,\"interval\":[41,44],\"text\":\"Mantenha-se à direita\",\"time\":52395,\"street_name\":\"\"},{\"distance\":35.813,\"sign\":2,\"interval\":[44,45],\"text\":\"Vire à direita na Viel\",\"time\":7162,\"street_name\":\"Viel\"},{\"distance\":125.343,\"sign\":-2,\"interval\":[45,46],\"text\":\"Vire à esquerda na Tejedor\",\"time\":25068,\"street_name\":\"Tejedor\"},{\"distance\":121.839,\"sign\":-2,\"interval\":[46,47],\"text\":\"Vire à esquerda na Doblas\",\"time\":31329,\"street_name\":\"Doblas\"},{\"distance\":121.976,\"sign\":-2,\"interval\":[47,48],\"text\":\"Vire à esquerda na Zuviría\",\"time\":24395,\"street_name\":\"Zuviría\"},{\"distance\":34.367,\"sign\":-2,\"interval\":[48,49],\"text\":\"Vire à esquerda na Viel\",\"time\":6873,\"street_name\":\"Viel\"},{\"distance\":3623.263,\"sign\":2,\"interval\":[49,85],\"text\":\"Vire à direita\",\"time\":234503,\"street_name\":\"\"},{\"distance\":1535.538,\"sign\":7,\"interval\":[85,105],\"text\":\"Mantenha-se à direita\",\"time\":129955,\"street_name\":\"\"},{\"distance\":0,\"sign\":4,\"last_heading\":305.9709217557622,\"interval\":[105,105],\"text\":\"Destino alcançado!\",\"time\":0,\"street_name\":\"\"}],\"legs\":[],\"details\":{\"average_speed\":[[0,1,null],[1,2,16],[2,7,78],[7,41,64],[41,43,26],[43,44,14],[44,46,18],[46,47,14],[47,49,18],[49,51,26],[51,85,64],[85,96,40],[96,103,48],[103,105,32]]},\"ascend\":40.208499908447266,\"descend\":36.19799995422363,\"snapped_waypoints\":{\"type\":\"LineString\",\"coordinates\":[[-58.465985,-34.650384],[-58.476658,-34.648084]]}}]}"; - final GHResponse ghResponse = objectMapper.readValue(result, GHResponse.class); - assertNotNull(ghResponse); - } + @Test + public void else_null_detail_average_speed() throws JsonProcessingException { + final ObjectMapper objectMapper = Jackson.newObjectMapper(); + final String result = "{\"distance\":9863.287,\"weight\":888.630785,\"time\":740658,\"transfers\":0,\"points_encoded\":false,\"bbox\":[-58.476658,-34.650384,-58.429018,-34.628717],\"points\":{\"type\":\"LineString\",\"coordinates\":[[-58.465985,-34.650384],[-58.465985,-34.650384],[-58.46555,-34.649904],[-58.465416,-34.649686],[-58.464935,-34.649033],[-58.464199,-34.648446],[-58.464014,-34.648262],[-58.463675,-34.647783],[-58.462575,-34.645549],[-58.462426,-34.645311],[-58.462031,-34.644079],[-58.461681,-34.643299],[-58.461561,-34.643099],[-58.461258,-34.642675],[-58.460623,-34.642013],[-58.460372,-34.641799],[-58.458082,-34.640102],[-58.456072,-34.638467],[-58.45555,-34.638104],[-58.453825,-34.637045],[-58.453518,-34.636894],[-58.453287,-34.636799],[-58.452854,-34.636647],[-58.452399,-34.636541],[-58.451783,-34.636477],[-58.451437,-34.636461],[-58.45096,-34.636455],[-58.449346,-34.636492],[-58.448718,-34.636475],[-58.448065,-34.636381],[-58.447576,-34.636252],[-58.447071,-34.636088],[-58.446624,-34.635893],[-58.445336,-34.635105],[-58.443767,-34.633999],[-58.441687,-34.632678],[-58.440299,-34.631866],[-58.439234,-34.631214],[-58.43877,-34.630985],[-58.437805,-34.630657],[-58.436974,-34.630468],[-58.434133,-34.630075],[-58.432415,-34.629923],[-58.430902,-34.629722],[-58.430437,-34.62966],[-58.430367,-34.629977],[-58.429018,-34.629782],[-58.429325,-34.628717],[-58.430635,-34.628917],[-58.430561,-34.62922],[-58.432879,-34.629618],[-58.434549,-34.629933],[-58.436946,-34.630326],[-58.437878,-34.630532],[-58.438762,-34.630822],[-58.439319,-34.63109],[-58.440885,-34.632045],[-58.442483,-34.632958],[-58.443484,-34.633603],[-58.445463,-34.634973],[-58.446519,-34.635643],[-58.446771,-34.635791],[-58.447069,-34.635926],[-58.447557,-34.636096],[-58.448078,-34.636254],[-58.44873,-34.636348],[-58.451367,-34.636297],[-58.452221,-34.636358],[-58.452712,-34.636448],[-58.453267,-34.636623],[-58.453552,-34.63673],[-58.453854,-34.636872],[-58.454214,-34.637075],[-58.455065,-34.637593],[-58.456306,-34.638427],[-58.457424,-34.639367],[-58.45823,-34.640018],[-58.460244,-34.641438],[-58.460783,-34.641844],[-58.461038,-34.642077],[-58.461295,-34.642353],[-58.461474,-34.64258],[-58.461901,-34.643196],[-58.462073,-34.643528],[-58.462269,-34.644017],[-58.462687,-34.645211],[-58.463101,-34.645982],[-58.463506,-34.646562],[-58.463782,-34.646898],[-58.464636,-34.647832],[-58.465174,-34.648395],[-58.4654,-34.648587],[-58.465807,-34.648882],[-58.466103,-34.649053],[-58.466435,-34.6492],[-58.466725,-34.649293],[-58.467056,-34.649351],[-58.467634,-34.649388],[-58.469992,-34.64935],[-58.47232,-34.64918],[-58.473264,-34.649091],[-58.474569,-34.648922],[-58.475036,-34.648843],[-58.475346,-34.648771],[-58.475799,-34.648596],[-58.476658,-34.648084]]},\"instructions\":[{\"distance\":3923.699,\"heading\":90,\"sign\":0,\"interval\":[0,41],\"text\":\"Continue na Autopista Teniente General Luis Dellepiane\",\"time\":228978,\"street_name\":\"Autopista Teniente General Luis Dellepiane\"},{\"distance\":341.449,\"sign\":7,\"interval\":[41,44],\"text\":\"Mantenha-se à direita\",\"time\":52395,\"street_name\":\"\"},{\"distance\":35.813,\"sign\":2,\"interval\":[44,45],\"text\":\"Vire à direita na Viel\",\"time\":7162,\"street_name\":\"Viel\"},{\"distance\":125.343,\"sign\":-2,\"interval\":[45,46],\"text\":\"Vire à esquerda na Tejedor\",\"time\":25068,\"street_name\":\"Tejedor\"},{\"distance\":121.839,\"sign\":-2,\"interval\":[46,47],\"text\":\"Vire à esquerda na Doblas\",\"time\":31329,\"street_name\":\"Doblas\"},{\"distance\":121.976,\"sign\":-2,\"interval\":[47,48],\"text\":\"Vire à esquerda na Zuviría\",\"time\":24395,\"street_name\":\"Zuviría\"},{\"distance\":34.367,\"sign\":-2,\"interval\":[48,49],\"text\":\"Vire à esquerda na Viel\",\"time\":6873,\"street_name\":\"Viel\"},{\"distance\":3623.263,\"sign\":2,\"interval\":[49,85],\"text\":\"Vire à direita\",\"time\":234503,\"street_name\":\"\"},{\"distance\":1535.538,\"sign\":7,\"interval\":[85,105],\"text\":\"Mantenha-se à direita\",\"time\":129955,\"street_name\":\"\"},{\"distance\":0,\"sign\":4,\"last_heading\":305.9709217557622,\"interval\":[105,105],\"text\":\"Destino alcançado!\",\"time\":0,\"street_name\":\"\"}],\"legs\":[],\"details\":{\"average_speed\":[[0,1,null],[1,2,16],[2,7,78],[7,41,64],[41,43,26],[43,44,14],[44,46,18],[46,47,14],[47,49,18],[49,51,26],[51,85,64],[85,96,40],[96,103,48],[103,105,32]]}}"; + final ResponsePath ghResponse = ResponsePathDeserializerHelper.createResponsePath(objectMapper, objectMapper.readTree(result), false, false); + assertNull(ghResponse.getPathDetails().get("average_speed").get(0).getValue()); + } } diff --git a/web-api/src/test/java/com/graphhopper/jackson/ResponsePathRepresentationTest.java b/web-api/src/test/java/com/graphhopper/jackson/ResponsePathRepresentationTest.java index 5201f50f606..a73b76f39b5 100644 --- a/web-api/src/test/java/com/graphhopper/jackson/ResponsePathRepresentationTest.java +++ b/web-api/src/test/java/com/graphhopper/jackson/ResponsePathRepresentationTest.java @@ -29,10 +29,10 @@ public class ResponsePathRepresentationTest { @Test public void testDecode() { - PointList list = ResponsePathDeserializer.decodePolyline("_p~iF~ps|U", 1, false); + PointList list = ResponsePathDeserializerHelper.decodePolyline("_p~iF~ps|U", 1, false, 1e5); assertEquals(Helper.createPointList(38.5, -120.2), list); - list = ResponsePathDeserializer.decodePolyline("_p~iF~ps|U_ulLnnqC_mqNvxq`@", 3, false); + list = ResponsePathDeserializerHelper.decodePolyline("_p~iF~ps|U_ulLnnqC_mqNvxq`@", 3, false, 1e5); assertEquals(Helper.createPointList(38.5, -120.2, 40.7, -120.95, 43.252, -126.453), list); } @@ -50,20 +50,20 @@ public void testBoth() { PointList list = Helper.createPointList(38.5, -120.2, 43.252, -126.453, 40.7, -120.95, 50.3139, 10.61279, 50.04303, 9.49768); String str = ResponsePathSerializer.encodePolyline(list, list.is3D(), 1e5); - assertEquals(list, ResponsePathDeserializer.decodePolyline(str, list.size(), false)); + assertEquals(list, ResponsePathDeserializerHelper.decodePolyline(str, list.size(), false, 1e5)); list = Helper.createPointList(38.5, -120.2, 43.252, -126.453, 40.7, -120.95, 40.70001, -120.95001); str = ResponsePathSerializer.encodePolyline(list, list.is3D(), 1e5); - assertEquals(list, ResponsePathDeserializer.decodePolyline(str, list.size(), false)); + assertEquals(list, ResponsePathDeserializerHelper.decodePolyline(str, list.size(), false, 1e5)); } @Test public void testDecode3D() { - PointList list = ResponsePathDeserializer.decodePolyline("_p~iF~ps|Uo}@", 1, true); + PointList list = ResponsePathDeserializerHelper.decodePolyline("_p~iF~ps|Uo}@", 1, true, 1e5); assertEquals(Helper.createPointList3D(38.5, -120.2, 10), list); - list = ResponsePathDeserializer.decodePolyline("_p~iF~ps|Uo}@_ulLnnqC_anF_mqNvxq`@?", 3, true); + list = ResponsePathDeserializerHelper.decodePolyline("_p~iF~ps|Uo}@_ulLnnqC_anF_mqNvxq`@?", 3, true, 1e5); assertEquals(Helper.createPointList3D(38.5, -120.2, 10, 40.7, -120.95, 1234, 43.252, -126.453, 1234), list); } diff --git a/web-api/src/test/java/com/graphhopper/jackson/RouteResourceRepresentationTest.java b/web-api/src/test/java/com/graphhopper/jackson/RouteResourceRepresentationTest.java index 12869f9bfba..e7aec0e154f 100644 --- a/web-api/src/test/java/com/graphhopper/jackson/RouteResourceRepresentationTest.java +++ b/web-api/src/test/java/com/graphhopper/jackson/RouteResourceRepresentationTest.java @@ -34,7 +34,7 @@ public void testUnknownInstructionSign() throws IOException { // Modified the sign though ObjectMapper objectMapper = Jackson.newObjectMapper(); JsonNode json = objectMapper.readTree("{\"instructions\":[{\"distance\":1.073,\"sign\":741,\"interval\":[0,1],\"text\":\"Continue onto A 81\",\"time\":32,\"street_name\":\"A 81\"},{\"distance\":0,\"sign\":4,\"interval\":[1,1],\"text\":\"Finish!\",\"time\":0,\"street_name\":\"\"}],\"descend\":0,\"ascend\":0,\"distance\":1.073,\"bbox\":[8.676286,48.354446,8.676297,48.354453],\"weight\":0.032179,\"time\":32,\"points_encoded\":true,\"points\":\"gfcfHwq}s@}c~AAA?\",\"snapped_waypoints\":\"gfcfHwq}s@}c~AAA?\"}"); - ResponsePath responsePath = ResponsePathDeserializer.createResponsePath(objectMapper, json, true, true); + ResponsePath responsePath = ResponsePathDeserializerHelper.createResponsePath(objectMapper, json, true, true); assertEquals(741, responsePath.getInstructions().get(0).getSign()); assertEquals("Continue onto A 81", responsePath.getInstructions().get(0).getName()); diff --git a/web-bundle/pom.xml b/web-bundle/pom.xml index f742bfb7b2b..349dfb7f04f 100644 --- a/web-bundle/pom.xml +++ b/web-bundle/pom.xml @@ -5,9 +5,9 @@ 4.0.0 graphhopper-web-bundle jar - 8.0-osm-reader-callbacks + 9.1-osm-reader-callbacks-SNAPSHOT - 0.0.0-56cdfc78acf88b477457f8846c8715058b5375db + 0.0.0-6fdf096cf324bcf6761713132fc84a8d1bb276fe GraphHopper Dropwizard Bundle Use the GraphHopper routing engine as a web-service @@ -15,7 +15,7 @@ com.graphhopper graphhopper-parent - 8.0-osm-reader-callbacks + 9.1-osm-reader-callbacks-SNAPSHOT diff --git a/web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java b/web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java index f59725ad302..ccf5afd8faa 100644 --- a/web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java +++ b/web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java @@ -89,12 +89,9 @@ public ProfileData(String name) { public Info getInfo() { final Info info = new Info(); info.bbox = new Envelope(baseGraph.getBounds().minLon, baseGraph.getBounds().maxLon, baseGraph.getBounds().minLat, baseGraph.getBounds().maxLat); - Set defaultHiddenEVs = new HashSet<>(); for (Profile p : config.getProfiles()) { Info.ProfileData profileData = new Info.ProfileData(p.getName()); info.profiles.add(profileData); - defaultHiddenEVs.addAll(Arrays.asList(VehiclePriority.key(p.getName()), VehicleAccess.key(p.getName()), - VehicleSpeed.key(p.getName()), TurnRestriction.key(p.getName()), Subnetwork.key(p.getName()))); } if (config.has("gtfs.file")) info.profiles.add(new Info.ProfileData("pt")); @@ -108,7 +105,7 @@ public Info getInfo() { for (EncodedValue encodedValue : evList) { List possibleValueList = new ArrayList<>(); String name = encodedValue.getName(); - if (privateEV.contains(name) || defaultHiddenEVs.contains(name)) { + if (privateEV.contains(name)) { continue; } else if (encodedValue instanceof EnumEncodedValue) { for (Enum o : ((EnumEncodedValue) encodedValue).getValues()) { diff --git a/web-bundle/src/main/java/com/graphhopper/resources/IsochroneResource.java b/web-bundle/src/main/java/com/graphhopper/resources/IsochroneResource.java index 629dd17a77f..7b90929e321 100644 --- a/web-bundle/src/main/java/com/graphhopper/resources/IsochroneResource.java +++ b/web-bundle/src/main/java/com/graphhopper/resources/IsochroneResource.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; import com.graphhopper.GraphHopper; +import com.graphhopper.GraphHopperConfig; import com.graphhopper.config.Profile; import com.graphhopper.http.GHPointParam; import com.graphhopper.http.ProfileResolver; @@ -48,13 +49,15 @@ public class IsochroneResource { private static final Logger logger = LoggerFactory.getLogger(IsochroneResource.class); + private final GraphHopperConfig config; private final GraphHopper graphHopper; private final Triangulator triangulator; private final ProfileResolver profileResolver; private final String osmDate; @Inject - public IsochroneResource(GraphHopper graphHopper, Triangulator triangulator, ProfileResolver profileResolver) { + public IsochroneResource(GraphHopperConfig config, GraphHopper graphHopper, Triangulator triangulator, ProfileResolver profileResolver) { + this.config = config; this.graphHopper = graphHopper; this.triangulator = triangulator; this.profileResolver = profileResolver; @@ -99,7 +102,7 @@ public Response doGet( if (!snap.isValid()) throw new IllegalArgumentException("Point not found:" + point); QueryGraph queryGraph = QueryGraph.create(graph, snap); - TraversalMode traversalMode = profile.isTurnCosts() ? EDGE_BASED : NODE_BASED; + TraversalMode traversalMode = profile.hasTurnCosts() ? EDGE_BASED : NODE_BASED; ShortestPathTree shortestPathTree = new ShortestPathTree(queryGraph, queryGraph.wrapWeighting(weighting), reverseFlow, traversalMode); double limit; @@ -143,7 +146,7 @@ public Response doGet( HashMap properties = new HashMap<>(); properties.put("bucket", features.size()); if (respType == geojson) { - properties.put("copyrights", ResponsePathSerializer.COPYRIGHTS); + properties.put("copyrights", config.getCopyrights()); } feature.setProperties(properties); feature.setGeometry(isochrone); @@ -160,7 +163,7 @@ public Response doGet( } else { json.putPOJO("polygons", features); final ObjectNode info = json.putObject("info"); - info.putPOJO("copyrights", ResponsePathSerializer.COPYRIGHTS); + info.putPOJO("copyrights", config.getCopyrights()); info.put("took", Math.round((float) sw.getMillis())); if (!osmDate.isEmpty()) info.put("road_data_timestamp", osmDate); finalJson = json; diff --git a/web-bundle/src/main/java/com/graphhopper/resources/MapMatchingResource.java b/web-bundle/src/main/java/com/graphhopper/resources/MapMatchingResource.java index debe35df563..067aa5c6e92 100644 --- a/web-bundle/src/main/java/com/graphhopper/resources/MapMatchingResource.java +++ b/web-bundle/src/main/java/com/graphhopper/resources/MapMatchingResource.java @@ -24,6 +24,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.graphhopper.GHResponse; import com.graphhopper.GraphHopper; +import com.graphhopper.GraphHopperConfig; import com.graphhopper.ResponsePath; import com.graphhopper.gpx.GpxConversions; import com.graphhopper.http.ProfileResolver; @@ -36,7 +37,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.Nullable; import javax.inject.Inject; +import javax.validation.constraints.NotNull; import javax.ws.rs.*; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; @@ -62,42 +65,45 @@ public interface MapMatchingRouterFactory { private static final Logger logger = LoggerFactory.getLogger(MapMatchingResource.class); + private final GraphHopperConfig config; private final GraphHopper graphHopper; private final ProfileResolver profileResolver; private final TranslationMap trMap; private final MapMatchingRouterFactory mapMatchingRouterFactory; private final ObjectMapper objectMapper = Jackson.newObjectMapper(); + @Nullable private final String osmDate; @Inject - public MapMatchingResource(GraphHopper graphHopper, ProfileResolver profileResolver, TranslationMap trMap, MapMatchingRouterFactory mapMatchingRouterFactory) { + public MapMatchingResource(GraphHopperConfig config, GraphHopper graphHopper, ProfileResolver profileResolver, TranslationMap trMap, MapMatchingRouterFactory mapMatchingRouterFactory) { + this.config = config; this.graphHopper = graphHopper; this.profileResolver = profileResolver; this.trMap = trMap; this.mapMatchingRouterFactory = mapMatchingRouterFactory; - this.osmDate = graphHopper.getProperties().get("datareader.data.date"); + this.osmDate = graphHopper.getProperties().getAll().get("datareader.data.date"); } @POST @Consumes({MediaType.APPLICATION_XML, "application/gpx+xml"}) @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, "application/gpx+xml"}) public Response match( - Gpx gpx, + @NotNull Gpx gpx, @Context UriInfo uriInfo, - @QueryParam(WAY_POINT_MAX_DISTANCE) @DefaultValue("1") double minPathPrecision, + @QueryParam(WAY_POINT_MAX_DISTANCE) @DefaultValue("0.5") double minPathPrecision, @QueryParam("type") @DefaultValue("json") String outType, @QueryParam(INSTRUCTIONS) @DefaultValue("true") boolean instructions, @QueryParam(CALC_POINTS) @DefaultValue("true") boolean calcPoints, @QueryParam("elevation") @DefaultValue("false") boolean enableElevation, @QueryParam("points_encoded") @DefaultValue("true") boolean pointsEncoded, + @QueryParam("points_encoded_multiplier") @DefaultValue("1e5") double pointsEncodedMultiplier, @QueryParam("locale") @DefaultValue("en") String localeStr, @QueryParam("profile") String profile, @QueryParam(PATH_DETAILS) List pathDetails, @QueryParam("gpx.route") @DefaultValue("true") boolean withRoute, @QueryParam("gpx.track") @DefaultValue("true") boolean withTrack, @QueryParam("traversal_keys") @DefaultValue("false") boolean enableTraversalKeys, - @QueryParam("gps_accuracy") @DefaultValue("40") double gpsAccuracy) { - + @QueryParam("gps_accuracy") @DefaultValue("10") double gpsAccuracy) { boolean writeGPX = "gpx".equalsIgnoreCase(outType); if (gpx.trk.isEmpty()) { throw new IllegalArgumentException("No tracks found in GPX document. Are you using waypoints or routes instead?"); @@ -136,7 +142,7 @@ public Response match( .putPOJO("mapmatching", matching.getStatistics()).toString()); if ("extended_json".equals(outType)) { - return Response.ok(convertToTree(matchResult, enableElevation, pointsEncoded)). + return Response.ok(convertToTree(matchResult, enableElevation, pointsEncoded, pointsEncodedMultiplier)). header("X-GH-Took", "" + Math.round(sw.getMillisDouble())). build(); } else { @@ -164,8 +170,8 @@ public Response match( header("X-GH-Took", "" + Math.round(sw.getMillisDouble())). build(); } else { - ObjectNode map = ResponsePathSerializer.jsonObject(rsp, osmDate, instructions, - calcPoints, enableElevation, pointsEncoded, sw.getMillisDouble()); + ObjectNode map = ResponsePathSerializer.jsonObject(rsp, new ResponsePathSerializer.Info(config.getCopyrights(), Math.round(sw.getMillisDouble()), osmDate), instructions, + calcPoints, enableElevation, pointsEncoded, pointsEncodedMultiplier); Map matchStatistics = new HashMap<>(); matchStatistics.put("distance", matchResult.getMatchLength()); @@ -189,7 +195,7 @@ public Response match( } } - public static JsonNode convertToTree(MatchResult result, boolean elevation, boolean pointsEncoded) { + public static JsonNode convertToTree(MatchResult result, boolean elevation, boolean pointsEncoded, double pointsEncodedMultiplier) { ObjectNode root = JsonNodeFactory.instance.objectNode(); ObjectNode diary = root.putObject("diary"); ArrayNode entries = diary.putArray("entries"); @@ -201,10 +207,10 @@ public static JsonNode convertToTree(MatchResult result, boolean elevation, bool PointList pointList = edgeMatch.getEdgeState().fetchWayGeometry(emIndex == 0 ? FetchMode.ALL : FetchMode.PILLAR_AND_ADJ); final ObjectNode geometry = link.putObject("geometry"); if (pointList.size() < 2) { - geometry.putPOJO("coordinates", pointsEncoded ? ResponsePathSerializer.encodePolyline(pointList, elevation, 1e5) : pointList.toLineString(elevation)); + geometry.putPOJO("coordinates", pointsEncoded ? ResponsePathSerializer.encodePolyline(pointList, elevation, pointsEncodedMultiplier) : pointList.toLineString(elevation)); geometry.put("type", "Point"); } else { - geometry.putPOJO("coordinates", pointsEncoded ? ResponsePathSerializer.encodePolyline(pointList, elevation, 1e5) : pointList.toLineString(elevation)); + geometry.putPOJO("coordinates", pointsEncoded ? ResponsePathSerializer.encodePolyline(pointList, elevation, pointsEncodedMultiplier) : pointList.toLineString(elevation)); geometry.put("type", "LineString"); } link.put("id", edgeMatch.getEdgeState().getEdge()); diff --git a/web-bundle/src/main/java/com/graphhopper/resources/PtIsochroneResource.java b/web-bundle/src/main/java/com/graphhopper/resources/PtIsochroneResource.java index 929694a5952..fdb5dfdfb75 100644 --- a/web-bundle/src/main/java/com/graphhopper/resources/PtIsochroneResource.java +++ b/web-bundle/src/main/java/com/graphhopper/resources/PtIsochroneResource.java @@ -19,20 +19,24 @@ package com.graphhopper.resources; import com.conveyal.gtfs.model.Stop; +import com.graphhopper.GraphHopperConfig; import com.graphhopper.gtfs.*; import com.graphhopper.http.GHLocationParam; import com.graphhopper.http.OffsetDateTimeParam; import com.graphhopper.isochrone.algorithm.ContourBuilder; import com.graphhopper.isochrone.algorithm.ReadableTriangulation; import com.graphhopper.jackson.ResponsePathSerializer; +import com.graphhopper.json.Statement; import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.DefaultSnapFilter; import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.weighting.TurnCostProvider; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.routing.weighting.custom.CustomModelParser; import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.NodeAccess; import com.graphhopper.storage.index.LocationIndex; +import com.graphhopper.util.CustomModel; import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.JsonFeature; import com.graphhopper.util.shapes.BBox; @@ -56,13 +60,15 @@ public class PtIsochroneResource { private static final double JTS_TOLERANCE = 0.00001; + private final GraphHopperConfig config; private final GtfsStorage gtfsStorage; private final EncodingManager encodingManager; private final BaseGraph baseGraph; private final LocationIndex locationIndex; @Inject - public PtIsochroneResource(GtfsStorage gtfsStorage, EncodingManager encodingManager, BaseGraph baseGraph, LocationIndex locationIndex) { + public PtIsochroneResource(GraphHopperConfig config, GtfsStorage gtfsStorage, EncodingManager encodingManager, BaseGraph baseGraph, LocationIndex locationIndex) { + this.config = config; this.gtfsStorage = gtfsStorage; this.encodingManager = encodingManager; this.baseGraph = baseGraph; @@ -93,9 +99,10 @@ public Response doGet( double targetZ = seconds * 1000; GeometryFactory geometryFactory = new GeometryFactory(); - BooleanEncodedValue accessEnc = encodingManager.getBooleanEncodedValue(VehicleAccess.key("foot")); - DecimalEncodedValue speedEnc = encodingManager.getDecimalEncodedValue(VehicleSpeed.key("foot")); - final Weighting weighting = CustomModelParser.createFastestWeighting(accessEnc, speedEnc, encodingManager); + CustomModel customModel = new CustomModel() + .addToPriority(Statement.If("!" + VehicleAccess.key("foot"), Statement.Op.MULTIPLY, "0")) + .addToSpeed(Statement.If("true", Statement.Op.LIMIT, VehicleSpeed.key("foot"))); + final Weighting weighting = CustomModelParser.createWeighting(encodingManager, TurnCostProvider.NO_TURN_COST_PROVIDER, customModel); DefaultSnapFilter snapFilter = new DefaultSnapFilter(weighting, encodingManager.getBooleanEncodedValue(Subnetwork.key("foot"))); PtLocationSnapper.Result snapResult = new PtLocationSnapper(baseGraph, locationIndex, gtfsStorage).snapAll(Arrays.asList(location), Arrays.asList(snapFilter)); @@ -189,7 +196,7 @@ public Response doGet( properties.put("z", targetZ); feature.setProperties(properties); response.polygons.add(feature); - response.info.copyrights.addAll(ResponsePathSerializer.COPYRIGHTS); + response.info.copyrights.addAll(config.getCopyrights()); return response; } else { return wrap(isoline); @@ -207,7 +214,7 @@ private Response wrap(Geometry isoline) { Response response = new Response(); response.polygons.add(feature); - response.info.copyrights.addAll(ResponsePathSerializer.COPYRIGHTS); + response.info.copyrights.addAll(config.getCopyrights()); return response; } diff --git a/web-bundle/src/main/java/com/graphhopper/resources/PtRouteResource.java b/web-bundle/src/main/java/com/graphhopper/resources/PtRouteResource.java index cea74c48313..3ad05aa32a9 100644 --- a/web-bundle/src/main/java/com/graphhopper/resources/PtRouteResource.java +++ b/web-bundle/src/main/java/com/graphhopper/resources/PtRouteResource.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.graphhopper.GHResponse; +import com.graphhopper.GraphHopperConfig; import com.graphhopper.gtfs.GHLocation; import com.graphhopper.gtfs.PtRouter; import com.graphhopper.gtfs.Request; @@ -45,10 +46,12 @@ @Path("route-pt") public class PtRouteResource { + private final GraphHopperConfig config; private final PtRouter ptRouter; @Inject - public PtRouteResource(PtRouter ptRouter) { + public PtRouteResource(GraphHopperConfig config, PtRouter ptRouter) { + this.config = config; this.ptRouter = ptRouter; } @@ -87,7 +90,7 @@ public ObjectNode route(@QueryParam("point") @Size(min=2,max=2) List pointParams, @QueryParam("type") @DefaultValue("json") String type, @@ -85,6 +90,7 @@ public Response doGet( @QueryParam(CALC_POINTS) @DefaultValue("true") boolean calcPoints, @QueryParam("elevation") @DefaultValue("false") boolean enableElevation, @QueryParam("points_encoded") @DefaultValue("true") boolean pointsEncoded, + @QueryParam("points_encoded_multiplier") @DefaultValue("1e5") double pointsEncodedMultiplier, @QueryParam("profile") String profileName, @QueryParam(ALGORITHM) @DefaultValue("") String algoStr, @QueryParam("locale") @DefaultValue("en") String localeStr, @@ -157,7 +163,7 @@ public Response doGet( header("X-GH-Took", "" + Math.round(took)). build() : - Response.ok(ResponsePathSerializer.jsonObject(ghResponse, osmDate, instructions, calcPoints, enableElevation, pointsEncoded, took)). + Response.ok(ResponsePathSerializer.jsonObject(ghResponse, new ResponsePathSerializer.Info(config.getCopyrights(), Math.round(took), osmDate), instructions, calcPoints, enableElevation, pointsEncoded, pointsEncodedMultiplier)). header("X-GH-Took", "" + Math.round(took)). type(MediaType.APPLICATION_JSON). build(); @@ -186,6 +192,7 @@ public Response doPost(@NotNull GHRequest request, @Context HttpServletRequest h boolean enableElevation = request.getHints().getBool("elevation", false); boolean calcPoints = request.getHints().getBool(CALC_POINTS, true); boolean pointsEncoded = request.getHints().getBool("points_encoded", true); + double pointsEncodedMultiplier = request.getHints().getDouble("points_encoded_multiplier", 1e5); double took = sw.stop().getMillisDouble(); String infoStr = httpReq.getRemoteAddr() + " " + httpReq.getLocale() + " " + httpReq.getHeader("User-Agent"); @@ -202,7 +209,7 @@ public Response doPost(@NotNull GHRequest request, @Context HttpServletRequest h + ", time0: " + Math.round(ghResponse.getBest().getTime() / 60000f) + "min" + ", points0: " + ghResponse.getBest().getPoints().size() + ", debugInfo: " + ghResponse.getDebugInfo()); - return Response.ok(ResponsePathSerializer.jsonObject(ghResponse, osmDate, instructions, calcPoints, enableElevation, pointsEncoded, took)). + return Response.ok(ResponsePathSerializer.jsonObject(ghResponse, new ResponsePathSerializer.Info(config.getCopyrights(), Math.round(took), osmDate), instructions, calcPoints, enableElevation, pointsEncoded, pointsEncodedMultiplier)). header("X-GH-Took", "" + Math.round(took)). type(MediaType.APPLICATION_JSON). build(); diff --git a/web-bundle/src/main/java/com/graphhopper/resources/SPTResource.java b/web-bundle/src/main/java/com/graphhopper/resources/SPTResource.java index a7a3cc947d2..4bd1bd33e20 100644 --- a/web-bundle/src/main/java/com/graphhopper/resources/SPTResource.java +++ b/web-bundle/src/main/java/com/graphhopper/resources/SPTResource.java @@ -100,7 +100,7 @@ public Response doGet( throw new IllegalArgumentException("Point not found:" + point); QueryGraph queryGraph = QueryGraph.create(graph, snap); NodeAccess nodeAccess = queryGraph.getNodeAccess(); - TraversalMode traversalMode = profile.isTurnCosts() ? EDGE_BASED : NODE_BASED; + TraversalMode traversalMode = profile.hasTurnCosts() ? EDGE_BASED : NODE_BASED; ShortestPathTree shortestPathTree = new ShortestPathTree(queryGraph, queryGraph.wrapWeighting(weighting), reverseFlow, traversalMode); if (distanceInMeter.orElseThrow(() -> new IllegalArgumentException("query param distance_limit is not a number.")) > 0) { diff --git a/web-bundle/src/test/java/com/graphhopper/gpx/GpxConversionsTest.java b/web-bundle/src/test/java/com/graphhopper/gpx/GpxConversionsTest.java index 8aa517dbe92..1462071cf27 100644 --- a/web-bundle/src/test/java/com/graphhopper/gpx/GpxConversionsTest.java +++ b/web-bundle/src/test/java/com/graphhopper/gpx/GpxConversionsTest.java @@ -21,13 +21,10 @@ import com.graphhopper.routing.Dijkstra; import com.graphhopper.routing.InstructionsFromEdges; import com.graphhopper.routing.Path; -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValueImpl; -import com.graphhopper.routing.ev.SimpleBooleanEncodedValue; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.routing.util.TraversalMode; -import com.graphhopper.routing.weighting.ShortestWeighting; +import com.graphhopper.routing.weighting.SpeedWeighting; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.NodeAccess; @@ -53,16 +50,14 @@ public class GpxConversionsTest { - private BooleanEncodedValue accessEnc; private DecimalEncodedValue speedEnc; private EncodingManager carManager; private TranslationMap trMap; @BeforeEach public void setUp() { - accessEnc = new SimpleBooleanEncodedValue("access", true); speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); - carManager = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + carManager = EncodingManager.start().add(speedEnc).add(Roundabout.create()).add(RoadClass.create()).add(RoadClassLink.create()).add(MaxSpeed.create()).build(); trMap = new TranslationMap().doImport(); } @@ -83,14 +78,14 @@ public void testInstructionsWithTimeAndPlace() { na.setNode(6, 15.1, 10.1); na.setNode(7, 15.1, 9.8); - GHUtility.setSpeed(63, true, true, accessEnc, speedEnc, g.edge(1, 2).setDistance(7000).setKeyValues(createKV(STREET_NAME, "1-2"))); - GHUtility.setSpeed(72, true, true, accessEnc, speedEnc, g.edge(2, 3).setDistance(8000).setKeyValues(createKV(STREET_NAME, "2-3"))); - GHUtility.setSpeed(9, true, true, accessEnc, speedEnc, g.edge(2, 6).setDistance(10000).setKeyValues(createKV(STREET_NAME, "2-6"))); - GHUtility.setSpeed(81, true, true, accessEnc, speedEnc, g.edge(3, 4).setDistance(9000).setKeyValues(createKV(STREET_NAME, "3-4"))); - GHUtility.setSpeed(9, true, true, accessEnc, speedEnc, g.edge(3, 7).setDistance(10000).setKeyValues(createKV(STREET_NAME, "3-7"))); - GHUtility.setSpeed(90, true, true, accessEnc, speedEnc, g.edge(4, 5).setDistance(10000).setKeyValues(createKV(STREET_NAME, "4-5"))); + g.edge(1, 2).set(speedEnc, 63).setDistance(7000).setKeyValues(createKV(STREET_NAME, "1-2")); + g.edge(2, 3).set(speedEnc, 72).setDistance(8000).setKeyValues(createKV(STREET_NAME, "2-3")); + g.edge(2, 6).set(speedEnc, 9).setDistance(10000).setKeyValues(createKV(STREET_NAME, "2-6")); + g.edge(3, 4).set(speedEnc, 81).setDistance(9000).setKeyValues(createKV(STREET_NAME, "3-4")); + g.edge(3, 7).set(speedEnc, 9).setDistance(10000).setKeyValues(createKV(STREET_NAME, "3-7")); + g.edge(4, 5).set(speedEnc, 90).setDistance(10000).setKeyValues(createKV(STREET_NAME, "4-5")); - Weighting weighting = new ShortestWeighting(accessEnc, speedEnc); + Weighting weighting = new SpeedWeighting(speedEnc); Path p = new Dijkstra(g, weighting, TraversalMode.NODE_BASED).calcPath(1, 5); InstructionList wayList = InstructionsFromEdges.calcInstructions(p, g, weighting, carManager, trMap.getWithFallBack(Locale.US)); PointList points = p.calcPoints(); @@ -99,7 +94,7 @@ public void testInstructionsWithTimeAndPlace() { assertEquals(34000, p.getDistance(), 1e-1); assertEquals(34000, sumDistances(wayList), 1e-1); assertEquals(5, points.size()); - assertEquals(1604121, p.getTime()); + assertEquals(445588, p.getTime()); assertEquals(Instruction.CONTINUE_ON_STREET, wayList.get(0).getSign()); assertEquals("1-2", wayList.get(0).getName()); diff --git a/web-bundle/src/test/resources/com/graphhopper/jackson/config.yml b/web-bundle/src/test/resources/com/graphhopper/jackson/config.yml index 71d80e1816a..ce66a1c94bc 100644 --- a/web-bundle/src/test/resources/com/graphhopper/jackson/config.yml +++ b/web-bundle/src/test/resources/com/graphhopper/jackson/config.yml @@ -1,11 +1,9 @@ datareader.file: map-matching/files/leipzig_germany.osm.pbf graph.location: graphs/leipzig-regular/graph -graph.vehicles: car profiles: - name: car - vehicle: car - weighting: fastest + weighting: custom index.max_region_search: 100 index: - pups: 200 \ No newline at end of file + pups: 200 diff --git a/web/nb-configuration.xml b/web/nb-configuration.xml deleted file mode 100644 index a40fe01b7d9..00000000000 --- a/web/nb-configuration.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - all - - diff --git a/web/pom.xml b/web/pom.xml index c05fb9bdbdf..c5e354ed5fb 100644 --- a/web/pom.xml +++ b/web/pom.xml @@ -5,14 +5,14 @@ 4.0.0 graphhopper-web jar - 8.0-osm-reader-callbacks + 9.1-osm-reader-callbacks-SNAPSHOT GraphHopper Web Use the GraphHopper routing engine as a web-service com.graphhopper graphhopper-parent - 8.0-osm-reader-callbacks + 9.1-osm-reader-callbacks-SNAPSHOT package diff --git a/web/src/test/java/com/graphhopper/application/GraphHopperLandmarksTest.java b/web/src/test/java/com/graphhopper/application/GraphHopperLandmarksTest.java index e3e5303dfbe..c5300314a86 100644 --- a/web/src/test/java/com/graphhopper/application/GraphHopperLandmarksTest.java +++ b/web/src/test/java/com/graphhopper/application/GraphHopperLandmarksTest.java @@ -21,7 +21,7 @@ import com.graphhopper.application.util.GraphHopperServerTestConfiguration; import com.graphhopper.config.CHProfile; import com.graphhopper.config.LMProfile; -import com.graphhopper.config.Profile; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.util.Helper; import io.dropwizard.testing.junit5.DropwizardAppExtension; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; @@ -51,15 +51,15 @@ public class GraphHopperLandmarksTest { private static GraphHopperServerConfiguration createConfig() { GraphHopperServerConfiguration config = new GraphHopperServerTestConfiguration(); config.getGraphHopperConfiguration(). - putObject("graph.vehicles", "car"). putObject("datareader.file", "../core/files/belarus-east.osm.gz"). + putObject("graph.encoded_values", "car_access, car_average_speed"). putObject("prepare.min_network_size", 0). putObject("import.osm.ignored_highways", ""). putObject("graph.location", DIR) // force landmark creation even for tiny networks .putObject("prepare.lm.min_network_size", 2) .setProfiles(Collections.singletonList( - new Profile("car_profile").setVehicle("car") + TestProfiles.accessAndSpeed("car_profile", "car") )) .setCHProfiles(Collections.singletonList( new CHProfile("car_profile") diff --git a/web/src/test/java/com/graphhopper/application/MapMatching2Test.java b/web/src/test/java/com/graphhopper/application/MapMatching2Test.java index c566f2a8524..950bbdc9dc2 100644 --- a/web/src/test/java/com/graphhopper/application/MapMatching2Test.java +++ b/web/src/test/java/com/graphhopper/application/MapMatching2Test.java @@ -20,13 +20,13 @@ import com.fasterxml.jackson.dataformat.xml.XmlMapper; import com.graphhopper.GraphHopper; import com.graphhopper.config.LMProfile; -import com.graphhopper.config.Profile; import com.graphhopper.gpx.GpxConversions; import com.graphhopper.jackson.Gpx; import com.graphhopper.matching.EdgeMatch; import com.graphhopper.matching.MapMatching; import com.graphhopper.matching.MatchResult; import com.graphhopper.matching.State; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.storage.index.Snap; import com.graphhopper.util.Helper; import com.graphhopper.util.PMap; @@ -59,7 +59,8 @@ public void testIssue13() throws IOException { GraphHopper hopper = new GraphHopper(); hopper.setOSMFile("../map-matching/files/map-issue13.osm.gz"); hopper.setGraphHopperLocation(GH_LOCATION); - hopper.setProfiles(new Profile("my_profile").setVehicle("car")); + hopper.setEncodedValuesString("car_access, car_average_speed"); + hopper.setProfiles(TestProfiles.accessAndSpeed("my_profile", "car")); hopper.getLMPreparationHandler().setLMProfiles(new LMProfile("my_profile")); hopper.importOrLoad(); @@ -84,7 +85,8 @@ public void testIssue70() throws IOException { GraphHopper hopper = new GraphHopper(); hopper.setOSMFile("../map-matching/files/issue-70.osm.gz"); hopper.setGraphHopperLocation(GH_LOCATION); - hopper.setProfiles(new Profile("my_profile").setVehicle("car")); + hopper.setEncodedValuesString("car_access, car_average_speed"); + hopper.setProfiles(TestProfiles.accessAndSpeed("my_profile", "car")); hopper.getLMPreparationHandler().setLMProfiles(new LMProfile("my_profile")); hopper.importOrLoad(); @@ -104,7 +106,8 @@ public void testIssue127() throws IOException { GraphHopper hopper = new GraphHopper(); hopper.setOSMFile("../map-matching/files/map-issue13.osm.gz"); hopper.setGraphHopperLocation(GH_LOCATION); - hopper.setProfiles(new Profile("my_profile").setVehicle("car")); + hopper.setEncodedValuesString("car_access, car_average_speed"); + hopper.setProfiles(TestProfiles.accessAndSpeed("my_profile", "car")); hopper.getLMPreparationHandler().setLMProfiles(new LMProfile("my_profile")); hopper.importOrLoad(); diff --git a/web/src/test/java/com/graphhopper/application/MapMatchingTest.java b/web/src/test/java/com/graphhopper/application/MapMatchingTest.java index a3fe82016ec..797eb2db047 100644 --- a/web/src/test/java/com/graphhopper/application/MapMatchingTest.java +++ b/web/src/test/java/com/graphhopper/application/MapMatchingTest.java @@ -22,13 +22,13 @@ import com.graphhopper.GraphHopper; import com.graphhopper.ResponsePath; import com.graphhopper.config.LMProfile; -import com.graphhopper.config.Profile; import com.graphhopper.gpx.GpxConversions; import com.graphhopper.jackson.Gpx; import com.graphhopper.matching.EdgeMatch; import com.graphhopper.matching.MapMatching; import com.graphhopper.matching.MatchResult; import com.graphhopper.matching.Observation; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.util.*; import com.graphhopper.util.shapes.GHPoint; import org.junit.jupiter.api.AfterAll; @@ -47,8 +47,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static com.graphhopper.json.Statement.If; -import static com.graphhopper.json.Statement.Op.MULTIPLY; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.core.Is.is; @@ -71,10 +69,8 @@ public static void setup() { graphHopper = new GraphHopper(); graphHopper.setOSMFile("../map-matching/files/leipzig_germany.osm.pbf"); graphHopper.setGraphHopperLocation(GH_LOCATION); - graphHopper.setProfiles(new Profile("my_profile"). - setCustomModel(new CustomModel(). - addToPriority(If("road_access == DESTINATION", MULTIPLY, "0.1"))). - setVehicle("car")); + graphHopper.setEncodedValuesString("car_access, car_average_speed"); + graphHopper.setProfiles(TestProfiles.accessAndSpeed("my_profile", "car")); graphHopper.getLMPreparationHandler().setLMProfiles(new LMProfile("my_profile")); graphHopper.importOrLoad(); } @@ -146,7 +142,7 @@ public void testDoWork(PMap hints) { assertEquals(route.getDistance(), mr.getMatchLength(), 0.5); // GraphHopper travel times aren't exactly additive assertThat(Math.abs(route.getTime() - mr.getMatchMillis()), is(lessThan(1000L))); - assertEquals(208, mr.getEdgeMatches().size()); + assertEquals(202, mr.getEdgeMatches().size()); } @ParameterizedTest @@ -173,7 +169,7 @@ public void testLongTrackWithTwoPoints(PMap hints) { new Observation(new GHPoint(51.23, 12.18)), new Observation(new GHPoint(51.45, 12.59))); MatchResult mr = mapMatching.match(inputGPXEntries); - assertEquals(57553.0, mr.getMatchLength(), 1.0); + assertEquals(57651, mr.getMatchLength(), 1.0); } @ParameterizedTest diff --git a/web/src/test/java/com/graphhopper/application/RoutingAdditivityTest.java b/web/src/test/java/com/graphhopper/application/RoutingAdditivityTest.java index 2228b9bc1fc..b5e2510d723 100644 --- a/web/src/test/java/com/graphhopper/application/RoutingAdditivityTest.java +++ b/web/src/test/java/com/graphhopper/application/RoutingAdditivityTest.java @@ -22,7 +22,7 @@ import com.graphhopper.GraphHopper; import com.graphhopper.ResponsePath; import com.graphhopper.config.LMProfile; -import com.graphhopper.config.Profile; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.util.Helper; import com.graphhopper.util.shapes.GHPoint; import org.junit.jupiter.api.AfterAll; @@ -48,7 +48,8 @@ public static void setup() { graphHopper = new GraphHopper(); graphHopper.setOSMFile("../map-matching/files/leipzig_germany.osm.pbf"); graphHopper.setGraphHopperLocation(GH_LOCATION); - graphHopper.setProfiles(new Profile("my_profile").setVehicle("car")); + graphHopper.setEncodedValuesString("car_access, car_average_speed"); + graphHopper.setProfiles(TestProfiles.accessAndSpeed("my_profile", "car")); graphHopper.getLMPreparationHandler().setLMProfiles(new LMProfile("my_profile")); graphHopper.importOrLoad(); } diff --git a/web/src/test/java/com/graphhopper/application/resources/ExtendedJsonResponseTest.java b/web/src/test/java/com/graphhopper/application/resources/ExtendedJsonResponseTest.java index 5607313bff3..cce7fabd6d3 100644 --- a/web/src/test/java/com/graphhopper/application/resources/ExtendedJsonResponseTest.java +++ b/web/src/test/java/com/graphhopper/application/resources/ExtendedJsonResponseTest.java @@ -43,7 +43,7 @@ public class ExtendedJsonResponseTest { @Test public void shouldCreateBasicStructure() { - JsonNode jsonObject = MapMatchingResource.convertToTree(new MatchResult(getEdgeMatch()), false, false); + JsonNode jsonObject = MapMatchingResource.convertToTree(new MatchResult(getEdgeMatch()), false, false, -1); JsonNode route = jsonObject.get("diary").get("entries").get(0); JsonNode link = route.get("links").get(0); JsonNode geometry = link.get("geometry"); diff --git a/web/src/test/java/com/graphhopper/application/resources/GpxTravelTimeConsistencyTest.java b/web/src/test/java/com/graphhopper/application/resources/GpxTravelTimeConsistencyTest.java index 0dddb530475..faee9707474 100644 --- a/web/src/test/java/com/graphhopper/application/resources/GpxTravelTimeConsistencyTest.java +++ b/web/src/test/java/com/graphhopper/application/resources/GpxTravelTimeConsistencyTest.java @@ -21,8 +21,8 @@ import com.graphhopper.GHRequest; import com.graphhopper.GraphHopper; import com.graphhopper.ResponsePath; -import com.graphhopper.config.Profile; import com.graphhopper.gpx.GpxConversions; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.util.Helper; import com.graphhopper.util.shapes.GHPoint; import org.junit.jupiter.api.BeforeAll; @@ -46,7 +46,7 @@ public static void beforeClass() { Helper.removeDir(new File(graphFileFoot)); hopper = new GraphHopper(). setOSMFile(osmFile). - setProfiles(new Profile("profile").setVehicle("foot")). + setProfiles(TestProfiles.constantSpeed("profile")). setStoreOnFlush(true). setGraphHopperLocation(graphFileFoot). importOrLoad(); diff --git a/web/src/test/java/com/graphhopper/application/resources/I18nResourceTest.java b/web/src/test/java/com/graphhopper/application/resources/I18nResourceTest.java index c2d764a41b6..ec0dc97c3c6 100644 --- a/web/src/test/java/com/graphhopper/application/resources/I18nResourceTest.java +++ b/web/src/test/java/com/graphhopper/application/resources/I18nResourceTest.java @@ -21,7 +21,7 @@ import com.graphhopper.application.GraphHopperApplication; import com.graphhopper.application.GraphHopperServerConfiguration; import com.graphhopper.application.util.GraphHopperServerTestConfiguration; -import com.graphhopper.config.Profile; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.util.Helper; import io.dropwizard.testing.junit5.DropwizardAppExtension; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; @@ -32,7 +32,7 @@ import javax.ws.rs.core.Response; import java.io.File; -import java.util.Collections; +import java.util.List; import static com.graphhopper.application.util.TestUtils.clientTarget; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -46,11 +46,10 @@ public class I18nResourceTest { private static GraphHopperServerConfiguration createConfig() { GraphHopperServerConfiguration config = new GraphHopperServerTestConfiguration(); config.getGraphHopperConfiguration(). - putObject("graph.vehicles", "car"). putObject("datareader.file", "../core/files/andorra.osm.pbf"). putObject("graph.location", DIR). putObject("import.osm.ignored_highways", ""). - setProfiles(Collections.singletonList(new Profile("car").setVehicle("car"))); + setProfiles(List.of(TestProfiles.constantSpeed("car"))); return config; } diff --git a/web/src/test/java/com/graphhopper/application/resources/IsochroneResourceTest.java b/web/src/test/java/com/graphhopper/application/resources/IsochroneResourceTest.java index 47f28709a1b..5dce2db840a 100644 --- a/web/src/test/java/com/graphhopper/application/resources/IsochroneResourceTest.java +++ b/web/src/test/java/com/graphhopper/application/resources/IsochroneResourceTest.java @@ -22,8 +22,8 @@ import com.graphhopper.application.GraphHopperApplication; import com.graphhopper.application.GraphHopperServerConfiguration; import com.graphhopper.application.util.GraphHopperServerTestConfiguration; -import com.graphhopper.config.Profile; -import com.graphhopper.util.CustomModel; +import com.graphhopper.config.TurnCostsConfig; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.util.Helper; import com.graphhopper.util.JsonFeatureCollection; import io.dropwizard.testing.junit5.DropwizardAppExtension; @@ -54,14 +54,14 @@ public class IsochroneResourceTest { private static GraphHopperServerConfiguration createConfig() { GraphHopperServerConfiguration config = new GraphHopperServerTestConfiguration(); config.getGraphHopperConfiguration(). - putObject("graph.vehicles", "car|turn_costs=true"). putObject("datareader.file", "../core/files/andorra.osm.pbf"). putObject("import.osm.ignored_highways", ""). putObject("graph.location", DIR). + putObject("graph.encoded_values", "car_access, car_average_speed"). setProfiles(Arrays.asList( - new Profile("fast_car").setVehicle("car").setTurnCosts(true), - new Profile("short_car").setCustomModel(new CustomModel().setDistanceInfluence(1_000d)).setVehicle("car").setTurnCosts(true), - new Profile("fast_car_no_turn_restrictions").setVehicle("car").setTurnCosts(false) + TestProfiles.accessAndSpeed("fast_car", "car").setTurnCostsConfig(TurnCostsConfig.car()), + TestProfiles.constantSpeed("short_car", 35).setTurnCostsConfig(TurnCostsConfig.car()), + TestProfiles.accessAndSpeed("fast_car_no_turn_restrictions", "car") )); return config; } @@ -369,6 +369,6 @@ public void requestTenBucketsIssue2094() { Polygon beforeLastPolygon = (Polygon) collection.getFeatures().get(collection.getFeatures().size() - 2).getGeometry(); assertTrue(beforeLastPolygon.contains(geometryFactory.createPoint(new Coordinate(1.564136, 42.524938)))); - assertFalse(beforeLastPolygon.contains(geometryFactory.createPoint(new Coordinate(1.571474, 42.529176)))); + assertFalse(beforeLastPolygon.contains(geometryFactory.createPoint(new Coordinate(1.575551, 42.532528)))); } } diff --git a/web/src/test/java/com/graphhopper/application/resources/MVTResourceTest.java b/web/src/test/java/com/graphhopper/application/resources/MVTResourceTest.java index 08a10fae109..0dc4737c1ec 100644 --- a/web/src/test/java/com/graphhopper/application/resources/MVTResourceTest.java +++ b/web/src/test/java/com/graphhopper/application/resources/MVTResourceTest.java @@ -20,7 +20,7 @@ import com.graphhopper.application.GraphHopperApplication; import com.graphhopper.application.GraphHopperServerConfiguration; import com.graphhopper.application.util.GraphHopperServerTestConfiguration; -import com.graphhopper.config.Profile; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.util.Helper; import io.dropwizard.testing.junit5.DropwizardAppExtension; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; @@ -36,7 +36,10 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; import static com.graphhopper.application.util.TestUtils.clientTarget; import static com.graphhopper.util.Parameters.Details.STREET_NAME; @@ -53,13 +56,12 @@ public class MVTResourceTest { private static GraphHopperServerConfiguration createConfig() { GraphHopperServerConfiguration config = new GraphHopperServerTestConfiguration(); config.getGraphHopperConfiguration(). - putObject("graph.vehicles", "car"). putObject("graph.encoded_values", "road_class,road_environment,max_speed,surface"). putObject("prepare.min_network_size", 0). putObject("datareader.file", "../core/files/andorra.osm.pbf"). putObject("import.osm.ignored_highways", ""). putObject("graph.location", DIR). - setProfiles(Collections.singletonList(new Profile("car").setVehicle("car"))); + setProfiles(List.of(TestProfiles.constantSpeed("car"))); return config; } @@ -79,7 +81,7 @@ public void testBasicMvtQuery() throws IOException { VectorTileDecoder.Feature feature = features.iterator().next(); Map attributes = feature.getAttributes(); Geometry geometry = feature.getGeometry(); - assertEquals(48, geometry.getCoordinates().length); + assertEquals(51, geometry.getCoordinates().length); assertEquals("Camì de les Pardines", attributes.get(STREET_NAME)); } diff --git a/web/src/test/java/com/graphhopper/application/resources/MapMatchingResourceTest.java b/web/src/test/java/com/graphhopper/application/resources/MapMatchingResourceTest.java index e8a03ec0500..f41b4281f39 100644 --- a/web/src/test/java/com/graphhopper/application/resources/MapMatchingResourceTest.java +++ b/web/src/test/java/com/graphhopper/application/resources/MapMatchingResourceTest.java @@ -20,8 +20,8 @@ import com.fasterxml.jackson.databind.JsonNode; import com.graphhopper.application.GraphHopperApplication; import com.graphhopper.application.GraphHopperServerConfiguration; -import com.graphhopper.config.Profile; -import com.graphhopper.jackson.ResponsePathDeserializer; +import com.graphhopper.jackson.ResponsePathDeserializerHelper; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.util.Helper; import io.dropwizard.testing.junit5.DropwizardAppExtension; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; @@ -54,13 +54,13 @@ public class MapMatchingResourceTest { private static GraphHopperServerConfiguration createConfig() { GraphHopperServerConfiguration config = new GraphHopperServerConfiguration(); config.getGraphHopperConfiguration(). - putObject("graph.vehicles", "car,bike"). putObject("datareader.file", "../map-matching/files/leipzig_germany.osm.pbf"). putObject("import.osm.ignored_highways", ""). putObject("graph.location", DIR). + putObject("graph.encoded_values", "car_access, car_average_speed, bike_access, bike_priority, bike_average_speed"). setProfiles(Arrays.asList( - new Profile("fast_car").setVehicle("car"), - new Profile("fast_bike").setVehicle("bike"))); + TestProfiles.accessAndSpeed("fast_car", "car"), + TestProfiles.accessSpeedAndPriority("fast_bike", "bike"))); return config; } @@ -80,7 +80,7 @@ public void testGPX() { JsonNode path = json.get("paths").get(0); LineString expectedGeometry = readWktLineString("LINESTRING (12.3607 51.34365, 12.36418 51.34443, 12.36379 51.34538, 12.36082 51.34471, 12.36188 51.34278)"); - LineString actualGeometry = ResponsePathDeserializer.decodePolyline(path.get("points").asText(), 10, false).toLineString(false); + LineString actualGeometry = ResponsePathDeserializerHelper.decodePolyline(path.get("points").asText(), 10, false, 1e5).toLineString(false); assertEquals(DiscreteHausdorffDistance.distance(expectedGeometry, actualGeometry), 0.0, 1E-4); assertEquals(101, path.get("time").asLong() / 1000f, 1); assertEquals(101, json.get("map_matching").get("time").asLong() / 1000f, 1); @@ -100,7 +100,7 @@ public void testBike() throws ParseException { JsonNode path = json.get("paths").get(0); LineString expectedGeometry = (LineString) wktReader.read("LINESTRING (12.3607 51.34365, 12.36418 51.34443, 12.36379 51.34538, 12.36082 51.34471, 12.36188 51.34278)"); - LineString actualGeometry = ResponsePathDeserializer.decodePolyline(path.get("points").asText(), 10, false).toLineString(false); + LineString actualGeometry = ResponsePathDeserializerHelper.decodePolyline(path.get("points").asText(), 10, false, 1e5).toLineString(false); assertEquals(DiscreteHausdorffDistance.distance(expectedGeometry, actualGeometry), 0.0, 1E-4); // ensure that is actually also is bike! (slower than car) diff --git a/web/src/test/java/com/graphhopper/application/resources/MapMatchingResourceTurnCostsTest.java b/web/src/test/java/com/graphhopper/application/resources/MapMatchingResourceTurnCostsTest.java index 7cb9a2adeb9..d25f7092511 100644 --- a/web/src/test/java/com/graphhopper/application/resources/MapMatchingResourceTurnCostsTest.java +++ b/web/src/test/java/com/graphhopper/application/resources/MapMatchingResourceTurnCostsTest.java @@ -22,8 +22,9 @@ import com.graphhopper.application.GraphHopperServerConfiguration; import com.graphhopper.config.CHProfile; import com.graphhopper.config.LMProfile; -import com.graphhopper.config.Profile; -import com.graphhopper.jackson.ResponsePathDeserializer; +import com.graphhopper.jackson.ResponsePathDeserializerHelper; +import com.graphhopper.config.TurnCostsConfig; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.util.Helper; import io.dropwizard.testing.junit5.DropwizardAppExtension; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; @@ -57,14 +58,14 @@ public class MapMatchingResourceTurnCostsTest { private static GraphHopperServerConfiguration createConfig() { GraphHopperServerConfiguration config = new GraphHopperServerConfiguration(); config.getGraphHopperConfiguration(). - putObject("graph.vehicles", "car|turn_costs=true,bike"). putObject("datareader.file", "../map-matching/files/leipzig_germany.osm.pbf"). putObject("import.osm.ignored_highways", ""). putObject("graph.location", DIR). + putObject("graph.encoded_values", "car_access, car_average_speed, bike_access, bike_priority, bike_average_speed"). setProfiles(Arrays.asList( - new Profile("car").setVehicle("car").setTurnCosts(true), - new Profile("car_no_tc").setVehicle("car"), - new Profile("bike").setVehicle("bike")) + TestProfiles.accessAndSpeed("car").setTurnCostsConfig(TurnCostsConfig.car()), + TestProfiles.accessAndSpeed("car_no_tc", "car"), + TestProfiles.accessSpeedAndPriority("bike")) ). setLMProfiles(Arrays.asList( new LMProfile("car"), @@ -121,7 +122,7 @@ private void runCar(String urlParams) { JsonNode path = json.get("paths").get(0); LineString expectedGeometry = readWktLineString("LINESTRING (12.3607 51.34365, 12.36418 51.34443, 12.36379 51.34538, 12.36082 51.34471, 12.36188 51.34278)"); - LineString actualGeometry = ResponsePathDeserializer.decodePolyline(path.get("points").asText(), 10, false).toLineString(false); + LineString actualGeometry = ResponsePathDeserializerHelper.decodePolyline(path.get("points").asText(), 10, false, 1e5).toLineString(false); assertEquals(DiscreteHausdorffDistance.distance(expectedGeometry, actualGeometry), 0.0, 1E-4); assertEquals(101, path.get("time").asLong() / 1000f, 1); assertEquals(101, json.get("map_matching").get("time").asLong() / 1000f, 1); @@ -140,7 +141,7 @@ private void runBike(String urlParams) { JsonNode path = json.get("paths").get(0); LineString expectedGeometry = readWktLineString("LINESTRING (12.3607 51.34365, 12.36418 51.34443, 12.36379 51.34538, 12.36082 51.34471, 12.36188 51.34278)"); - LineString actualGeometry = ResponsePathDeserializer.decodePolyline(path.get("points").asText(), 10, false).toLineString(false); + LineString actualGeometry = ResponsePathDeserializerHelper.decodePolyline(path.get("points").asText(), 10, false, 1e5).toLineString(false); assertEquals(DiscreteHausdorffDistance.distance(expectedGeometry, actualGeometry), 0.0, 1E-4); assertEquals(162.31, path.get("time").asLong() / 1000f, 0.1); assertEquals(162.31, json.get("map_matching").get("time").asLong() / 1000f, 0.1); diff --git a/web/src/test/java/com/graphhopper/application/resources/NearestResourceTest.java b/web/src/test/java/com/graphhopper/application/resources/NearestResourceTest.java index 6e1a69808fd..4531e71a317 100644 --- a/web/src/test/java/com/graphhopper/application/resources/NearestResourceTest.java +++ b/web/src/test/java/com/graphhopper/application/resources/NearestResourceTest.java @@ -20,8 +20,8 @@ import com.graphhopper.application.GraphHopperApplication; import com.graphhopper.application.GraphHopperServerConfiguration; import com.graphhopper.application.util.GraphHopperServerTestConfiguration; -import com.graphhopper.config.Profile; import com.graphhopper.resources.NearestResource; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.util.Helper; import io.dropwizard.testing.junit5.DropwizardAppExtension; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; @@ -32,7 +32,7 @@ import javax.ws.rs.core.Response; import java.io.File; -import java.util.Collections; +import java.util.List; import static com.graphhopper.application.util.TestUtils.clientTarget; import static org.junit.jupiter.api.Assertions.assertArrayEquals; @@ -49,11 +49,10 @@ public class NearestResourceTest { private static GraphHopperServerConfiguration createConfig() { GraphHopperServerConfiguration config = new GraphHopperServerTestConfiguration(); config.getGraphHopperConfiguration(). - putObject("graph.vehicles", "car"). putObject("datareader.file", "../core/files/andorra.osm.pbf"). putObject("graph.location", dir). putObject("import.osm.ignored_highways", ""). - setProfiles(Collections.singletonList(new Profile("car").setVehicle("car"))); + setProfiles(List.of(TestProfiles.constantSpeed("car"))); return config; } diff --git a/web/src/test/java/com/graphhopper/application/resources/NearestResourceWithEleTest.java b/web/src/test/java/com/graphhopper/application/resources/NearestResourceWithEleTest.java index ead34bd125a..fc26ef9308c 100644 --- a/web/src/test/java/com/graphhopper/application/resources/NearestResourceWithEleTest.java +++ b/web/src/test/java/com/graphhopper/application/resources/NearestResourceWithEleTest.java @@ -22,7 +22,7 @@ import com.graphhopper.application.GraphHopperApplication; import com.graphhopper.application.GraphHopperServerConfiguration; import com.graphhopper.application.util.GraphHopperServerTestConfiguration; -import com.graphhopper.config.Profile; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.util.Helper; import io.dropwizard.testing.junit5.DropwizardAppExtension; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; @@ -32,7 +32,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import java.io.File; -import java.util.Collections; +import java.util.List; import static com.graphhopper.application.util.TestUtils.clientTarget; import static org.junit.jupiter.api.Assertions.*; @@ -51,11 +51,10 @@ private static GraphHopperServerConfiguration createConfig() { putObject("graph.elevation.provider", "srtm"). putObject("graph.elevation.cache_dir", "../core/files/"). putObject("prepare.min_network_size", 0). - putObject("graph.vehicles", "car"). putObject("datareader.file", "../core/files/monaco.osm.gz"). putObject("import.osm.ignored_highways", ""). putObject("graph.location", dir). - setProfiles(Collections.singletonList(new Profile("car").setVehicle("car"))); + setProfiles(List.of(TestProfiles.constantSpeed("car"))); return config; } diff --git a/web/src/test/java/com/graphhopper/application/resources/PtIsochroneTest.java b/web/src/test/java/com/graphhopper/application/resources/PtIsochroneTest.java index f115bb76b4b..14b7d21be52 100644 --- a/web/src/test/java/com/graphhopper/application/resources/PtIsochroneTest.java +++ b/web/src/test/java/com/graphhopper/application/resources/PtIsochroneTest.java @@ -21,8 +21,8 @@ import com.graphhopper.application.GraphHopperApplication; import com.graphhopper.application.GraphHopperServerConfiguration; import com.graphhopper.application.util.GraphHopperServerTestConfiguration; -import com.graphhopper.config.Profile; import com.graphhopper.resources.PtIsochroneResource; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.util.Helper; import io.dropwizard.testing.junit5.DropwizardAppExtension; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; @@ -39,7 +39,7 @@ import java.io.File; import java.time.LocalDateTime; import java.time.ZoneId; -import java.util.Collections; +import java.util.List; import static com.graphhopper.application.util.TestUtils.clientTarget; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -55,12 +55,12 @@ public class PtIsochroneTest { private static GraphHopperServerConfiguration createConfig() { GraphHopperServerConfiguration config = new GraphHopperServerTestConfiguration(); - config.getGraphHopperConfiguration() - .putObject("graph.vehicles", "foot") - .putObject("graph.location", GRAPH_LOC) - .putObject("gtfs.file", "../reader-gtfs/files/sample-feed") - .putObject("import.osm.ignored_highways", ""). - setProfiles(Collections.singletonList(new Profile("foot").setVehicle("foot"))); + config.getGraphHopperConfiguration(). + putObject("graph.location", GRAPH_LOC). + putObject("gtfs.file", "../reader-gtfs/files/sample-feed"). + putObject("import.osm.ignored_highways", ""). + putObject("graph.encoded_values", "foot_access, foot_priority, foot_average_speed"). + setProfiles(List.of(TestProfiles.accessSpeedAndPriority("foot"))); Helper.removeDir(new File(GRAPH_LOC)); return config; } diff --git a/web/src/test/java/com/graphhopper/application/resources/PtRouteResourceTest.java b/web/src/test/java/com/graphhopper/application/resources/PtRouteResourceTest.java index d536ce7a9ea..86e0ecccf69 100644 --- a/web/src/test/java/com/graphhopper/application/resources/PtRouteResourceTest.java +++ b/web/src/test/java/com/graphhopper/application/resources/PtRouteResourceTest.java @@ -22,8 +22,8 @@ import com.graphhopper.application.GraphHopperApplication; import com.graphhopper.application.GraphHopperServerConfiguration; import com.graphhopper.application.util.GraphHopperServerTestConfiguration; -import com.graphhopper.config.Profile; import com.graphhopper.resources.InfoResource; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.util.Helper; import io.dropwizard.testing.junit5.DropwizardAppExtension; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; @@ -34,7 +34,7 @@ import javax.ws.rs.core.Response; import java.io.File; -import java.util.Collections; +import java.util.List; import static com.graphhopper.application.util.TestUtils.clientTarget; import static org.junit.jupiter.api.Assertions.*; @@ -55,7 +55,8 @@ private static GraphHopperServerConfiguration createConfig() { putObject("gtfs.file", "../reader-gtfs/files/sample-feed"). putObject("graph.location", DIR). putObject("import.osm.ignored_highways", ""). - setProfiles(Collections.singletonList(new Profile("foot").setVehicle("foot"))); + putObject("graph.encoded_values", "foot_access, foot_priority, foot_average_speed"). + setProfiles(List.of(TestProfiles.accessSpeedAndPriority("foot"))); return config; } diff --git a/web/src/test/java/com/graphhopper/application/resources/RouteResourceClientHCTest.java b/web/src/test/java/com/graphhopper/application/resources/RouteResourceClientHCTest.java index fb9b59feda9..2f6c917fa15 100644 --- a/web/src/test/java/com/graphhopper/application/resources/RouteResourceClientHCTest.java +++ b/web/src/test/java/com/graphhopper/application/resources/RouteResourceClientHCTest.java @@ -26,7 +26,7 @@ import com.graphhopper.application.util.GraphHopperServerTestConfiguration; import com.graphhopper.application.util.TestUtils; import com.graphhopper.config.CHProfile; -import com.graphhopper.config.Profile; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.util.*; import com.graphhopper.util.details.PathDetail; import com.graphhopper.util.exceptions.PointNotFoundException; @@ -61,20 +61,20 @@ public class RouteResourceClientHCTest { private static GraphHopperServerConfiguration createConfig() { GraphHopperServerConfiguration config = new GraphHopperServerTestConfiguration(); config.getGraphHopperConfiguration(). - putObject("graph.vehicles", "car,bike"). putObject("prepare.min_network_size", 0). putObject("graph.elevation.provider", "srtm"). putObject("graph.elevation.cache_dir", "../core/files/"). putObject("datareader.file", "../core/files/andorra.osm.pbf"). putObject("graph.encoded_values", "road_class,surface,road_environment,max_speed"). putObject("import.osm.ignored_highways", ""). - putObject("graph.location", DIR) - .setProfiles(Arrays.asList( - new Profile("car").setVehicle("car"), - new Profile("bike").setVehicle("bike"), - new Profile("my_custom_car").setVehicle("car") - )) - .setCHProfiles(Arrays.asList(new CHProfile("car"), new CHProfile("bike"))); + putObject("graph.location", DIR). + putObject("graph.encoded_values", "road_class, surface, road_environment, max_speed, car_access, car_average_speed, bike_access, bike_priority, bike_average_speed"). + setProfiles(Arrays.asList( + TestProfiles.accessAndSpeed("car"), + TestProfiles.accessSpeedAndPriority("bike"), + TestProfiles.accessAndSpeed("my_custom_car", "car") + )). + setCHProfiles(Arrays.asList(new CHProfile("car"), new CHProfile("bike"))); return config; } @@ -118,10 +118,10 @@ public void testSimpleRoute(TestParam p) { GHResponse rsp = gh.route(req); assertFalse(rsp.hasErrors(), "errors:" + rsp.getErrors().toString()); ResponsePath res = rsp.getBest(); - isBetween(60, 70, res.getPoints().size()); + isBetween(70, 80, res.getPoints().size()); isBetween(2900, 3000, res.getDistance()); isBetween(110, 120, res.getAscend()); - isBetween(70, 80, res.getDescend()); + isBetween(75, 85, res.getDescend()); isBetween(190, 200, res.getRouteWeight()); // change vehicle @@ -152,13 +152,13 @@ public void testAlternativeRoute(TestParam p) { assertEquals(2, paths.size()); ResponsePath path = paths.get(0); - assertEquals(35, path.getPoints().size()); - assertEquals(1689, path.getDistance(), 1); + assertEquals(52, path.getPoints().size()); + assertEquals(1690, path.getDistance(), 1); assertTrue(path.getInstructions().toString().contains("Avinguda de Tarragona"), path.getInstructions().toString()); path = paths.get(1); - assertEquals(30, path.getPoints().size()); - assertEquals(1759, path.getDistance(), 1); + assertEquals(35, path.getPoints().size()); + assertEquals(1763, path.getDistance(), 1); assertTrue(path.getInstructions().toString().contains("Avinguda Prat de la Creu"), path.getInstructions().toString()); } @@ -388,6 +388,8 @@ public void testWaypointIndicesAndLegDetails(TestParam p) { GraphHopperWeb gh = createGH(p); List legDetails = Arrays.asList("leg_time", "leg_distance", "leg_weight"); GHRequest req = new GHRequest(). + addPoint(new GHPoint(42.509141, 1.546063)). + // #2915: duplicating the first point yields an empty leg, but there should still be path details for it addPoint(new GHPoint(42.509141, 1.546063)). addPoint(new GHPoint(42.507173, 1.531902)). addPoint(new GHPoint(42.505435, 1.515943)). @@ -397,33 +399,39 @@ public void testWaypointIndicesAndLegDetails(TestParam p) { addPoint(new GHPoint(42.49833, 1.504619)). addPoint(new GHPoint(42.498217, 1.504377)). addPoint(new GHPoint(42.495611, 1.498368)). + // #2915: duplicating the last point yields an empty leg, but there should still be path details for it + addPoint(new GHPoint(42.495611, 1.498368)). setPathDetails(legDetails). setProfile("bike"); GHResponse response = gh.route(req); ResponsePath path = response.getBest(); - assertEquals(5428, path.getDistance(), 5); - assertEquals(9, path.getWaypoints().size()); + assertEquals(5436, path.getDistance(), 5); + assertEquals(11, path.getWaypoints().size()); assertEquals(path.getTime(), path.getPathDetails().get("leg_time").stream().mapToLong(d -> (long) d.getValue()).sum(), 1); assertEquals(path.getDistance(), path.getPathDetails().get("leg_distance").stream().mapToDouble(d -> (double) d.getValue()).sum(), 1); assertEquals(path.getRouteWeight(), path.getPathDetails().get("leg_weight").stream().mapToDouble(d -> (double) d.getValue()).sum(), 1); + assertEquals(10, path.getPathDetails().get("leg_time").size()); + assertEquals(10, path.getPathDetails().get("leg_distance").size()); + assertEquals(10, path.getPathDetails().get("leg_weight").size()); + List pointListFromInstructions = getPointListFromInstructions(path); for (String detail : legDetails) { List pathDetails = path.getPathDetails().get(detail); // explicitly check one of the waypoints - assertEquals(42.50539, path.getWaypoints().get(2).lat); - assertEquals(42.50539, path.getPoints().get(pathDetails.get(1).getLast()).getLat()); - assertEquals(42.50539, path.getPoints().get(pathDetails.get(2).getFirst()).getLat()); + assertEquals(42.505398, path.getWaypoints().get(3).lat); + assertEquals(42.505398, path.getPoints().get(pathDetails.get(2).getLast()).getLat()); + assertEquals(42.505398, path.getPoints().get(pathDetails.get(3).getFirst()).getLat()); // check all the waypoints assertEquals(path.getWaypoints().get(0), path.getPoints().get(pathDetails.get(0).getFirst())); for (int i = 1; i < path.getWaypoints().size(); ++i) assertEquals(path.getWaypoints().get(i), path.getPoints().get(pathDetails.get(i - 1).getLast())); List pointListFromLegDetails = getPointListFromLegDetails(path, detail); - assertEquals(8, pointListFromLegDetails.size()); + assertEquals(10, pointListFromLegDetails.size()); assertPointListsEquals(pointListFromInstructions, pointListFromLegDetails); } } diff --git a/web/src/test/java/com/graphhopper/application/resources/RouteResourceCustomModelLMTest.java b/web/src/test/java/com/graphhopper/application/resources/RouteResourceCustomModelLMTest.java index 6b8685bde23..f32ffa3cc1e 100644 --- a/web/src/test/java/com/graphhopper/application/resources/RouteResourceCustomModelLMTest.java +++ b/web/src/test/java/com/graphhopper/application/resources/RouteResourceCustomModelLMTest.java @@ -22,8 +22,7 @@ import com.graphhopper.application.GraphHopperServerConfiguration; import com.graphhopper.application.util.GraphHopperServerTestConfiguration; import com.graphhopper.config.LMProfile; -import com.graphhopper.config.Profile; -import com.graphhopper.util.CustomModel; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.util.Helper; import io.dropwizard.testing.junit5.DropwizardAppExtension; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; @@ -52,15 +51,14 @@ public class RouteResourceCustomModelLMTest { private static GraphHopperServerConfiguration createConfig() { GraphHopperServerConfiguration config = new GraphHopperServerTestConfiguration(); config.getGraphHopperConfiguration(). - putObject("graph.vehicles", "car,foot"). putObject("datareader.file", "../core/files/andorra.osm.pbf"). putObject("graph.location", DIR). putObject("import.osm.ignored_highways", ""). - putObject("graph.encoded_values", "surface"). + putObject("graph.encoded_values", "surface, car_access, car_average_speed, foot_access, foot_priority, foot_average_speed"). setProfiles(Arrays.asList( - new Profile("car_custom").setCustomModel(new CustomModel().setDistanceInfluence(15d)).setVehicle("car"), - new Profile("foot_profile").setVehicle("foot"), - new Profile("foot_custom").setVehicle("foot"))). + TestProfiles.accessAndSpeed("car_custom", "car"), + TestProfiles.accessSpeedAndPriority("foot_profile", "foot"), + TestProfiles.accessSpeedAndPriority("foot_custom", "foot"))). setLMProfiles(Arrays.asList(new LMProfile("car_custom"), new LMProfile("foot_custom"))); return config; } diff --git a/web/src/test/java/com/graphhopper/application/resources/RouteResourceCustomModelTest.java b/web/src/test/java/com/graphhopper/application/resources/RouteResourceCustomModelTest.java index f32d9ccc8df..abb3cbf150d 100644 --- a/web/src/test/java/com/graphhopper/application/resources/RouteResourceCustomModelTest.java +++ b/web/src/test/java/com/graphhopper/application/resources/RouteResourceCustomModelTest.java @@ -24,7 +24,7 @@ import com.graphhopper.application.util.GraphHopperServerTestConfiguration; import com.graphhopper.config.CHProfile; import com.graphhopper.config.Profile; -import com.graphhopper.util.CustomModel; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.util.GHUtility; import com.graphhopper.util.Helper; import io.dropwizard.testing.junit5.DropwizardAppExtension; @@ -42,6 +42,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.util.Arrays; +import java.util.List; import static com.graphhopper.application.util.TestUtils.clientTarget; import static com.graphhopper.json.Statement.If; @@ -58,44 +59,36 @@ public class RouteResourceCustomModelTest { private static GraphHopperServerConfiguration createConfig() { GraphHopperServerConfiguration config = new GraphHopperServerTestConfiguration(); config.getGraphHopperConfiguration(). - putObject("graph.vehicles", "bike,car,foot,wheelchair,roads"). putObject("prepare.min_network_size", 200). putObject("datareader.file", "../core/files/north-bayreuth.osm.gz"). putObject("graph.location", DIR). - putObject("graph.encoded_values", "max_height,max_weight,max_width,hazmat,toll,surface,track_type,hgv,average_slope,max_slope"). + putObject("graph.encoded_values", "max_height,max_weight,max_width,hazmat,toll,surface,track_type,hgv,average_slope,max_slope,bus_access"). putObject("custom_areas.directory", "./src/test/resources/com/graphhopper/application/resources/areas"). putObject("import.osm.ignored_highways", ""). - setProfiles(Arrays.asList( - new Profile("wheelchair")/* TODO .setVehicle("wheelchair") */, - new Profile("roads").setCustomModel(new CustomModel()).setVehicle("roads"), - new Profile("car").setCustomModel(new CustomModel().setDistanceInfluence(70d)).setVehicle("car"), - new Profile("car_with_area").setCustomModel(new CustomModel().addToPriority(If("in_external_area52", MULTIPLY, "0.05"))), - new Profile("bike").setCustomModel(new CustomModel().setDistanceInfluence(0d)).setVehicle("bike"), - new Profile("bike_fastest").setVehicle("bike"), - new Profile("bus").setCustomModel(null).setVehicle("roads").putHint("custom_model_files", Arrays.asList("bus.json")), - new Profile("cargo_bike").setCustomModel(null).setVehicle("bike"). - putHint("custom_model_files", Arrays.asList("cargo_bike.json")), - new Profile("json_bike").setCustomModel(null).setVehicle("roads"). - putHint("custom_model_files", Arrays.asList("bike.json", "bike_elevation.json")), - new Profile("foot_profile").setVehicle("foot"), - new Profile("car_no_unclassified").setCustomModel( - new CustomModel(new CustomModel(). - addToPriority(If("road_class == UNCLASSIFIED", LIMIT, "0")))). - setVehicle("car"), + putObject("graph.encoded_values", "max_height, max_weight, max_width, hazmat, toll, surface, track_type, hgv, average_slope, max_slope, bus_access, " + + "car_access, car_average_speed, bike_access, bike_priority, bike_average_speed, road_class, road_access, get_off_bike, roundabout, foot_access, foot_priority, foot_average_speed"). + setProfiles(List.of( + TestProfiles.constantSpeed("roads", 120), + new Profile("car").setCustomModel(TestProfiles.accessAndSpeed("unused", "car").getCustomModel().setDistanceInfluence(70d)), + new Profile("car_with_area").setCustomModel(TestProfiles.accessAndSpeed("unused", "car").getCustomModel().addToPriority(If("in_external_area52", MULTIPLY, "0.05"))), + TestProfiles.accessSpeedAndPriority("bike"), + new Profile("bus").setCustomModel(null).putHint("custom_model_files", List.of("bus.json")), + new Profile("cargo_bike").setCustomModel(null).putHint("custom_model_files", List.of("cargo_bike.json")), + new Profile("json_bike").setCustomModel(null).putHint("custom_model_files", List.of("bike.json", "bike_elevation.json")), + TestProfiles.accessSpeedAndPriority("foot_profile", "foot"), + new Profile("car_no_unclassified").setCustomModel(TestProfiles.accessAndSpeed("unused", "car").getCustomModel(). + addToPriority(If("road_class == UNCLASSIFIED", LIMIT, "0"))), new Profile("custom_bike"). - setCustomModel(new CustomModel(). + setCustomModel(TestProfiles.accessSpeedAndPriority("unused", "bike").getCustomModel(). addToSpeed(If("road_class == PRIMARY", LIMIT, "28")). - addToPriority(If("max_width < 1.2", MULTIPLY, "0"))). - setVehicle("bike"), + addToPriority(If("max_width < 1.2", MULTIPLY, "0"))), new Profile("custom_bike2").setCustomModel( - new CustomModel(new CustomModel().setDistanceInfluence(70d). - addToPriority(If("road_class == TERTIARY || road_class == TRACK", MULTIPLY, "0")))). - setVehicle("bike"), - new Profile("custom_bike3").setCustomModel( - new CustomModel(new CustomModel(). + TestProfiles.accessSpeedAndPriority("unused", "bike").getCustomModel().setDistanceInfluence(70d). + addToPriority(If("road_class == TERTIARY || road_class == TRACK", MULTIPLY, "0"))), + new Profile("custom_bike3").setCustomModel(TestProfiles.accessSpeedAndPriority("unused", "bike").getCustomModel(). addToSpeed(If("road_class == TERTIARY || road_class == TRACK", MULTIPLY, "10")). - addToSpeed(If("true", LIMIT, "40")))). - setVehicle("bike"))). + addToSpeed(If("true", LIMIT, "40"))) + )). setCHProfiles(Arrays.asList(new CHProfile("bus"), new CHProfile("car_no_unclassified"))); return config; } @@ -118,8 +111,8 @@ public void testBus() { // the bus profile is a custom profile and we can use its CH preparation as long as we do not add a custom model String body = "{\"points\": [[11.58199, 50.0141], [11.5865, 50.0095]], \"profile\": \"bus\"}"; JsonNode path = getPath(body); - assertEquals(1500, path.get("distance").asDouble(), 10); - assertEquals(162_000, path.get("time").asLong(), 1_000); + assertEquals(610, path.get("distance").asDouble(), 10); + assertEquals(27_000, path.get("time").asLong(), 1_000); } @Test @@ -228,21 +221,12 @@ public void testBikeWithPriority() { String coords = " \"points\": [[11.539421, 50.018274], [11.593966, 50.007739]],"; String jsonQuery = "{" + coords + - " \"profile\": \"bike_fastest\"," + + " \"profile\": \"bike\"," + " \"ch.disable\": true" + "}"; JsonNode path = getPath(jsonQuery); double expectedDistance = path.get("distance").asDouble(); assertEquals(4781, expectedDistance, 10); - - // base profile bike has to use distance_influence = 0 (unlike default) otherwise not comparable to "fastest" - jsonQuery = "{" + - coords + - " \"profile\": \"bike\"," + - " \"ch.disable\": true" + - "}"; - path = getPath(jsonQuery); - assertEquals(4781, path.get("distance").asDouble(), 10); } @Test @@ -318,17 +302,6 @@ public void customBikeWithHighSpeed() { assertEquals(77, path.get("time").asLong() / 1000, 1); } - @Test - public void wheelchair() { - String jsonQuery = "{" + - " \"points\": [[11.58199, 50.0141], [11.5865, 50.0095]]," + - " \"profile\": \"wheelchair\"," + - " \"ch.disable\": true" + - "}"; - JsonNode path = getPath(jsonQuery); - assertEquals(1500, path.get("distance").asDouble(), 10); - } - @Test public void testHgv() { String body = "{\"points\": [[11.603998, 50.014554], [11.594095, 50.023334]], \"profile\": \"roads\", \"ch.disable\":true," + @@ -337,7 +310,7 @@ public void testHgv() { " \"priority\": [{\"if\": \"car_access == false || hgv == NO || max_width < 3 || max_height < 4\", \"multiply_by\": \"0\"}]}}"; JsonNode path = getPath(body); assertEquals(7314, path.get("distance").asDouble(), 10); - assertEquals(943 * 1000, path.get("time").asLong(), 1_000); + assertEquals(944 * 1000, path.get("time").asLong(), 1_000); } private void assertMessageStartsWith(JsonNode jsonNode, String message) { diff --git a/web/src/test/java/com/graphhopper/application/resources/RouteResourceIssue2020Test.java b/web/src/test/java/com/graphhopper/application/resources/RouteResourceIssue2020Test.java index 3ffeb757dd3..e72b699fb11 100644 --- a/web/src/test/java/com/graphhopper/application/resources/RouteResourceIssue2020Test.java +++ b/web/src/test/java/com/graphhopper/application/resources/RouteResourceIssue2020Test.java @@ -23,7 +23,7 @@ import com.graphhopper.application.GraphHopperServerConfiguration; import com.graphhopper.application.util.GraphHopperServerTestConfiguration; import com.graphhopper.config.LMProfile; -import com.graphhopper.config.Profile; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.util.Helper; import io.dropwizard.testing.junit5.DropwizardAppExtension; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; @@ -48,13 +48,12 @@ public class RouteResourceIssue2020Test { private static GraphHopperServerConfiguration createConfig() { GraphHopperServerConfiguration config = new GraphHopperServerTestConfiguration(); config.getGraphHopperConfiguration(). - putObject("graph.vehicles", "car"). putObject("prepare.lm.split_area_location", "../core/files/split.geo.json"). putObject("datareader.file", "../core/files/north-bayreuth.osm.gz"). - putObject("graph.encoded_values", "road_class,surface,road_environment,max_speed"). + putObject("graph.encoded_values", "road_class,surface,road_environment,max_speed,car_access,car_average_speed"). putObject("import.osm.ignored_highways", ""). putObject("graph.location", DIR). - setProfiles(Collections.singletonList(new Profile("my_car").setVehicle("car"))). + setProfiles(Collections.singletonList(TestProfiles.accessAndSpeed("my_car", "car"))). setLMProfiles(Collections.singletonList(new LMProfile("my_car"))); return config; } diff --git a/web/src/test/java/com/graphhopper/application/resources/RouteResourceLeipzigTest.java b/web/src/test/java/com/graphhopper/application/resources/RouteResourceLeipzigTest.java index eef496b0b2c..9904ed12145 100644 --- a/web/src/test/java/com/graphhopper/application/resources/RouteResourceLeipzigTest.java +++ b/web/src/test/java/com/graphhopper/application/resources/RouteResourceLeipzigTest.java @@ -23,8 +23,7 @@ import com.graphhopper.application.GraphHopperServerConfiguration; import com.graphhopper.application.util.GraphHopperServerTestConfiguration; import com.graphhopper.config.CHProfile; -import com.graphhopper.config.Profile; -import com.graphhopper.util.CustomModel; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.util.Helper; import io.dropwizard.testing.junit5.DropwizardAppExtension; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; @@ -38,11 +37,10 @@ import javax.ws.rs.core.Response; import java.io.File; import java.util.Collections; +import java.util.List; import java.util.Random; import static com.graphhopper.application.util.TestUtils.clientTarget; -import static com.graphhopper.json.Statement.If; -import static com.graphhopper.json.Statement.Op.MULTIPLY; import static com.graphhopper.util.Parameters.Algorithms.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -58,11 +56,10 @@ private static GraphHopperServerConfiguration createConfig() { putObject("prepare.min_network_size", 200). putObject("datareader.file", "../map-matching/files/leipzig_germany.osm.pbf"). putObject("import.osm.ignored_highways", ""). - putObject("graph.location", DIR) - .setProfiles(Collections.singletonList(new Profile("my_car"). - setCustomModel(new CustomModel(). - addToPriority(If("road_access == DESTINATION", MULTIPLY, "0.1"))).setVehicle("car"))) - .setCHProfiles(Collections.singletonList(new CHProfile("my_car"))); + putObject("graph.location", DIR). + putObject("graph.encoded_values", "car_access, car_average_speed"). + setProfiles(List.of(TestProfiles.accessAndSpeed("my_car", "car"))). + setCHProfiles(Collections.singletonList(new CHProfile("my_car"))); return config; } @@ -82,12 +79,12 @@ void testNoErrors() { @ParameterizedTest @CsvSource(value = { - "92,-1,algorithm=" + DIJKSTRA_BI, - "92,-1,algorithm=" + ASTAR_BI, - "30719,1,ch.disable=true&algorithm=" + DIJKSTRA, - "21011,1,ch.disable=true&algorithm=" + ASTAR, - "14720,1,ch.disable=true&algorithm=" + DIJKSTRA_BI, - "10392,1,ch.disable=true&algorithm=" + ASTAR_BI + "103,-1,algorithm=" + DIJKSTRA_BI, + "128,-1,algorithm=" + ASTAR_BI, + "30867,1,ch.disable=true&algorithm=" + DIJKSTRA, + "21181,1,ch.disable=true&algorithm=" + ASTAR, + "14854,1,ch.disable=true&algorithm=" + DIJKSTRA_BI, + "10538,1,ch.disable=true&algorithm=" + ASTAR_BI }) void testTimeout(int expectedVisitedNodes, int timeout, String args) { { diff --git a/web/src/test/java/com/graphhopper/application/resources/RouteResourceProfileSelectionTest.java b/web/src/test/java/com/graphhopper/application/resources/RouteResourceProfileSelectionTest.java index 46773311928..c6a190fa98b 100644 --- a/web/src/test/java/com/graphhopper/application/resources/RouteResourceProfileSelectionTest.java +++ b/web/src/test/java/com/graphhopper/application/resources/RouteResourceProfileSelectionTest.java @@ -24,8 +24,7 @@ import com.graphhopper.application.util.GraphHopperServerTestConfiguration; import com.graphhopper.config.CHProfile; import com.graphhopper.config.LMProfile; -import com.graphhopper.config.Profile; -import com.graphhopper.util.CustomModel; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.util.Helper; import io.dropwizard.testing.junit5.DropwizardAppExtension; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; @@ -51,23 +50,23 @@ public class RouteResourceProfileSelectionTest { private static GraphHopperServerConfiguration createConfig() { GraphHopperServerConfiguration config = new GraphHopperServerTestConfiguration(); config.getGraphHopperConfiguration(). - putObject("graph.vehicles", "bike,car,foot"). putObject("prepare.min_network_size", 0). putObject("datareader.file", "../core/files/monaco.osm.gz"). putObject("graph.encoded_values", "road_class,surface,road_environment,max_speed"). putObject("import.osm.ignored_highways", ""). - putObject("graph.location", DIR) - .setProfiles(Arrays.asList( - new Profile("my_car").setVehicle("car"), - new Profile("my_bike").setCustomModel(new CustomModel().setDistanceInfluence(200d)).setVehicle("bike"), - new Profile("my_feet").setVehicle("foot") - )) - .setCHProfiles(Arrays.asList( + putObject("graph.location", DIR). + putObject("graph.encoded_values", "road_class, surface, road_environment, max_speed, car_access, car_average_speed, bike_access, bike_priority, bike_average_speed, foot_access, foot_priority, foot_average_speed"). + setProfiles(Arrays.asList( + TestProfiles.accessAndSpeed("my_car", "car"), + TestProfiles.accessSpeedAndPriority("my_bike", "bike"), + TestProfiles.accessSpeedAndPriority("my_feet", "foot") + )). + setCHProfiles(Arrays.asList( new CHProfile("my_car"), new CHProfile("my_bike"), new CHProfile("my_feet") - )) - .setLMProfiles(Arrays.asList( + )). + setLMProfiles(Arrays.asList( new LMProfile("my_car"), new LMProfile("my_bike"), new LMProfile("my_feet") @@ -85,7 +84,7 @@ public static void cleanUp() { @ValueSource(strings = {"CH", "LM", "flex"}) public void selectUsingProfile(String mode) { assertDistance("my_car", mode, 3563); - assertDistance("my_bike", mode, 3296); + assertDistance("my_bike", mode, 3700); assertDistance("my_feet", mode, 3158); assertError("my_pink_car", mode, "The requested profile 'my_pink_car' does not exist"); } diff --git a/web/src/test/java/com/graphhopper/application/resources/RouteResourceTest.java b/web/src/test/java/com/graphhopper/application/resources/RouteResourceTest.java index 2bd1fa1f739..9019245ae2c 100644 --- a/web/src/test/java/com/graphhopper/application/resources/RouteResourceTest.java +++ b/web/src/test/java/com/graphhopper/application/resources/RouteResourceTest.java @@ -26,7 +26,7 @@ import com.graphhopper.application.GraphHopperServerConfiguration; import com.graphhopper.application.util.GraphHopperServerTestConfiguration; import com.graphhopper.config.CHProfile; -import com.graphhopper.config.Profile; +import com.graphhopper.routing.TestProfiles; import com.graphhopper.routing.ev.RoadClass; import com.graphhopper.routing.ev.RoadClassLink; import com.graphhopper.routing.ev.RoadEnvironment; @@ -80,7 +80,6 @@ private static GraphHopperServerConfiguration createConfig() { GraphHopperServerConfiguration config = new GraphHopperServerTestConfiguration(); config.getGraphHopperConfiguration(). putObject("profiles_mapbox", mapboxResolver). - putObject("graph.vehicles", "car"). putObject("prepare.min_network_size", 0). putObject("datareader.file", "../core/files/andorra.osm.pbf"). putObject("graph.encoded_values", "road_class,surface,road_environment,max_speed,country"). @@ -88,11 +87,12 @@ private static GraphHopperServerConfiguration createConfig() { putObject("graph.urban_density.threads", 1). // for max_speed_calculator putObject("graph.urban_density.city_radius", 0). putObject("import.osm.ignored_highways", ""). - putObject("graph.location", DIR) + putObject("graph.location", DIR). // adding this so the corresponding check is not just skipped... - .putObject(MAX_NON_CH_POINT_DISTANCE, 10e6) - .setProfiles(Collections.singletonList(new Profile("my_car").setVehicle("car"))) - .setCHProfiles(Collections.singletonList(new CHProfile("my_car"))); + putObject(MAX_NON_CH_POINT_DISTANCE, 10e6). + putObject("graph.encoded_values", "road_class, surface, road_environment, max_speed, country, car_access, car_average_speed"). + setProfiles(Collections.singletonList(TestProfiles.accessAndSpeed("my_car", "car"))). + setCHProfiles(Collections.singletonList(new CHProfile("my_car"))); return config; } @@ -110,6 +110,7 @@ public void testBasicQuery() { JsonNode json = response.readEntity(JsonNode.class); JsonNode infoJson = json.get("info"); assertFalse(infoJson.has("errors")); + assertEquals("GraphHopper", infoJson.at("/copyrights/0").asText()); JsonNode path = json.get("paths").get(0); double distance = path.get("distance").asDouble(); assertTrue(distance > 9000, "distance wasn't correct:" + distance); @@ -295,7 +296,7 @@ public void testPathDetails() { List averageSpeedList = pathDetails.get("average_speed"); assertEquals(13, averageSpeedList.size()); assertEquals(30.0, averageSpeedList.get(0).getValue()); - assertEquals(14, averageSpeedList.get(0).getLength()); + assertEquals(15, averageSpeedList.get(0).getLength()); assertEquals(60.0, averageSpeedList.get(1).getValue()); assertEquals(5, averageSpeedList.get(1).getLength()); @@ -304,7 +305,7 @@ public void testPathDetails() { assertEquals(924L, edgeIdDetails.get(0).getValue()); assertEquals(2, edgeIdDetails.get(0).getLength()); assertEquals(925L, edgeIdDetails.get(1).getValue()); - assertEquals(8, edgeIdDetails.get(1).getLength()); + assertEquals(9, edgeIdDetails.get(1).getLength()); long expectedTime = rsp.getBest().getTime(); long actualTime = 0; @@ -351,9 +352,9 @@ public void testPathDetailsWithoutGraphHopperWeb() { assertTrue(details.has("average_speed")); JsonNode averageSpeed = details.get("average_speed"); assertEquals(30.0, averageSpeed.get(0).get(2).asDouble(), .1); - assertEquals(14, averageSpeed.get(0).get(1).asInt(), .1); + assertEquals(15, averageSpeed.get(0).get(1).asInt(), .1); assertEquals(60.0, averageSpeed.get(1).get(2).asDouble(), .1); - assertEquals(19, averageSpeed.get(1).get(1).asInt()); + assertEquals(20, averageSpeed.get(1).get(1).asInt()); assertTrue(details.has("edge_id")); JsonNode edgeIds = details.get("edge_id"); int firstLink = edgeIds.get(0).get(2).asInt(); @@ -362,24 +363,24 @@ public void testPathDetailsWithoutGraphHopperWeb() { assertEquals(1584, lastLink); JsonNode maxSpeed = details.get("max_speed"); - assertEquals("[0,33,50.0]", maxSpeed.get(0).toString()); - assertEquals("[33,34,60.0]", maxSpeed.get(1).toString()); - assertEquals("[34,38,50.0]", maxSpeed.get(2).toString()); - assertEquals("[38,50,90.0]", maxSpeed.get(3).toString()); - assertEquals("[50,52,50.0]", maxSpeed.get(4).toString()); - assertEquals("[52,60,90.0]", maxSpeed.get(5).toString()); + assertEquals("[0,34,50.0]", maxSpeed.get(0).toString()); + assertEquals("[34,35,60.0]", maxSpeed.get(1).toString()); + assertEquals("[35,39,50.0]", maxSpeed.get(2).toString()); + assertEquals("[39,53,90.0]", maxSpeed.get(3).toString()); + assertEquals("[53,55,50.0]", maxSpeed.get(4).toString()); + assertEquals("[55,65,90.0]", maxSpeed.get(5).toString()); JsonNode urbanDensityNode = details.get("urban_density"); - assertEquals("[0,63,\"residential\"]", urbanDensityNode.get(0).toString()); - assertEquals("[63,68,\"rural\"]", urbanDensityNode.get(1).toString()); - assertEquals("[68,71,\"residential\"]", urbanDensityNode.get(2).toString()); - assertEquals("[71,75,\"rural\"]", urbanDensityNode.get(3).toString()); - assertEquals("[75,106,\"residential\"]", urbanDensityNode.get(4).toString()); - assertEquals("[106,128,\"rural\"]", urbanDensityNode.get(5).toString()); - assertEquals("[128,166,\"residential\"]", urbanDensityNode.get(6).toString()); - assertEquals("[166,171,\"rural\"]", urbanDensityNode.get(7).toString()); - assertEquals("[171,183,\"residential\"]", urbanDensityNode.get(8).toString()); - assertEquals("[183,213,\"rural\"]", urbanDensityNode.get(9).toString()); + assertEquals("[0,68,\"residential\"]", urbanDensityNode.get(0).toString()); + assertEquals("[68,73,\"rural\"]", urbanDensityNode.get(1).toString()); + assertEquals("[73,76,\"residential\"]", urbanDensityNode.get(2).toString()); + assertEquals("[76,80,\"rural\"]", urbanDensityNode.get(3).toString()); + assertEquals("[80,115,\"residential\"]", urbanDensityNode.get(4).toString()); + assertEquals("[115,141,\"rural\"]", urbanDensityNode.get(5).toString()); + assertEquals("[141,181,\"residential\"]", urbanDensityNode.get(6).toString()); + assertEquals("[181,186,\"rural\"]", urbanDensityNode.get(7).toString()); + assertEquals("[186,199,\"residential\"]", urbanDensityNode.get(8).toString()); + assertEquals("[199,233,\"rural\"]", urbanDensityNode.get(9).toString()); } @Test @@ -531,8 +532,8 @@ public void testGPX() { assertEquals(200, response.getStatus()); String str = response.readEntity(String.class); // For backward compatibility we currently export route and track. - assertTrue(str.contains("1841.5"), str); - assertFalse(str.contains(" Finish!")); + assertTrue(str.contains("1841.7"), str); + assertFalse(str.contains("Finish!")); assertTrue(str.contains("