diff --git a/README.md b/README.md index d6532cf42e..057b453b87 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ If you want to do contribute your improvements, please follow these steps: ## Installation -We suggest using docker to install and launch OpenRouteService backend. In short, run the following command under the source code tree will get everything done. +We suggest using docker to install and launch openrouteservice backend. In short, run the following command under the source code tree will get everything done. ```bash cd docker && docker-compose up @@ -46,8 +46,18 @@ For more details, check the [docker installation guide](docker/README.md). ## Usage -Instructions on how to use the endpoints of the running tomcat container will follow soon. +Openrouteservice offers a set of endpoints for different spatial purposes. They are served with the help of [Tomcat in a java servlet container](https://github.com/GIScience/openrouteservice/blob/master/openrouteservice/WebContent/WEB-INF/web.xml). By default you will be able to query the services with these addresses: +- `http://localhost:8080/name_of_war_archive/routes` +- `http://localhost:8080/name_of_war_archive/isochrones` +- `http://localhost:8080/name_of_war_archive/matrix` + +Both `/locations` and `/geocoding` need additional setup steps for usage. +- `http://localhost:8080/name_of_war_archive/locations` | You will have to set up a locations database, for this please refer to [openrouteservice-tools](https://github.com/GIScience/openrouteservice-tools). +- `http://localhost:8080/name_of_war_archive/geocoding` | You can either use [Photon](https://github.com/komoot/photon), [Nominatim](https://github.com/openstreetmap/Nominatim) or [Pelias](https://github.com/pelias/pelias). One of these services must be installed in addition to the openrouteservice and configured in `app.config`. + +Please find a detailed description of the api architecture on https://app.swaggerhub.com/apis/OpenRouteService/ors-api/. + ## API Documentation For an easy and interactive way to test the api, visit our [documentation](https://app.swaggerhub.com/apis/openrouteservice/ors-api/) at swaggerhub. After obtaining your key you can try out the different endpoints instantly and start firing requests. diff --git a/docker/README.md b/docker/README.md index 8644d5101b..a8742a16ef 100644 --- a/docker/README.md +++ b/docker/README.md @@ -1,22 +1,22 @@ -# Install and Run OpenRouteService Backend via Docker +# Install and run openrouteservice with docker -It's possible and easy to install and launch the OpenRouteService backend service with Docker. Please note that the [Dockerfile](../Dockerfile) under the repository root directory is only for building the [WAR file](https://www.wikiwand.com/en/WAR_(file_format)). +Installing the openrouteservice backend service with **Docker** is quite straightforward. Please note that the [Dockerfile](../Dockerfile) located in the repository root directory is merely for building the [WAR file](https://www.wikiwand.com/en/WAR_(file_format)). ## Short version -run the following command within this `docker/` directory: +Run the following command within this `docker/` directory: ```bash -docker-compose up +sudo docker-compose up -d ``` -It will: +This will: -1. build and test the OpenRouteService core from the local codebase with the `docker/conf/app.config.sample` as the config and the dataset for Heidelberg under `docker/data/` as sample data; -2. generate the built `ors.war` file and expose it to `docker/build/` directory; -3. launch the OpenRouteService backend service on port `8080`. +1. Build and test the openrouteservice core from the local codebase with the `docker/conf/app.config.sample` as the config file and the OpenStreetMap dataset for Heidelberg under `docker/data/` as sample data. +2. Generate the built `ors.war` file and expose it to `docker/build/` directory. +3. Launch the openrouteservice service on port `8080` within a tomcat container. -The service status is queryable via `http://localhost:8080/ors/health` endpoint. When the service is ready, go to `http://localhost:8080/ors/status` and it will show more detailed information. A URL for test can be `http://localhost:8080/ors/routes?profile=foot-walking&coordinates=8.676581,49.418204|8.692803,49.409465`. It should be able to provide the recommanded walking path in JSON format. +By default the service status is queryable via the `http://localhost:8080/ors/health` endpoint. When the service is ready, you will be able to request `http://localhost:8080/ors/status` for further information on the running services. If you use the default dataset you will be able to request `http://localhost:8080/ors/routes?profile=foot-walking&coordinates=8.676581,49.418204|8.692803,49.409465` for test purposes. ## Long version @@ -34,20 +34,20 @@ or docker-compose up ors-build ``` -If everything goes fine, the built `ors.war` file can be found under the host directory, e.g. `/Users/user/build` in the above `docker run` command or `./build/` in the `docker-compose` command. +If everything goes fine, the built `ors.war` archive can be found under the shared host directory, e.g. `/Users/user/build` for the above `docker run` command or `./build/` for the `docker-compose` command. -### Run OpenRouteService +### Run openrouteservice -No matter whether the WAR file has been built or not, run +No matter whether the WAR file has been built or not, simply run: ```bash -docker-compose up +sudo docker-compose up ``` -will get everything done with the sample Heidelberg dataset. +will take care of all steps with the sample Heidelberg dataset. -### Run with your own OSM dataset +### Run with your own OpenStreetMap dataset -Prepare the OSM dataset (formats support `.osm`, `.osm.gz`, `.osm.zip`, `.pbf`) in the `docker/data/` directory. Make your own `app.config` (check the sample with detailed comments [here](../openrouteservice/WebContent/WEB-INF/app.config.sample) for reference) and change the `APP_CONFIG` variable in `docker-compose.yml` to let it point to your customized `app.config`. Then, run `docker-compose up`. +Prepare the OSM dataset (formats supported are `.osm`, `.osm.gz`, `.osm.zip` and `.pbf`) in the `docker/data/` directory. Adapt your own `app.config` (check the sample with detailed comments [here](../openrouteservice/WebContent/WEB-INF/app.config.sample) for reference) and change the `APP_CONFIG` variable in `docker-compose.yml` to let it point to your customized `app.config`. Then, run `docker-compose up`. -It should be noticed that if your dataset is very big, please adjust the `-Xmx` parameter of `JAVA_OPTS` in `docker-compose.yml`. According to our experiences, it should be at least `180g` for the whole globe. \ No newline at end of file +It should be mentioned that if your dataset is very large, please adjust the `-Xmx` parameter of `JAVA_OPTS` in `docker-compose.yml`. According to our experience, this should be at least `180g` for the whole globe if you are planning to use 3 or more modes of transport. diff --git a/openrouteservice-api-tests/src/test/java/heigit/ors/services/locations/ResultsValidationTest.java b/openrouteservice-api-tests/src/test/java/heigit/ors/services/locations/ResultsValidationTest.java index 7451469953..5e590ab713 100644 --- a/openrouteservice-api-tests/src/test/java/heigit/ors/services/locations/ResultsValidationTest.java +++ b/openrouteservice-api-tests/src/test/java/heigit/ors/services/locations/ResultsValidationTest.java @@ -73,7 +73,7 @@ public void bboxSearchFilterTest() { Assert.assertEquals(jResponse.getJSONArray("features").length(), 8); } - @Test + /**@Test public void pointSearchWithoutNameFilterTest() { Response response = given() .param("request", "pois") @@ -95,9 +95,9 @@ public void pointSearchWithoutNameFilterTest() { Assert.assertEquals(true, jBbox.getDouble(1) == 49.377332); Assert.assertEquals(true, jBbox.getDouble(2) == 8.710422); Assert.assertEquals(true, jBbox.getDouble(3) == 49.454217); - } + }*/ - @Test + /**@Test public void pointSearchWithNameFilterTest() { Response response = given() .param("request", "pois") @@ -113,9 +113,9 @@ public void pointSearchWithNameFilterTest() { JSONObject jResponse = new JSONObject(response.body().asString()); Assert.assertEquals(jResponse.getJSONArray("features").length(), 9); - } + }*/ - @Test + /**@Test public void linestringSearchWithNameFilterTest() { Response response = given() .param("request", "pois") @@ -130,7 +130,7 @@ public void linestringSearchWithNameFilterTest() { Assert.assertEquals(response.getStatusCode(), 200); JSONObject jResponse = new JSONObject(response.body().asString()); Assert.assertEquals(jResponse.getJSONArray("features").length(), 2); - } + }*/ @Test public void detailsTest() { diff --git a/openrouteservice-api-tests/src/test/java/heigit/ors/services/routing/ResultTest.java b/openrouteservice-api-tests/src/test/java/heigit/ors/services/routing/ResultTest.java index 44b122c4e6..d31b46c602 100644 --- a/openrouteservice-api-tests/src/test/java/heigit/ors/services/routing/ResultTest.java +++ b/openrouteservice-api-tests/src/test/java/heigit/ors/services/routing/ResultTest.java @@ -103,7 +103,7 @@ public void testSummary() { .body("routes[0].containsKey('segments')", is(true)) .body("routes[0].segments.size()", is(2)) .body("routes[0].summary.distance", is(14132.5f)) - .body("routes[0].summary.duration", is(3815.6f)) + .body("routes[0].summary.duration", is(4115.6f)) .body("routes[0].summary.ascent", is(349.4f)) .body("routes[0].summary.descent", is(340)) .statusCode(200); @@ -127,7 +127,7 @@ public void testSegmentDistances() { .body("routes[0].segments[0].distance", is(7199.4f)) .body("routes[0].segments[0].duration", is(2597.4f)) .body("routes[0].segments[1].distance", is(6933.1f)) - .body("routes[0].segments[1].duration", is(1218.2f)) + .body("routes[0].segments[1].duration", is(1518.2f)) .statusCode(200); } @@ -408,6 +408,7 @@ public void testTollwaysExtraDetails() { .param("instructions", "true") .param("preference", "fastest") .param("profile", "driving-hgv") + .param("continue_straight", "false") .param("options", "{\"profile_params\":{\"width\":\"2\",\"height\":\"2\",\"weight\":\"14\"},\"vehicle_type\":\"hgv\"}") .param("extra_info", "suitability|tollways") .when() diff --git a/openrouteservice/WebContent/WEB-INF/app.config.sample b/openrouteservice/WebContent/WEB-INF/app.config.sample index 3ee877b2ae..032a3de2b1 100644 --- a/openrouteservice/WebContent/WEB-INF/app.config.sample +++ b/openrouteservice/WebContent/WEB-INF/app.config.sample @@ -1,66 +1,88 @@ { ors { - # The topmost element for parameterization of ORS end-points such as Routing, Geocoding, Isochrones, etc. + # The topmost element for parameterization of the ORS end-points such as Routing, Geocoding, Isochrones, etc. services: { - # An end-point used to shorten links. + # ******************************************************************************************************************** + # An end-point used to shorten links. + # ******************************************************************************************************************** shortenlink: { - # Enables or disables (true/false) the end-point. Default value is true. + # Enables or disables (true/false) the end-point. Default value is true. enabled: true, user_name: YOUR_USER, user_password: YOUR_PASSWORD, api_key: YOUR_API_KEY, } + # ******************************************************************************************************************** # Accessibility API end-point parameters # NOTE: not ready for production yet. + # ******************************************************************************************************************** accessibility: { - # Enables or disables (true/false) the end-point. Default value is true. - enabled: true, - maximum_range_distance: 120000, - maximum_range_time: 3600, - maximum_locations: 2, - route_details_allowed: true, - attribution: "openrouteservice.org, OpenStreetMap contributors" - } + # Enables or disables (true/false) the end-point. Default value is true. + enabled: true, + # Possible values for maximum_range_distance and maximum_range_time are an integer or a list of values specifically defined for each profile. + maximum_range_distance: + [ + { profiles: "any", value: 50000 } + { profiles: "driving-car, driving-hgv", value: 100000 } + ], + maximum_range_time: + [ + { profiles: "any", value: 18000 } + { profiles: "driving-car, driving-hgv", value: 3600 } + ], + maximum_locations: 2, + route_details_allowed: true, + attribution: "openrouteservice.org, OpenStreetMap contributors" + } + # ******************************************************************************************************************** # Matrix API end-point parameters - matrix: { - # Enables or disables (true/false) the end-point. Default value is true. - enabled: true, + # ******************************************************************************************************************** + matrix: { + # Enables or disables (true/false) the end-point. Default value is true. + enabled: true, # Maximum dimension of the result matrix. In other words, the maximum possible length of a row or a column in the matrix. # Default value is 100. - maximum_locations: 100, + maximum_locations: 100, # Maximum allowed distance between the requested coordinate and a point on the nearest road. The value is measured in meters. - maximum_search_radius: 5000, + maximum_search_radius: 5000, # Maximum allowed number of visited nodes in shortest path computation. This threshold is applied only for Dijkstra algorithm. - maximum_visited_nodes: 100000, + maximum_visited_nodes: 100000, # Specifies whether the name of a neares street to the location can be resolved or not. Default value is true. - allow_resolve_locations: true, - attribution: "openrouteservice.org, OpenStreetMap contributors" - } + allow_resolve_locations: true, + attribution: "openrouteservice.org, OpenStreetMap contributors" + } + # ******************************************************************************************************************** # Optimization API end-point parameters - # NOTE: not ready for production yet. - optimization: { - # Enables or disables (true/false) the end-point. Default value is true. - enabled: true, - maximum_locations: 200, - solver_name: "default", - solver_options: - { - } - attribution: "openrouteservice.org, OpenStreetMap contributors" - } + # NOTE: not implemented yet. + # ******************************************************************************************************************** + optimization: { + # Enables or disables (true/false) the end-point. Default value is true. + enabled: true, + maximum_locations: 200, + solver_name: "default", + solver_options: + { + }, + attribution: "openrouteservice.org, OpenStreetMap contributors" + } + # ******************************************************************************************************************** # MapMatching API end-point parameters # NOTE: not implemented yet. - mapmatching: { - # Enables or disables (true/false) the end-point. Default value is true. - enabled: true, - maximum_locations: 100, - maximum_search_radius: 200, - maximum_visited_nodes: 10000, - attribution: "openrouteservice.org, OpenStreetMap contributors" - } + # ******************************************************************************************************************** + mapmatching: { + # Enables or disables (true/false) the end-point. Default value is true. + enabled: true, + maximum_locations: 100, + maximum_search_radius: 200, + maximum_visited_nodes: 10000, + attribution: "openrouteservice.org, OpenStreetMap contributors" + } + # ******************************************************************************************************************** # Isochrones API end-point parameters + # ******************************************************************************************************************** isochrones: { - # Enables or disables (true/false) the end-point. + # Enables or disables (true/false) the end-point. Default value is true. + enabled: true, # Possible values for maximum_range_distance and maximum_range_time are an integer or a list of values specifically defined for each profile. maximum_range_distance: [ @@ -72,26 +94,41 @@ { profiles: "any", value: 18000 } { profiles: "driving-car, driving-hgv", value: 3600 } ], + # Maximum number of intervals/isochrones computed for each location. maximum_intervals: 10, + # Maximum number of locations in one request. maximum_locations: 2, + # Speficies whether area computation by setting "attributes=area" is allowed or not. allow_compute_area: true, attribution: "openrouteservice.org, OpenStreetMap contributors" } + # ******************************************************************************************************************** # Geocoding API end-point parameters + # ******************************************************************************************************************** geocoding: { # Enables or disables (true/false) the end-point. Default value is true. - geocoder_name: pelias, - geocoding_url: "http://YOUR_ADDRESS/v1/search", - reverse_geocoding_url: "http://YOUR_ADDRESS/v1/reverse", - response_limit: 20, - user_agent: OpenRouteService, - attribution: "openrouteservice.org, OpenStreetMap contributors" - } + enabled: true, + # The name of a geocoder. Possible values are nomimatim, pelias or photon. + geocoder_name: pelias, + # URL for forward geocoding. + geocoding_url: "http://YOUR_ADDRESS/v1/search", + # URL for reverse geocoding. + reverse_geocoding_url: "http://YOUR_ADDRESS/v1/reverse", + # The maximum allowed number of returned results. + response_limit: 20, + # Sets the value of the User-agent HTTP header sent to a backend. + user_agent: OpenRouteService, + attribution: "openrouteservice.org, OpenStreetMap contributors" + } + # ******************************************************************************************************************** # Locations API end-point parameters + # ******************************************************************************************************************** locations: { # Enables or disables (true/false) the end-point. Default value is true. enabled: true, + # Defines the provider name of the Locations API backend. Possible values are postgreql, memsql (not finished). provider_name: postgresql, + # Connection parameters of the provider. provider_parameters: { host: "YOUR_HOST", port: 5432, @@ -100,89 +137,106 @@ password : "YOUR_PASSWORD", table_name : "YOUR_TABLE" }, + # The maximum allowed number of returned results. response_limit: 1000, + # The maximum allowed number of categories specifed in a request. maximum_categories: 5, - # maximum allowed length of a linestring, measured in meters + # The maximum allowed length of a linestring, measured in meters. maximum_feature_length: 10000000, - # maximum allowed area of a polygon, measured in square meters + # The maximum allowed area of a polygon, measured in square meters maximum_feature_area: -1, - # maximum allowed search radius + # The maximum allowed search radius, measured in meters. maximum_search_radius_for_points: 50000, maximum_search_radius_for_linestrings: 2000, maximum_search_radius_for_polygons: 1000, attribution: "openrouteservice.org, OpenStreetMap contributors" } + # ******************************************************************************************************************** # Routing API end-point parameters + # ******************************************************************************************************************** routing: { # Enables or disables (true/false) the end-point. Default value is true. enabled: true, mode: "normal", + # The path to an OpenStreetMap data file. sources: ["openrouteservice/src/main/files/heidelberg.osm.gz"], + # The number of threads used to initialize (build/load) graphs. Higher numbers requires more RAM. init_threads: 2, attribution: "openrouteservice.org, OpenStreetMap contributors", + # Defines a set of routing profiles. profiles: { + # Defines a list of active routing profiles. The element name XXX must correspond to a notation "profile-XXX", which is used in the following sections. active: ["vehicles", "bike", "bike2", "pedestrian"], + # Set parameters that is applied to every profile by default. default_params: { + # The number of bytes used for FlagEncoders. encoder_flags_size: 8, + # The root path to a directory for storing graphs. graphs_root_path: "graphs", + # The name of an elevation provider. Possible values are cgiar and srtm. elevation_provider: cgiar, + # The path to a directory in which SRTM tiles will be stored. elevation_cache_path: "cgiar_provider", - instructions: true, - maximum_distance: 100000, - maximum_segment_distance_with_dynamic_weights: 50000, - maximum_waypoints: 50, - - preparation: - { - min_network_size: 200, - min_one_way_network_size: 200, + # Specifies whether way names will be stored during the import or not. + instructions: true, + # The maximum allowed total distance of a route. + maximum_distance: 100000, + # The maximum allowed distance between two way points when dynamic weights are used. + maximum_segment_distance_with_dynamic_weights: 50000, + # The maximum number of way points in a request. + maximum_waypoints: 50, + + # The parameters for the pre-processing stage. + preparation: { + min_network_size: 200, + min_one_way_network_size: 200, - methods: - { - lm: - { - enabled: true, - threads: 1, - weightings: "fastest|shortest", - landmarks: 16 - } + methods: { + # Landmarks + lm: { + # Enables or disables landmarks features. + enabled: true, + # The number of threads used to compute landmarks. + threads: 1, + # A pipe (|) separated list of weightings used for Landmarks. + weightings: "fastest|shortest", + # The number of landmarks. + landmarks: 16 } } - execution: - { - methods: - { - lm: - { - disabling_allowed: true, - active_landmarks: 8 - } + } + # The parameters for the execution stage. + execution: { + methods: { + lm: { + disabling_allowed: true, + active_landmarks: 8 } } + } }, + # ==================================================================================================================== + # Vehicle profiles + # ==================================================================================================================== profile-vehicles: { profiles: "driving-car,driving-hgv,driving-motorcycle", parameters: { - encoder_flags_size: 8, - encoder_options : "turn_costs=true|block_fords=false,turn_costs=true|block_fords=false,turn_costs=true|block_fords=false", - maximum_distance: 100000, - elevation: true, - # parameters for the pre-processing stage - preparation: - { - min_network_size: 200, - min_one_way_network_size: 200, - - methods: - { - ch: - { + encoder_flags_size: 8, + # List of options used by FlagEncoders. + encoder_options : "turn_costs=true|block_fords=false,turn_costs=true|block_fords=false,turn_costs=true|block_fords=false", + maximum_distance: 100000, + elevation: true, + preparation: { + min_network_size: 200, + min_one_way_network_size: 200, + + methods: { + ch: { enabled: true, threads: 1, weightings: "fastest" }, - lm: - { + lm: { enabled: true, threads: 1, weightings: "fastest|shortest", @@ -190,79 +244,84 @@ } } } - # parameters for pre-processing the execution stage - execution: - { - methods: - { - # Contraction hierachies - ch: - { + execution: { + methods: { + # Contraction hierachies + ch: { disabling_allowed: true }, # Landmarks - lm: - { + lm: { disabling_allowed: true, active_landmarks: 8 } } } - ext_storages: { - WayCategory: { }, - HeavyVehicle: { + # The list of custom storages that store additional attribtues for graph edges. + ext_storages: { + WayCategory: { }, + HeavyVehicle: { restrictions: true - }, + }, WaySurfaceType: { } - } + } # Traffic feature is not ready for production. - traffic: false - } + traffic: false + } } + # ==================================================================================================================== + # Cycling profiles + # ==================================================================================================================== profile-bike: { profiles: "cycling-regular, cycling-mountain, cycling-road", parameters: { - # extent: [5.866240, 15.042050, 47.270210, 55.058140], # - encoder_options :"consider_elevation=true|turn_costs=true|block_fords=false,consider_elevation=true|turn_costs=true|block_fords=false,consider_elevation=true|turn_costs=true|block_fords=false", - elevation: true, - ext_storages: { - WayCategory: { }, + # extent: [5.866240, 15.042050, 47.270210, 55.058140], # + encoder_options :"consider_elevation=true|turn_costs=true|block_fords=false,consider_elevation=true|turn_costs=true|block_fords=false,consider_elevation=true|turn_costs=true|block_fords=false", + elevation: true, + ext_storages: { + WayCategory: { }, WaySurfaceType: { }, HillIndex: { }, TrailDifficulty: { } - } + } } } + # ==================================================================================================================== + # Cycling profiles + # ==================================================================================================================== profile-bike2: { profiles: "cycling-safe,cycling-tour, cycling-electric", parameters: { - encoder_options: "consider_elevation=true|turn_costs=true|block_fords=false,consider_elevation=true|turn_costs=true|block_fords=false,consider_elevation=true|turn_costs=true|block_fords=false", - elevation: true, - ext_storages: { - WayCategory: { }, + encoder_options: "consider_elevation=true|turn_costs=true|block_fords=false,consider_elevation=true|turn_costs=true|block_fords=false,consider_elevation=true|turn_costs=true|block_fords=false", + elevation: true, + ext_storages: { + WayCategory: { }, WaySurfaceType: { }, HillIndex: { }, TrailDifficulty: { } - } + } } } + # ==================================================================================================================== + # Walking profiles + # ==================================================================================================================== profile-pedestrian: { profiles: "foot-walking,foot-hiking", parameters: { - encoder_options: "block_fords=false,block_fords=false", - elevation: true, - ext_storages: { - #GreenIndex: { - # filepath: "PATH/TO/THE_GREEN_INDEX_CSV_FILE" - #}, - #NoiseIndex: { - # filepath: "PATH/TO/THE_NOISE_INDEX_CSV_FILE" - #}, - WayCategory: { }, + encoder_options: "block_fords=false,block_fords=false", + elevation: true, + ext_storages: { + #GreenIndex: { + # filepath: "PATH/TO/THE_GREEN_INDEX_CSV_FILE" + #}, + #NoiseIndex: { + # filepath: "PATH/TO/THE_NOISE_INDEX_CSV_FILE" + #}, + WayCategory: { }, WaySurfaceType: { }, HillIndex: { }, TrailDifficulty: { } - } + } } } } @@ -282,10 +341,15 @@ } } } + # Logging configuration logging: { + # Enables or disables logging. Default value is true. enabled: true, + # The path to a file containing logging parameters. level_file: "DEBUG_LOGGING.properties", + # The path to a directory to which logs will be written. location: "./logs", + # Enables/disables writing logs to STDOUT. stdout: true } } diff --git a/openrouteservice/docs/services/isochrones/eurostat/age_groups_mappings.txt b/openrouteservice/docs/services/isochrones/eurostat/age_groups_mappings.txt new file mode 100644 index 0000000000..3790aca6ed --- /dev/null +++ b/openrouteservice/docs/services/isochrones/eurostat/age_groups_mappings.txt @@ -0,0 +1,63 @@ +"t_a-total":"pop_total", +"t_a-lt5":"pop_lt5", +"t_a-5_9":"pop_5-9", +"t_a-10_14":"pop_10-14", +"t_a-15_19":"pop_15-19", +"t_a-20_24":"pop_20-24", +"t_a-25_29":"pop_25-29", +"t_a-30_34":"pop_30-34", +"t_a-35_39":"pop_35-39", +"t_a-40_44":"pop_40-44", +"t_a-45_49":"pop_45-49", +"t_a-50_54":"pop_50-54", +"t_a-55_59":"pop_55-59", +"t_a-60_64":"pop_60-64", +"t_a-65_69":"pop_65-69", +"t_a-70_74":"pop_70-74", +"t_a-75_79":"pop_75-79", +"t_a-80_84":"pop_80-84", +"t_a-85_89":"pop_85-89", +"t_a-ge85":"pop_ge85", +"t_a-ge90":"pop_ge90", +"m_a-total":"pop_male_total", +"m_a-lt5":"pop_male_lt5", +"m_a-5_9":"pop_male_5-9", +"m_a-10_14":"pop_male_10-14", +"m_a-15_19":"pop_male_15-19", +"m_a-20_24":"pop_male_20-24", +"m_a-25_29":"pop_male_25-29", +"m_a-30_34":"pop_male_30-34", +"m_a-35_39":"pop_male_35-39", +"m_a-40_44":"pop_male_40-44", +"m_a-45_49":"pop_male_45-49", +"m_a-50_54":"pop_male_50-54", +"m_a-55_59":"pop_male_55-59", +"m_a-60_64":"pop_male_60-64", +"m_a-65_69":"pop_male_65-69", +"m_a-70_74":"pop_male_70-74", +"m_a-75_79":"pop_male_75-79", +"m_a-80_84":"pop_male_80-84", +"m_a-85_89":"pop_male_85-89", +"m_a-ge85":"pop_male_ge85", +"m_a-ge90":"pop_male_ge90", +"f_a-total":"pop_female_total", +"f_a-lt5":"pop_female_lt5", +"f_a-5_9":"pop_female_5-9", +"f_a-10_14":"pop_female_10-14", +"f_a-15_19":"pop_female_15-19", +"f_a-20_24":"pop_female_20-24", +"f_a-25_29":"pop_female_25-29", +"f_a-30_34":"pop_female_30-34", +"f_a-35_39":"pop_female_35-39", +"f_a-40_44":"pop_female_40-44", +"f_a-45_49":"pop_female_45-49", +"f_a-50_54":"pop_female_50-54", +"f_a-55_59":"pop_female_55-59", +"f_a-60_64":"pop_female_60-64", +"f_a-65_69":"pop_female_65-69", +"f_a-70_74":"pop_female_70-74", +"f_a-75_79":"pop_female_75-79", +"f_a-80_84":"pop_female_80-84", +"f_a-85_89":"pop_female_85-89", +"f_a-ge85":"pop_female_ge85", +"f_a-ge90":"pop_female_ge90" diff --git a/openrouteservice/docs/services/isochrones/eurostat/example_query.sql b/openrouteservice/docs/services/isochrones/eurostat/example_query.sql new file mode 100644 index 0000000000..bb6bd1eac1 --- /dev/null +++ b/openrouteservice/docs/services/isochrones/eurostat/example_query.sql @@ -0,0 +1,12 @@ +SELECT Round(SUM(c.ratio * c.%%column_name%%)) +FROM ( + SELECT St_area(St_intersection(a.geog,poly)) / St_area(a.geog) ratio, + a.* + FROM geostat_grd_2016_ageclasses_nuts a, + %%wkb_geom%% + WHERE a.gid IN + ( + SELECT a.gid + FROM geostat_grd_2016_ageclasses_nuts a, + %%wkb_geom%% + WHERE st_intersects (a.geog, poly))) AS c; diff --git a/openrouteservice/pom.xml b/openrouteservice/pom.xml index b89822df0c..179ec9f5c6 100644 --- a/openrouteservice/pom.xml +++ b/openrouteservice/pom.xml @@ -5,14 +5,14 @@ 4.0.0 heigit.ors openrouteservice - 4.2.2 + 4.3.0 war OpenRouteService openrouteservice.org 2017 - GitLab - https://gitlab.gistools.geog.uni-heidelberg.de/giscience/openrouteservice/core/issues + GitHub + https://github.com/GIScience/openrouteservice/issues UTF-8 @@ -178,12 +178,12 @@ 3.0.3 - + com.github.GIScience graphhopper - v0.9.2 + v0.9.3 - + com.typesafe config diff --git a/openrouteservice/src/main/java/heigit/ors/routing/RouteResultBuilder.java b/openrouteservice/src/main/java/heigit/ors/routing/RouteResultBuilder.java index 8218ad60e8..b475efc82e 100644 --- a/openrouteservice/src/main/java/heigit/ors/routing/RouteResultBuilder.java +++ b/openrouteservice/src/main/java/heigit/ors/routing/RouteResultBuilder.java @@ -196,6 +196,10 @@ public RouteResult createRouteResult(List routes, RoutingRequest req RoundaboutInstruction raInstr = (RoundaboutInstruction)instr; step.setExitNumber(raInstr.getExitNumber()); instrText = instrTranslator.getRoundabout(raInstr.getExitNumber(), roadName); + if (raInstr.getRoundaboutExitBearings() != null) + { + step.setRoundaboutExitBearings(raInstr.getRoundaboutExitBearings()); + } } else { diff --git a/openrouteservice/src/main/java/heigit/ors/routing/RouteStep.java b/openrouteservice/src/main/java/heigit/ors/routing/RouteStep.java index abf85a0948..e98cb12168 100644 --- a/openrouteservice/src/main/java/heigit/ors/routing/RouteStep.java +++ b/openrouteservice/src/main/java/heigit/ors/routing/RouteStep.java @@ -32,6 +32,7 @@ public class RouteStep private int _type; private int[] _wayPoints; private RouteStepManeuver _maneuver; + private int[] _roundaboutExitBearings; public double getDuration() { return _duration; @@ -112,4 +113,12 @@ public RouteStepManeuver getManeuver() { public void setManeuver(RouteStepManeuver maneuver) { _maneuver = maneuver; } + + public int[] getRoundaboutExitBearings() { + return _roundaboutExitBearings; + } + + public void setRoundaboutExitBearings(int[] roundaboutExitBearings) { + _roundaboutExitBearings = roundaboutExitBearings; + } } diff --git a/openrouteservice/src/main/java/heigit/ors/routing/RoutingProfile.java b/openrouteservice/src/main/java/heigit/ors/routing/RoutingProfile.java index f79bb88517..7542436b78 100644 --- a/openrouteservice/src/main/java/heigit/ors/routing/RoutingProfile.java +++ b/openrouteservice/src/main/java/heigit/ors/routing/RoutingProfile.java @@ -84,7 +84,6 @@ import com.graphhopper.reader.dem.ElevationProvider; import com.graphhopper.routing.util.DefaultEdgeFilter; import com.graphhopper.routing.util.EdgeFilter; -import com.graphhopper.routing.util.EdgeFilterSequence; import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.routing.util.FlagEncoder; import com.graphhopper.routing.util.HintsMap; @@ -767,7 +766,7 @@ public boolean canProcessRequest(double totalDistance, double longestSegmentDist return totalDistance <= maxDistance && wayPoints <= maxWayPoints; } - public GHResponse computeRoute(double lat0, double lon0, double lat1, double lon1, boolean directedSegment, RouteSearchParameters searchParams, EdgeFilter customEdgeFilter, boolean simplifyGeometry, RouteProcessContext routeProcCntx) + public GHResponse computeRoute(double lat0, double lon0, double lat1, double lon1, double heading, boolean directedSegment, RouteSearchParameters searchParams, EdgeFilter customEdgeFilter, boolean simplifyGeometry, RouteProcessContext routeProcCntx) throws Exception { GHResponse resp = null; @@ -782,12 +781,17 @@ public GHResponse computeRoute(double lat0, double lon0, double lat1, double lon RouteSearchContext searchCntx = createSearchContext(searchParams, RouteSearchMode.Routing, customEdgeFilter); boolean flexibleMode = searchParams.getFlexibleMode(); - GHRequest req = new GHRequest(new GHPoint(lat0, lon0), new GHPoint(lat1, lon1)); + GHRequest req = null; + if (heading == Double.MIN_VALUE) + req = new GHRequest(new GHPoint(lat0, lon0), new GHPoint(lat1, lon1)); + else + req = new GHRequest(new GHPoint(lat0, lon0), new GHPoint(lat1, lon1), heading, Double.NaN); + req.setVehicle(searchCntx.getEncoder().toString()); req.setMaxSpeed(searchParams.getMaximumSpeed()); req.setSimplifyGeometry(simplifyGeometry); req.setAlgorithm("dijkstrabi"); - + PMap props = searchCntx.getProperties(); if (props != null && props.size() > 0) req.getHints().merge(props); @@ -826,7 +830,7 @@ else if (weightingMethod == WeightingMethod.RECOMMENDED) flexibleMode = true; } - + if (RoutingProfileType.isDriving(profileType) && RealTrafficDataProvider.getInstance().isInitialized()) req.setEdgeAnnotator(new TrafficEdgeAnnotator(mGraphHopper.getGraphHopperStorage())); @@ -849,6 +853,13 @@ else if (weightingMethod == WeightingMethod.RECOMMENDED) req.getHints().put("ch.disable", true); } + if (profileType == RoutingProfileType.DRIVING_EMERGENCY) + { + req.getHints().put("custom_weightings", true); + req.getHints().put("weighting_#acceleration#", true); + req.getHints().put("lm.disable", true); // REMOVE + } + if (_astarEpsilon != null) req.getHints().put("astarbi.epsilon", _astarEpsilon); if (_astarApproximation != null) diff --git a/openrouteservice/src/main/java/heigit/ors/routing/RoutingProfileManager.java b/openrouteservice/src/main/java/heigit/ors/routing/RoutingProfileManager.java index de8b08eae7..fabfa18723 100644 --- a/openrouteservice/src/main/java/heigit/ors/routing/RoutingProfileManager.java +++ b/openrouteservice/src/main/java/heigit/ors/routing/RoutingProfileManager.java @@ -66,6 +66,7 @@ import com.graphhopper.storage.RAMDataAccess; import com.graphhopper.util.DistanceCalc; import com.graphhopper.util.Helper; +import com.graphhopper.util.PointList; import com.vividsolutions.jts.geom.Coordinate; public class RoutingProfileManager { @@ -289,9 +290,9 @@ public List computeRoutes(RoutingRequest req, boolean invertFlow, b Coordinate c1 = coords[i]; GHResponse gr = null; if (invertFlow) - gr = rp.computeRoute(c0.y, c0.x, c1.y, c1.x, false, searchParams, customEdgeFilter, req.getSimplifyGeometry(), routeProcCntx); + gr = rp.computeRoute(c0.y, c0.x, c1.y, c1.x, 0.0, false, searchParams, customEdgeFilter, req.getSimplifyGeometry(), routeProcCntx); else - gr = rp.computeRoute(c1.y, c1.x, c0.y, c0.x, false, searchParams, customEdgeFilter, req.getSimplifyGeometry(), routeProcCntx); + gr = rp.computeRoute(c1.y, c1.x, c0.y, c0.x, 0.0, false, searchParams, customEdgeFilter, req.getSimplifyGeometry(), routeProcCntx); //if (gr.hasErrors()) // throw new InternalServerException(RoutingErrorCodes.UNKNOWN, String.format("Unable to find a route between points %d (%s) and %d (%s)", i, FormatUtility.formatCoordinate(c0), i + 1, FormatUtility.formatCoordinate(c1))); @@ -345,7 +346,8 @@ public RouteResult computeRoute(RoutingRequest req) throws Exception int nSegments = coords.length - 1; RouteProcessContext routeProcCntx = new RouteProcessContext(pathProcessor); EdgeFilter customEdgeFilter = rp.createAccessRestrictionFilter(coords); - + GHResponse prevResp = null; + for(int i = 1; i <= nSegments; ++i) { c1 = coords[i]; @@ -353,11 +355,16 @@ public RouteResult computeRoute(RoutingRequest req) throws Exception if (pathProcessor != null) pathProcessor.setSegmentIndex(i - 1, nSegments); - GHResponse gr = rp.computeRoute(c0.y, c0.x, c1.y, c1.x, c0.z == 1.0, searchParams, customEdgeFilter, req.getSimplifyGeometry(), routeProcCntx); - + double heading = Double.MIN_VALUE; + if (i > 1 && req.getContinueStraight()) + heading = getHeadingDirection(prevResp); + + GHResponse gr = rp.computeRoute(c0.y, c0.x, c1.y, c1.x, heading, c0.z == 1.0, searchParams, customEdgeFilter, req.getSimplifyGeometry(), routeProcCntx); + if (gr.hasErrors()) throw new InternalServerException(RoutingErrorCodes.UNKNOWN, String.format("Unable to find a route between points %d (%s) and %d (%s)", i, FormatUtility.formatCoordinate(c0), i + 1, FormatUtility.formatCoordinate(c1))); + prevResp = gr; routes.add(gr); c0 = c1; } @@ -365,6 +372,22 @@ public RouteResult computeRoute(RoutingRequest req) throws Exception return new RouteResultBuilder().createRouteResult(routes, req, (pathProcessor != null && (pathProcessor instanceof ExtraInfoProcessor)) ? ((ExtraInfoProcessor)pathProcessor).getExtras(): null); } + private double getHeadingDirection(GHResponse resp) + { + PointList points = resp.getBest().getPoints(); + int nPoints = points.size(); + if (nPoints > 1) + { + double lon1 = points.getLon(nPoints - 2); + double lat1 = points.getLat(nPoints - 2); + double lon2 = points.getLon(nPoints - 1); + double lat2 = points.getLat(nPoints - 1); + return Helper.ANGLE_CALC.calcAzimuth(lat1, lon1, lat2, lon2); + } + else + return 0; + } + public RoutingProfile getRouteProfile(RoutingRequest req, boolean oneToMany) throws Exception { RouteSearchParameters searchParams = req.getSearchParameters(); int profileType = searchParams.getProfileType(); diff --git a/openrouteservice/src/main/java/heigit/ors/routing/RoutingRequest.java b/openrouteservice/src/main/java/heigit/ors/routing/RoutingRequest.java index 4aedb47083..5cdb0972fe 100644 --- a/openrouteservice/src/main/java/heigit/ors/routing/RoutingRequest.java +++ b/openrouteservice/src/main/java/heigit/ors/routing/RoutingRequest.java @@ -37,10 +37,12 @@ public class RoutingRequest extends ServiceRequest private Boolean _includeElevation = false; private Boolean _includeGeometry = true; private Boolean _includeManeuvers = false; + private boolean _includeRoundaboutExits = false; private Boolean _simplifyGeometry = false; private String[] _attributes = null; private int _extraInfo; private int _locationIndex = -1; + private boolean _continueStraight = true; public RoutingRequest() { @@ -179,4 +181,20 @@ public Boolean getIncludeManeuvers() { public void setIncludeManeuvers(Boolean includeManeuvers) { _includeManeuvers = includeManeuvers; } + + public boolean getContinueStraight() { + return _continueStraight; + } + + public void setContinueStraight(boolean continueStraight) { + _continueStraight = continueStraight; + } + + public boolean getIncludeRoundaboutExits() { + return _includeRoundaboutExits; + } + + public void setIncludeRoundaboutExits(boolean includeRoundaboutExits) { + _includeRoundaboutExits = includeRoundaboutExits; + } } diff --git a/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/ORSWeightingFactory.java b/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/ORSWeightingFactory.java index 2764eb9846..5ad24d2aec 100644 --- a/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/ORSWeightingFactory.java +++ b/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/ORSWeightingFactory.java @@ -156,6 +156,9 @@ else if ("recommended".equalsIgnoreCase(strWeighting)) case "quiet": softWeightings.add(new QuietWeighting(encoder, getWeightingProps(weightingName, map), graphStorage)); break; + case "acceleration": + softWeightings.add(new AccelerationWeighting(encoder, getWeightingProps(weightingName, map), graphStorage)); + break; } } diff --git a/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/edgefilters/EdgeFilterSequence.java b/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/edgefilters/EdgeFilterSequence.java new file mode 100644 index 0000000000..0dea1415cf --- /dev/null +++ b/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/edgefilters/EdgeFilterSequence.java @@ -0,0 +1,81 @@ +/* + * Licensed to GIScience Research Group, Heidelberg University (GIScience) + * + * http://www.giscience.uni-hd.de + * http://www.heigit.org + * + * under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright + * ownership. The GIScience 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 heigit.ors.routing.graphhopper.extensions.edgefilters; + +import java.util.ArrayList; + +import com.graphhopper.routing.util.EdgeFilter; +import com.graphhopper.util.EdgeIteratorState; + +public class EdgeFilterSequence implements EdgeFilter { + + private ArrayList edgeFilters; + private int filtersCount; + + /** + * Creates an edges filter which accepts both direction of the specified + * vehicle. + */ + public EdgeFilterSequence(ArrayList edgeFilters) { + this.edgeFilters = edgeFilters; + this.filtersCount = edgeFilters.size(); + } + + public void addFilter(EdgeFilter e) { + edgeFilters.add(e); + filtersCount++; + } + + public EdgeFilter getEdgeFilter(Class type) + { + for (int i = 0; i < filtersCount; i++) { + if (type.isAssignableFrom(edgeFilters.get(i).getClass())) + return edgeFilters.get(i); + } + + return null; + } + + public boolean containsEdgeFilter(Class type) + { + for (int i = 0; i < filtersCount; i++) { + if (type.isAssignableFrom(edgeFilters.get(i).getClass())) + return true; + } + + return false; + } + + @Override + public final boolean accept(EdgeIteratorState iter) { + for (int i = 0; i < filtersCount; i++) { + if (!edgeFilters.get(i).accept(iter)) + return false; + } + + return true; + } + + @Override + public String toString() { + return "EdgeFilter Sequence :" + filtersCount; + } +} diff --git a/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/flagencoders/EmergencyFlagEncoder.java b/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/flagencoders/EmergencyFlagEncoder.java index c16e1e84ce..a781681340 100644 --- a/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/flagencoders/EmergencyFlagEncoder.java +++ b/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/flagencoders/EmergencyFlagEncoder.java @@ -258,7 +258,9 @@ public double getMaxSpeed(ReaderWay way ) // runge if (backSpeed >= 0 && (maxSpeed < 0 || backSpeed < maxSpeed)) maxSpeed = backSpeed; */ - String maxspeedTag = way.getTag("maxspeed"); + String maxspeedTag = way.getTag("maxspeed:hgv"); + if (Helper.isEmpty(maxspeedTag)) + maxspeedTag = way.getTag("maxspeed"); double maxSpeed = parseSpeed(maxspeedTag); if (bCheckMaxSpeed) diff --git a/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/weighting/AccelerationWeighting.java b/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/weighting/AccelerationWeighting.java new file mode 100644 index 0000000000..3c1d58903d --- /dev/null +++ b/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/weighting/AccelerationWeighting.java @@ -0,0 +1,168 @@ +/* + * Licensed to GIScience Research Group, Heidelberg University (GIScience) + * + * http://www.giscience.uni-hd.de + * http://www.heigit.org + * + * under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright + * ownership. The GIScience 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 heigit.ors.routing.graphhopper.extensions.weighting; + +import com.graphhopper.routing.util.FlagEncoder; +import com.graphhopper.routing.weighting.FastestWeighting; +import com.graphhopper.storage.GraphHopperStorage; +import com.graphhopper.storage.GraphStorage; +import com.graphhopper.util.AngleCalc; +import com.graphhopper.util.EdgeIteratorState; +import com.graphhopper.util.PMap; +import com.graphhopper.util.PointList; + +public class AccelerationWeighting extends FastestWeighting { + private GraphHopperStorage _ghStorage; + private AngleCalc _angleCalc = new AngleCalc(); + private long _maxEdges; + + public AccelerationWeighting(FlagEncoder encoder, PMap map, GraphStorage graphStorage) { + super(encoder, map); + _ghStorage = (GraphHopperStorage)graphStorage; + _maxEdges= _ghStorage.getEdges(); + } + + private double getTurnAngle(PointList currEdgeGeom, PointList prevEdgeGeom) + { + if (currEdgeGeom.size() >= 1 && prevEdgeGeom.size() >= 1) + { + int locIndex = prevEdgeGeom.size() - 1; + double lon0 = prevEdgeGeom.getLon(locIndex - 1); + double lat0 = prevEdgeGeom.getLat(locIndex - 1); + double lon1 = prevEdgeGeom.getLon(locIndex); + double lat1 = prevEdgeGeom.getLat(locIndex); + + double bearingBefore = Math.round(_angleCalc.calcAzimuth(lat0, lon0, lat1, lon1)); + + double lon2 = currEdgeGeom.getLon(1); + double lat2 = currEdgeGeom.getLat(1); + + double bearingAfter = (int)Math.round(_angleCalc.calcAzimuth(lat1, lon1, lat2, lon2)); + //bearingAfter = _angleCalc.alignOrientation(bearingBefore, bearingAfter); + double res = Math.abs(bearingBefore - bearingAfter); + if (res > 180) + { + res = 360 - res; + return res; + } + + return res; + } + + return 0.0; + } + + @Override + public double calcWeight(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) { + if (prevOrNextEdgeId == -1 || edgeState.getEdge() >= _maxEdges || prevOrNextEdgeId >= _maxEdges) + { + //TODO + return 1.0; + } + + PointList currEdgeGeom, prevEdgeGeom; + if (reverse) + { + prevEdgeGeom = _ghStorage.getEdgeIteratorState(edgeState.getEdge(), edgeState.getBaseNode()).fetchWayGeometry(3); + currEdgeGeom = _ghStorage.getEdgeIteratorState(prevOrNextEdgeId, edgeState.getBaseNode()).detach(true).fetchWayGeometry(3); + } + else + { + currEdgeGeom = _ghStorage.getEdgeIteratorState(edgeState.getEdge(), edgeState.getAdjNode()).fetchWayGeometry(3); + prevEdgeGeom = _ghStorage.getEdgeIteratorState(prevOrNextEdgeId, edgeState.getBaseNode()).fetchWayGeometry(3); + } + + double turnAngle = getTurnAngle(currEdgeGeom, prevEdgeGeom); + + if (isFullTurn(turnAngle)) + { + // TODO + return 1.1; + } + + return 1.0; + } + + private boolean isFullTurn(double angle) + { + return angle > 50 && angle <= 140; + } + + @Override + public long calcMillis(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) { + if (prevOrNextEdgeId == -1 || edgeState.getEdge() >= _maxEdges || prevOrNextEdgeId >= _maxEdges) + { + // compute acceleration for departure and finish edges. + return (long)(0); + } + + PointList currEdgeGeom, prevEdgeGeom; + if (reverse) + { + prevEdgeGeom = _ghStorage.getEdgeIteratorState(edgeState.getEdge(), edgeState.getBaseNode()).fetchWayGeometry(3); + currEdgeGeom = _ghStorage.getEdgeIteratorState(prevOrNextEdgeId, edgeState.getBaseNode()).detach(true).fetchWayGeometry(3); + } + else + { + currEdgeGeom = _ghStorage.getEdgeIteratorState(edgeState.getEdge(), edgeState.getAdjNode()).fetchWayGeometry(3); + prevEdgeGeom = _ghStorage.getEdgeIteratorState(prevOrNextEdgeId, edgeState.getBaseNode()).fetchWayGeometry(3); + } + + double turnAngle = getTurnAngle(currEdgeGeom, prevEdgeGeom); + + if (isFullTurn(turnAngle)) + { + /*double speed = 1000*edgeState.getDistance()/weight * SPEED_CONV; + double distAfter = currEdgeGeom.calcDistance(Helper.DIST_EARTH); + + // compute acceleration influence only for a segment after the turn. + int totalSeconds = (int)(weight/1000) + 100; + int accelTime = 0; + double accelDist = 0.0; + + for (int i= 0; i < totalSeconds; ++i) + { + double currSpeed = (i + 1)* 2.5*0.3048; + + accelTime = i + 1; + accelDist += currSpeed; + + if (currSpeed >= speed/SPEED_CONV) + break; + if (accelDist > distAfter) + break; + } + + accelTime *= 1000; + long fullSpeedTime = 0; + if (accelDist < distAfter) + { + fullSpeedTime = (long)((distAfter - accelDist)/speed * SPEED_CONV); + } + + return (long)(-weight + accelTime + fullSpeedTime);*/ + + return (long)0;// 10 seconds for every turn + } + + return 0; + } +} \ No newline at end of file diff --git a/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/weighting/AdditionWeighting.java b/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/weighting/AdditionWeighting.java index eee50399f4..20017379d0 100644 --- a/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/weighting/AdditionWeighting.java +++ b/openrouteservice/src/main/java/heigit/ors/routing/graphhopper/extensions/weighting/AdditionWeighting.java @@ -51,6 +51,8 @@ else if (count == 5) public abstract class WeightCalc { public abstract double calcWeight(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId); + + public abstract long calcMillis(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId); } public class OneWeightCalc extends WeightCalc @@ -66,6 +68,11 @@ public double calcWeight(EdgeIteratorState edgeState, boolean reverse, int prevO { return _weighting.calcWeight(edgeState, reverse, prevOrNextEdgeId); } + + public long calcMillis(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) + { + return _weighting.calcMillis(edgeState, reverse, prevOrNextEdgeId); + } } public class TwoWeightCalc extends OneWeightCalc @@ -82,6 +89,11 @@ public double calcWeight(EdgeIteratorState edgeState, boolean reverse, int prevO { return super.calcWeight(edgeState, reverse, prevOrNextEdgeId) + _weighting.calcWeight(edgeState, reverse, prevOrNextEdgeId); } + + public long calcMillis(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) + { + return super.calcMillis(edgeState, reverse, prevOrNextEdgeId) + _weighting.calcMillis(edgeState, reverse, prevOrNextEdgeId); + } } public class ThreeWeightCalc extends TwoWeightCalc @@ -98,6 +110,11 @@ public double calcWeight(EdgeIteratorState edgeState, boolean reverse, int prevO { return super.calcWeight(edgeState, reverse, prevOrNextEdgeId) + _weighting.calcWeight(edgeState, reverse, prevOrNextEdgeId); } + + public long calcMillis(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) + { + return super.calcMillis(edgeState, reverse, prevOrNextEdgeId) + _weighting.calcMillis(edgeState, reverse, prevOrNextEdgeId); + } } public class FourWeightCalc extends ThreeWeightCalc @@ -114,6 +131,11 @@ public double calcWeight(EdgeIteratorState edgeState, boolean reverse, int prevO { return super.calcWeight(edgeState, reverse, prevOrNextEdgeId) + _weighting.calcWeight(edgeState, reverse, prevOrNextEdgeId); } + + public long calcMillis(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) + { + return super.calcMillis(edgeState, reverse, prevOrNextEdgeId) + _weighting.calcMillis(edgeState, reverse, prevOrNextEdgeId); + } } public class FiveWeightCalc extends FourWeightCalc @@ -130,6 +152,11 @@ public double calcWeight(EdgeIteratorState edgeState, boolean reverse, int prevO { return super.calcWeight(edgeState, reverse, prevOrNextEdgeId) + _weighting.calcWeight(edgeState, reverse, prevOrNextEdgeId); } + + public long calcMillis(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) + { + return super.calcMillis(edgeState, reverse, prevOrNextEdgeId) + _weighting.calcMillis(edgeState, reverse, prevOrNextEdgeId); + } } @Override @@ -142,6 +169,11 @@ public double calcWeight(EdgeIteratorState edgeState, boolean reverse, int prevO public double getMinWeight(double distance) { return 0; } + + @Override + public long calcMillis(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) { + return _superWeighting.calcMillis(edgeState, reverse, prevOrNextEdgeId) + _weightCalc.calcMillis(edgeState, reverse, prevOrNextEdgeId); + } @Override public String getName() { diff --git a/openrouteservice/src/main/java/heigit/ors/services/accessibility/AccessibilityServiceSettings.java b/openrouteservice/src/main/java/heigit/ors/services/accessibility/AccessibilityServiceSettings.java index a973589ab5..9d749b1b24 100644 --- a/openrouteservice/src/main/java/heigit/ors/services/accessibility/AccessibilityServiceSettings.java +++ b/openrouteservice/src/main/java/heigit/ors/services/accessibility/AccessibilityServiceSettings.java @@ -20,14 +20,23 @@ */ package heigit.ors.services.accessibility; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.typesafe.config.ConfigObject; + import heigit.ors.common.TravelRangeType; import heigit.ors.config.AppConfig; +import heigit.ors.routing.RoutingProfileType; public class AccessibilityServiceSettings { private static int maximumLocations = 1; private static int maximumRangeDistance = 100000; // in meters + private static Map profileMaxRangeDistances; private static int maximumRangeTime = 3600; // in seconds + private static Map profileMaxRangeTimes; private static boolean routeDetailsAllowed = false; private static int responseLimit = 50; private static String attribution = ""; @@ -44,9 +53,30 @@ public class AccessibilityServiceSettings value = AppConfig.Global().getServiceParameter("accessibility", "maximum_range_distance"); if (value != null) maximumRangeDistance = Integer.parseInt(value); + else + { + List params = AppConfig.Global().getObjectList("accessibility", "maximum_range_distance"); + if (params != null) + { + profileMaxRangeDistances = getParameters(params); + if (profileMaxRangeDistances.containsKey(-1)) + maximumRangeDistance = profileMaxRangeDistances.get(-1); + } + } + value = AppConfig.Global().getServiceParameter("accessibility", "maximum_range_time"); if (value != null) maximumRangeTime = Integer.parseInt(value); + else + { + List params = AppConfig.Global().getObjectList("accessibility", "maximum_range_time"); + if (params != null) + { + profileMaxRangeTimes = getParameters(params); + if (profileMaxRangeTimes.containsKey(-1)) + maximumRangeTime = profileMaxRangeTimes.get(-1); + } + } value = AppConfig.Global().getServiceParameter("accessibility", "route_details_allowed"); if (value != null) routeDetailsAllowed = Boolean.parseBoolean(value); @@ -58,6 +88,28 @@ public class AccessibilityServiceSettings attribution = value; } + private static Map getParameters(List params) + { + Map result = new HashMap(); + + for(ConfigObject cfgObj : params) + { + if (cfgObj.containsKey("profiles") && cfgObj.containsKey("value")) + { + String[] profiles = cfgObj.toConfig().getString("profiles").split(","); + for (String profileStr : profiles) + { + profileStr = profileStr.trim(); + Integer profile = ("any".equalsIgnoreCase(profileStr)) ? -1 : RoutingProfileType.getFromString(profileStr); + if (profile != RoutingProfileType.UNKNOWN) + result.put(profile, cfgObj.toConfig().getInt("value")); + } + } + } + + return result; + } + public static Boolean getEnabled() { return enabled; } @@ -70,16 +122,30 @@ public static int getMaximumLocations() { return maximumLocations; } - public static int getMaximumRange(TravelRangeType range) { + public static int getMaximumRange(int profileType, TravelRangeType range) { + Integer res = 0; + switch(range) { case Distance: - return maximumRangeDistance; + res = maximumRangeDistance; + + if (profileMaxRangeDistances != null && profileMaxRangeDistances.containsKey(profileType)) + { + res = profileMaxRangeDistances.get(profileType); + } + break; case Time: - return maximumRangeTime; + res = maximumRangeTime; + + if (profileMaxRangeTimes != null && profileMaxRangeTimes.containsKey(profileType)) + { + res = profileMaxRangeTimes.get(profileType); + } + break; } - return 0; + return res; } public static boolean getRouteDetailsAllowed() diff --git a/openrouteservice/src/main/java/heigit/ors/services/accessibility/requestprocessors/json/JsonAccessibilityRequestProcessor.java b/openrouteservice/src/main/java/heigit/ors/services/accessibility/requestprocessors/json/JsonAccessibilityRequestProcessor.java index 18ade402e1..40a1ce6fe5 100644 --- a/openrouteservice/src/main/java/heigit/ors/services/accessibility/requestprocessors/json/JsonAccessibilityRequestProcessor.java +++ b/openrouteservice/src/main/java/heigit/ors/services/accessibility/requestprocessors/json/JsonAccessibilityRequestProcessor.java @@ -36,9 +36,12 @@ import com.vividsolutions.jts.geom.Geometry; import heigit.ors.accessibility.AccessibilityAnalyzer; +import heigit.ors.accessibility.AccessibilityErrorCodes; import heigit.ors.accessibility.AccessibilityRequest; import heigit.ors.accessibility.AccessibilityResult; import heigit.ors.common.StatusCode; +import heigit.ors.common.TravellerInfo; +import heigit.ors.exceptions.ParameterOutOfRangeException; import heigit.ors.exceptions.StatusCodeException; import heigit.ors.geojson.GeometryJSON; import heigit.ors.services.accessibility.AccessibilityServiceSettings; @@ -77,6 +80,20 @@ public void process(HttpServletResponse response) throws Exception if (req == null) throw new StatusCodeException(StatusCode.BAD_REQUEST, "AccessibilityRequest object is null."); + + List travellers = req.getTravellers(); + + if (travellers.size() > AccessibilityServiceSettings.getMaximumLocations()) + throw new ParameterOutOfRangeException(AccessibilityErrorCodes.PARAMETER_VALUE_EXCEEDS_MAXIMUM, "locations", Integer.toString(travellers.size()), Integer.toString(AccessibilityServiceSettings.getMaximumLocations())); + + for (int i = 0;i < travellers.size(); ++i){ + TravellerInfo traveller = travellers.get(i); + int maxAllowedRange = AccessibilityServiceSettings.getMaximumRange(traveller.getRouteSearchParameters().getProfileType(), traveller.getRangeType()); + double maxRange = traveller.getMaximumRange(); + if (maxRange > maxAllowedRange) + throw new ParameterOutOfRangeException(AccessibilityErrorCodes.PARAMETER_VALUE_EXCEEDS_MAXIMUM, "range", Integer.toString(maxAllowedRange), Double.toString(maxRange)); + } + AccessibilityResult accesibilityResult = AccessibilityAnalyzer.computeAccessibility(req); diff --git a/openrouteservice/src/main/java/heigit/ors/services/routing/requestprocessors/json/JsonRoutingRequestParser.java b/openrouteservice/src/main/java/heigit/ors/services/routing/requestprocessors/json/JsonRoutingRequestParser.java index 2be7d258ce..464d92861e 100644 --- a/openrouteservice/src/main/java/heigit/ors/services/routing/requestprocessors/json/JsonRoutingRequestParser.java +++ b/openrouteservice/src/main/java/heigit/ors/services/routing/requestprocessors/json/JsonRoutingRequestParser.java @@ -141,6 +141,14 @@ public static RoutingRequest parseFromRequestParams(HttpServletRequest request) value = request.getParameter("elevation"); if (!Helper.isEmpty(value)) req.setIncludeElevation(Boolean.parseBoolean(value)); + + value = request.getParameter("continue_straight"); + if (!Helper.isEmpty(value)) + req.setContinueStraight(Boolean.parseBoolean(value)); + + value = request.getParameter("roundabout_exits"); + if (!Helper.isEmpty(value)) + req.setIncludeRoundaboutExits(Boolean.parseBoolean(value)); value = request.getParameter("instructions_format"); if (!Helper.isEmpty(value)) diff --git a/openrouteservice/src/main/java/heigit/ors/services/routing/requestprocessors/json/JsonRoutingResponseWriter.java b/openrouteservice/src/main/java/heigit/ors/services/routing/requestprocessors/json/JsonRoutingResponseWriter.java index 9dc55b1ebd..fd37272e58 100644 --- a/openrouteservice/src/main/java/heigit/ors/services/routing/requestprocessors/json/JsonRoutingResponseWriter.java +++ b/openrouteservice/src/main/java/heigit/ors/services/routing/requestprocessors/json/JsonRoutingResponseWriter.java @@ -233,6 +233,11 @@ public static JSONArray toJsonArray(RoutingRequest request, RouteResult[] routeR jStep.put("maneuver", jManeuver); } } + + if (request.getIncludeRoundaboutExits() && step.getRoundaboutExitBearings() != null) + { + jStep.put("exit_bearings", new JSONArray(step.getRoundaboutExitBearings())); + } // add mode: driving, cycling, etc. diff --git a/openrouteservice/src/main/java/heigit/ors/servlet/filters/CompressionFilter.java b/openrouteservice/src/main/java/heigit/ors/servlet/filters/CompressionFilter.java index a881845d43..9dae1c964a 100644 --- a/openrouteservice/src/main/java/heigit/ors/servlet/filters/CompressionFilter.java +++ b/openrouteservice/src/main/java/heigit/ors/servlet/filters/CompressionFilter.java @@ -42,13 +42,16 @@ public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) String acceptEncoding = request.getHeader("accept-encoding"); if (acceptEncoding != null) { - if (acceptEncoding.indexOf(ContentEncodingType.BROTLI) != -1) { + /* Commented out as jBrotli library crashes the server. + * Java frames: (J=compiled Java code, j=interpreted, Vv=VM code) + *J 4868 org.meteogroup.jbrotli.BrotliStreamCompressor.freeNativeResources()I + * if (acceptEncoding.indexOf(ContentEncodingType.BROTLI) != -1) { BrotliResponseWrapper wrappedResponse = new BrotliResponseWrapper(response); chain.doFilter(req, wrappedResponse); wrappedResponse.finishResponse(); return; } - else if(acceptEncoding.indexOf(ContentEncodingType.GZIP) != -1) { + else*/ if(acceptEncoding.indexOf(ContentEncodingType.GZIP) != -1) { GZIPResponseWrapper wrappedResponse = new GZIPResponseWrapper(response); chain.doFilter(req, wrappedResponse); wrappedResponse.finishResponse(); diff --git a/openrouteservice/src/main/java/heigit/ors/util/PolylineEncoder.java b/openrouteservice/src/main/java/heigit/ors/util/PolylineEncoder.java index fa41174aef..5086e78dfe 100644 --- a/openrouteservice/src/main/java/heigit/ors/util/PolylineEncoder.java +++ b/openrouteservice/src/main/java/heigit/ors/util/PolylineEncoder.java @@ -52,7 +52,7 @@ public static String encode(final Coordinate[] coords, boolean includeElevation, return buffer.toString(); } - + private static void encode(long v, StringBuffer buffer) { v = v < 0 ? ~(v << 1) : v << 1;