Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add experimental cargo bike profile #1744

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ docs/
.github/
Dockerfile
envs/
.jqwik-database
.jqwik-database
4 changes: 3 additions & 1 deletion ors-api/src/main/java/org/heigit/ors/api/APIEnums.java
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,8 @@ public enum AvoidFeatures {
TOLLWAYS("tollways"),
FERRIES("ferries"),
FORDS("fords"),
STEPS("steps");
STEPS("steps"),
JUNCTION("junction");

private final String value;

Expand Down Expand Up @@ -299,6 +300,7 @@ public enum Profile {
CYCLING_ROAD("cycling-road"),
CYCLING_MOUNTAIN("cycling-mountain"),
CYCLING_ELECTRIC("cycling-electric"),
CYCLING_CARGO("cycling-cargo"),
FOOT_WALKING("foot-walking"),
FOOT_HIKING("foot-hiking"),
WHEELCHAIR("wheelchair"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class RequestOptions implements RouteRequestParameterNames {
@Schema(name = PARAM_AVOID_FEATURES, description = "List of features to avoid. ",
extensions = {@Extension(name = "itemRestrictions", properties = {
@ExtensionProperty(name = "ref", value = "profile"),
@ExtensionProperty(name = "itemsWhen", value = "{\"driving-*\":[\"highways\",\"tollways\",\"ferries\"],\"cycling-*\":[\"ferries\",\"steps\",\"fords\"],\"foot-*\":[\"ferries\",\"fords\",\"steps\"],\"wheelchair\":[\"ferries\",\"steps\"]}", parseValue = true)}
@ExtensionProperty(name = "itemsWhen", value = "{\"driving-*\":[\"highways\",\"tollways\",\"ferries\"],\"cycling-*\":[\"ferries\",\"steps\",\"fords\",\"junction\"],\"foot-*\":[\"ferries\",\"fords\",\"steps\"],\"wheelchair\":[\"ferries\",\"steps\"]}", parseValue = true)}
)},
example = "[\"highways\"]")
@JsonProperty(PARAM_AVOID_FEATURES)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

class APIMatrixRequestProfileConverterTest {
private APIEnums.Profile cyclingElectric;
private APIEnums.Profile cyclingCargo;
private APIEnums.Profile cyclingMountain;
private APIEnums.Profile cyclingRegular;
private APIEnums.Profile cyclingRoad;
Expand All @@ -23,6 +24,7 @@ class APIMatrixRequestProfileConverterTest {
@BeforeEach
void setUp() {
cyclingElectric = APIEnums.Profile.CYCLING_ELECTRIC;
cyclingCargo = APIEnums.Profile.CYCLING_CARGO;
cyclingMountain = APIEnums.Profile.CYCLING_MOUNTAIN;
cyclingRegular = APIEnums.Profile.CYCLING_REGULAR;
cyclingRoad = APIEnums.Profile.CYCLING_ROAD;
Expand All @@ -42,6 +44,7 @@ void convert() {
assertEquals(cyclingRoad, apiRequestProfileConverter.convert("cycling-road"));
assertEquals(cyclingMountain, apiRequestProfileConverter.convert("cycling-mountain"));
assertEquals(cyclingElectric, apiRequestProfileConverter.convert("cycling-electric"));
assertEquals(cyclingCargo, apiRequestProfileConverter.convert("cycling-cargo"));
assertEquals(footWalking, apiRequestProfileConverter.convert("foot-walking"));
assertEquals(footHiking, apiRequestProfileConverter.convert("foot-hiking"));
assertEquals(wheelchair, apiRequestProfileConverter.convert("wheelchair"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ void testAvoidFeaturesEnumCreation() throws ParameterValueException {
assertEquals(APIEnums.AvoidFeatures.HIGHWAYS, APIEnums.AvoidFeatures.forValue("highways"));
assertEquals(APIEnums.AvoidFeatures.STEPS, APIEnums.AvoidFeatures.forValue("steps"));
assertEquals(APIEnums.AvoidFeatures.TOLLWAYS, APIEnums.AvoidFeatures.forValue("tollways"));
assertEquals(APIEnums.AvoidFeatures.JUNCTION, APIEnums.AvoidFeatures.forValue("junction"));
assertThrows(ParameterValueException.class, () -> APIEnums.AvoidFeatures.forValue("invalid"));
}

Expand All @@ -114,6 +115,8 @@ void testAvoidFeaturesEnumValue() {
assertEquals("highways", APIEnums.AvoidFeatures.HIGHWAYS.toString());
assertEquals("steps", APIEnums.AvoidFeatures.STEPS.toString());
assertEquals("tollways", APIEnums.AvoidFeatures.TOLLWAYS.toString());
assertEquals("junction", APIEnums.AvoidFeatures.JUNCTION.toString());

}

@Test
Expand All @@ -139,6 +142,7 @@ void testProfileEnumCreation() throws ParameterValueException {
assertEquals(APIEnums.Profile.CYCLING_ROAD, APIEnums.Profile.forValue("cycling-road"));
assertEquals(APIEnums.Profile.CYCLING_MOUNTAIN, APIEnums.Profile.forValue("cycling-mountain"));
assertEquals(APIEnums.Profile.CYCLING_ELECTRIC, APIEnums.Profile.forValue("cycling-electric"));
assertEquals(APIEnums.Profile.CYCLING_CARGO, APIEnums.Profile.forValue("cycling-cargo"));
assertEquals(APIEnums.Profile.FOOT_WALKING, APIEnums.Profile.forValue("foot-walking"));
assertEquals(APIEnums.Profile.FOOT_HIKING, APIEnums.Profile.forValue("foot-hiking"));
assertEquals(APIEnums.Profile.WHEELCHAIR, APIEnums.Profile.forValue("wheelchair"));
Expand All @@ -153,6 +157,7 @@ void testProfileEnumValue() {
assertEquals("cycling-road", APIEnums.Profile.CYCLING_ROAD.toString());
assertEquals("cycling-mountain", APIEnums.Profile.CYCLING_MOUNTAIN.toString());
assertEquals("cycling-electric", APIEnums.Profile.CYCLING_ELECTRIC.toString());
assertEquals("cycling-cargo", APIEnums.Profile.CYCLING_CARGO.toString());
assertEquals("foot-walking", APIEnums.Profile.FOOT_WALKING.toString());
assertEquals("foot-hiking", APIEnums.Profile.FOOT_HIKING.toString());
assertEquals("wheelchair", APIEnums.Profile.WHEELCHAIR.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ void convertRouteRequestTest() throws Exception {
assertEquals(BordersExtractor.Avoid.CONTROLLED, routingRequest.getSearchParameters().getAvoidBorders());
assertArrayEquals(new int[]{115}, routingRequest.getSearchParameters().getAvoidCountries());
assertEquals(AvoidFeatureFlags.getFromString("fords"), routingRequest.getSearchParameters().getAvoidFeatureTypes());
assertTrue(AvoidFeatureFlags.isValid(18,32));

checkPolygon(routingRequest.getSearchParameters().getAvoidAreas(), geoJsonPolygon);

Expand Down Expand Up @@ -243,6 +244,24 @@ void TestWheelchairParameters() throws Exception {
assertTrue(params.allowUnsuitable());
}

@Test
void TestCargoBikeParameters() throws Exception {
request.setProfile(APIEnums.Profile.CYCLING_CARGO);

request.getRouteOptions().getProfileParams().setRestrictions(vehicleParams);

RoutingRequest routingRequest = routingService.convertRouteRequest(request);

WheelchairParameters params = (WheelchairParameters) routingRequest.getSearchParameters().getProfileParameters();
assertEquals(WheelchairTypesEncoder.getSmoothnessType(APIEnums.SmoothnessTypes.SMOOTHNESS_GOOD), params.getSmoothnessType());
assertEquals(3.0f, params.getMaximumIncline(), 0);
assertEquals(1.0f, params.getMaximumSlopedKerb(), 0);
assertEquals(2.0f, params.getMinimumWidth(), 0);
assertEquals(WheelchairTypesEncoder.getSurfaceType("asphalt"), params.getSurfaceType());
assertTrue(params.isRequireSurfaceQualityKnown());
assertTrue(params.allowUnsuitable());
}

@Test
void testBearings() throws StatusCodeException {
request.setBearings(new Double[][]{{10.0, 10.0}, {260.0, 90.0}, {45.0, 30.0}});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,7 @@ void expectCarToRejectWalkingAvoidables() {
JSONObject options = new JSONObject();
JSONArray avoids = new JSONArray();
avoids.put("steps");
avoids.put("junction");
avoids.put("fords");
options.put("avoid_features", avoids);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.heigit.ors.api.services;

import org.heigit.ors.routing.RouteResult;
import org.heigit.ors.routing.APIEnums;
import org.locationtech.jts.geom.Coordinate;
import org.heigit.ors.api.EndpointsProperties;
import org.heigit.ors.api.requests.routing.RouteRequestOptions;
import org.heigit.ors.api.requests.routing.RequestProfileParams;
import org.heigit.ors.api.requests.routing.RequestProfileParamsWeightings;
import org.heigit.ors.api.requests.routing.RouteRequest;

import org.junit.jupiter.api.Test;
class RoutingGreenTest {

public RoutingGreenTest() throws Exception {

}
@Test
void RoutingGreenTestCargo(){
EndpointsProperties endpointsProperties = new EndpointsProperties();
RoutingService routingService = new RoutingService(endpointsProperties);
Coordinate start = new Coordinate(8.650547, 49.400285);
Coordinate end = new Coordinate(9.121427,48.775995);

try{
RouteRequest request = new RouteRequest(start, end);
request.setProfile(APIEnums.Profile.CYCLING_CARGO);

RouteRequestOptions options = new RouteRequestOptions();
RequestProfileParams params = new RequestProfileParams();
RequestProfileParamsWeightings weightings = new RequestProfileParamsWeightings();

weightings.setGreenIndex(0.5f);
weightings.setQuietIndex(0.2f);
weightings.setSteepnessDifficulty(3);

params.setWeightings(weightings);
params.setSurfaceQualityKnown(true);
params.setAllowUnsuitable(true);

options.setProfileParams(params);
request.setRouteOptions(options);

RouteResult[] result = routingService.generateRouteFromRequest(request);
} catch (Exception e){
int i = 7;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ public class AvoidFeatureFlags {
public static final int STEPS = 4;
public static final int FERRIES = 8;
public static final int FORDS = 16;
public static final int JUNCTION = 32;

private static final int DRIVING_FEATURES = HIGHWAYS | TOLLWAYS | FERRIES | FORDS;
private static final int CYCLING_FEATURES = STEPS | FERRIES | FORDS;
private static final int CYCLING_FEATURES = STEPS | FERRIES | FORDS | JUNCTION;
private static final int WALKING_FEATURES = STEPS | FERRIES | FORDS;
private static final int WHEELCHAIR_FEATURES = WALKING_FEATURES;

Expand All @@ -38,6 +39,7 @@ public static int getFromString(String value) {
case "ferries" -> FERRIES;
case "steps" -> STEPS;
case "fords" -> FORDS;
case "junction" -> JUNCTION;
default -> 0;
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public class RoutingProfileType {
public static final int CYCLING_MOUNTAIN = 11;
public static final int CYCLING_ROAD = 12;
public static final int CYCLING_ELECTRIC = 17;
public static final int CYCLING_CARGO = 18;

// WALKING STUFF
public static final int FOOT_WALKING = 20;
Expand Down Expand Up @@ -98,6 +99,7 @@ public static boolean isCycling(int routePref) {
|| routePref == CYCLING_MOUNTAIN
|| routePref == CYCLING_ROAD
|| routePref == CYCLING_ELECTRIC
|| routePref == CYCLING_CARGO
|| routePref == GH_BIKE
|| routePref == GH_BIKE2
|| routePref == GH_BIKE_MTB
Expand All @@ -119,6 +121,7 @@ public static String getName(int profileType) {
case CYCLING_MOUNTAIN -> "cycling-mountain";
case CYCLING_ROAD -> "cycling-road";
case CYCLING_ELECTRIC -> "cycling-electric";
case CYCLING_CARGO -> "cycling-cargo";
case FOOT_WALKING -> "foot-walking";
case FOOT_HIKING -> "foot-hiking";
case FOOT_JOGGING -> "foot-jogging";
Expand Down Expand Up @@ -148,6 +151,7 @@ public static int getFromString(String profileType) {
case "cycling-mountain" -> CYCLING_MOUNTAIN;
case "cycling-road" -> CYCLING_ROAD;
case "cycling-electric" -> CYCLING_ELECTRIC;
case "cycling-cargo" -> CYCLING_CARGO;
case "foot-walking" -> FOOT_WALKING;
case "foot-hiking" -> FOOT_HIKING;
case "foot-jogging" -> FOOT_JOGGING;
Expand Down Expand Up @@ -191,6 +195,7 @@ public static String getEncoderName(int routePref) {
case RoutingProfileType.GH_FOOT -> FlagEncoderNames.GH_FOOT;
case RoutingProfileType.GH_HIKE -> FlagEncoderNames.GH_HIKE;
case RoutingProfileType.CYCLING_ELECTRIC -> FlagEncoderNames.BIKE_ELECTRO;
case RoutingProfileType.CYCLING_CARGO-> FlagEncoderNames.BIKE_CARGO;
default -> FlagEncoderNames.UNKNOWN;
};
}
Expand Down Expand Up @@ -227,6 +232,7 @@ public static int getFromEncoderName(String encoder) {
case FlagEncoderNames.GH_FOOT -> RoutingProfileType.FOOT_WALKING;
case FlagEncoderNames.GH_HIKE -> RoutingProfileType.FOOT_HIKING;
case FlagEncoderNames.BIKE_ELECTRO -> RoutingProfileType.CYCLING_ELECTRIC;
case FlagEncoderNames.BIKE_CARGO -> RoutingProfileType.CYCLING_CARGO;
default -> RoutingProfileType.UNKNOWN;
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/* This file is part of Openrouteservice.
*
* Openrouteservice is free software; you can redistribute it and/or modify it under the terms of the
* GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.

* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.

* You should have received a copy of the GNU Lesser General Public License along with this library;
* if not, see <https://www.gnu.org/licenses/>.
*/
package org.heigit.ors.routing.graphhopper.extensions;

public class JunctionType {

public static final int NONE = 0;

// https://en.wikipedia.org/wiki/Vehicle_category

public static final int M1 = 1;
public static final int M2 = 2;
public static final int M3 = 4;
public static final int M = M1 | M2 | M3;

public static final int N1 = 8;
public static final int N2 = 16;
public static final int N3 = 32;
public static final int L1 = 64; // L1 = Electric Cycle

public static final int N = N1 | N2 | N3;

public static final int GENERAL = M | N | L1;

// OSM classification
public static final int MOTORCAR = M1;
public static final int GOODS = N1;
public static final int HGV = N2 | N3;

public static final int CYCLING_CARGO = L1;

private JunctionType() {
}

public static boolean isSet(int flag, int value) {
return (flag & value) == flag;
}

public static boolean isType(int flag, int value) {
return (flag & value) != 0;
}

public static boolean isMType(int value) {
return isType(M, value);
}

public static boolean isNType(int value) {
return isType(N, value);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ public FlagEncoder createFlagEncoder(String name, PMap configuration) {
}
return new org.heigit.ors.routing.graphhopper.extensions.flagencoders.bike.ElectroBikeFlagEncoder(configuration);

case FlagEncoderNames.BIKE_CARGO:
// MARQ24 hardcoded "ignore" consider_elevation for the NextGenMountainBike FlagEncoder - when
// consider_elevation is enabled we have various detours (over smaler tracks)
if (configuration.getBool(KEY_CONSIDER_ELEVATION, false)) {
configuration.remove(KEY_CONSIDER_ELEVATION);
}
return new org.heigit.ors.routing.graphhopper.extensions.flagencoders.bike.CargoBikeFlagEncoder(configuration);

case FlagEncoderNames.ROADBIKE_ORS:
// MARQ24 hardcoded "ignore" consider_elevation for the NextGenRoadbike FlagEncoder - when
// consider_elevation is enabled we have various detours (over smaler tracks)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ private Keys() {
public static final String JUNCTION = "junction";
public static final String RAILWAY = "railway";
public static final String MAN_MADE = "man_made";
public static final String CYCLEWAY = "cycleway";
public static final String TUNNEL = "tunnel";
public static final String BICYCLE = "bicycle";
public static final String WATERWAY = "waterway";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,17 @@
import org.heigit.ors.routing.RoutingProfileCategory;
import org.heigit.ors.routing.graphhopper.extensions.storages.GraphStorageUtils;
import org.heigit.ors.routing.graphhopper.extensions.storages.TollwaysGraphStorage;
import org.heigit.ors.routing.graphhopper.extensions.storages.JunctionGraphStorage;
import org.heigit.ors.routing.graphhopper.extensions.storages.WayCategoryGraphStorage;
import org.heigit.ors.routing.pathprocessors.TollwayExtractor;
import org.heigit.ors.routing.pathprocessors.JunctionExtractor;


public class AvoidFeaturesEdgeFilter implements EdgeFilter {
private final byte[] buffer;
private final WayCategoryGraphStorage storage;
private TollwayExtractor tollwayExtractor;
private JunctionExtractor junctionExtractor;
private final int avoidFeatureType;

private static final int NOT_TOLLWAYS = ~AvoidFeatureFlags.TOLLWAYS;
Expand All @@ -45,6 +49,10 @@ public AvoidFeaturesEdgeFilter(int profileType, RouteSearchParameters searchPara
TollwaysGraphStorage extTollways = GraphStorageUtils.getGraphExtension(graphStorage, TollwaysGraphStorage.class);
if (extTollways != null)
tollwayExtractor = new TollwayExtractor(extTollways, searchParams.getProfileType(), searchParams.getProfileParameters());

JunctionGraphStorage extJunction = GraphStorageUtils.getGraphExtension(graphStorage, JunctionGraphStorage.class);
if (extJunction != null)
junctionExtractor = new JunctionExtractor(extJunction, searchParams.getProfileType(), searchParams.getProfileParameters());
}

public AvoidFeaturesEdgeFilter(int avoidFeatureType, GraphHopperStorage graphStorage) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class FlagEncoderNames {
public static final String ROADBIKE_ORS = "roadbike" + ORS_SUFFIX;
public static final String MTB_ORS = "mtb" + ORS_SUFFIX;
public static final String BIKE_ELECTRO = "electrobike";
public static final String BIKE_CARGO = "cargobike";

public static final String PEDESTRIAN_ORS = "pedestrian" + ORS_SUFFIX;
public static final String HIKING_ORS = "hiking" + ORS_SUFFIX;
Expand Down
Loading
Loading