From 3cdd2b93657a026d37d67a58a731ba23601b2daf Mon Sep 17 00:00:00 2001 From: aoles Date: Wed, 1 Oct 2025 17:17:51 +0200 Subject: [PATCH 1/9] refactor: replace binary mask for vehicle type access by separate encoded values --- .../routing/ev/AgriculturalAccess.java | 13 ++ .../com/graphhopper/routing/ev/BusAccess.java | 13 ++ .../routing/ev/DeliveryAccess.java | 13 ++ .../routing/ev/ForestryAccess.java | 13 ++ .../graphhopper/routing/ev/GoodsAccess.java | 13 ++ .../com/graphhopper/routing/ev/HgvAccess.java | 13 ++ .../ors/config/profile/BuildProperties.java | 26 ++++ .../profile/EncodedValuesProperties.java | 41 ++++- .../extensions/ORSGraphHopper.java | 4 +- .../extensions/OrsTagParserFactory.java | 6 + .../edgefilters/HeavyVehicleEdgeFilter.java | 46 +++++- .../util/parsers/VehicleAccessParser.java | 141 ++++++++++++++++++ 12 files changed, 330 insertions(+), 12 deletions(-) create mode 100644 ors-engine/src/main/java/com/graphhopper/routing/ev/AgriculturalAccess.java create mode 100644 ors-engine/src/main/java/com/graphhopper/routing/ev/BusAccess.java create mode 100644 ors-engine/src/main/java/com/graphhopper/routing/ev/DeliveryAccess.java create mode 100644 ors-engine/src/main/java/com/graphhopper/routing/ev/ForestryAccess.java create mode 100644 ors-engine/src/main/java/com/graphhopper/routing/ev/GoodsAccess.java create mode 100644 ors-engine/src/main/java/com/graphhopper/routing/ev/HgvAccess.java create mode 100644 ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/util/parsers/VehicleAccessParser.java diff --git a/ors-engine/src/main/java/com/graphhopper/routing/ev/AgriculturalAccess.java b/ors-engine/src/main/java/com/graphhopper/routing/ev/AgriculturalAccess.java new file mode 100644 index 0000000000..77b6eb3b71 --- /dev/null +++ b/ors-engine/src/main/java/com/graphhopper/routing/ev/AgriculturalAccess.java @@ -0,0 +1,13 @@ +package com.graphhopper.routing.ev; + +public class AgriculturalAccess { + public static final String KEY = "agricultural_access"; + + private AgriculturalAccess() { + // do not instantiate + } + + public static BooleanEncodedValue create() { + return new SimpleBooleanEncodedValue(KEY, false); + } +} diff --git a/ors-engine/src/main/java/com/graphhopper/routing/ev/BusAccess.java b/ors-engine/src/main/java/com/graphhopper/routing/ev/BusAccess.java new file mode 100644 index 0000000000..0bddeffe44 --- /dev/null +++ b/ors-engine/src/main/java/com/graphhopper/routing/ev/BusAccess.java @@ -0,0 +1,13 @@ +package com.graphhopper.routing.ev; + +public class BusAccess { + public static final String KEY = "bus_access"; + + private BusAccess() { + // do not instantiate + } + + public static BooleanEncodedValue create() { + return new SimpleBooleanEncodedValue(KEY, false); + } +} diff --git a/ors-engine/src/main/java/com/graphhopper/routing/ev/DeliveryAccess.java b/ors-engine/src/main/java/com/graphhopper/routing/ev/DeliveryAccess.java new file mode 100644 index 0000000000..6879b65e3a --- /dev/null +++ b/ors-engine/src/main/java/com/graphhopper/routing/ev/DeliveryAccess.java @@ -0,0 +1,13 @@ +package com.graphhopper.routing.ev; + +public class DeliveryAccess { + public static final String KEY = "delivery_access"; + + private DeliveryAccess() { + // do not instantiate + } + + public static BooleanEncodedValue create() { + return new SimpleBooleanEncodedValue(KEY, false); + } +} diff --git a/ors-engine/src/main/java/com/graphhopper/routing/ev/ForestryAccess.java b/ors-engine/src/main/java/com/graphhopper/routing/ev/ForestryAccess.java new file mode 100644 index 0000000000..ea90c25588 --- /dev/null +++ b/ors-engine/src/main/java/com/graphhopper/routing/ev/ForestryAccess.java @@ -0,0 +1,13 @@ +package com.graphhopper.routing.ev; + +public class ForestryAccess { + public static final String KEY = "forestry_access"; + + private ForestryAccess() { + // do not instantiate + } + + public static BooleanEncodedValue create() { + return new SimpleBooleanEncodedValue(KEY, false); + } +} diff --git a/ors-engine/src/main/java/com/graphhopper/routing/ev/GoodsAccess.java b/ors-engine/src/main/java/com/graphhopper/routing/ev/GoodsAccess.java new file mode 100644 index 0000000000..b965f5cce7 --- /dev/null +++ b/ors-engine/src/main/java/com/graphhopper/routing/ev/GoodsAccess.java @@ -0,0 +1,13 @@ +package com.graphhopper.routing.ev; + +public class GoodsAccess { + public static final String KEY = "goods_access"; + + private GoodsAccess() { + // do not instantiate + } + + public static BooleanEncodedValue create() { + return new SimpleBooleanEncodedValue(KEY, false); + } +} diff --git a/ors-engine/src/main/java/com/graphhopper/routing/ev/HgvAccess.java b/ors-engine/src/main/java/com/graphhopper/routing/ev/HgvAccess.java new file mode 100644 index 0000000000..7a67d8c44f --- /dev/null +++ b/ors-engine/src/main/java/com/graphhopper/routing/ev/HgvAccess.java @@ -0,0 +1,13 @@ +package com.graphhopper.routing.ev; + +public class HgvAccess { + public static final String KEY = "hgv_access"; + + private HgvAccess() { + // do not instantiate + } + + public static BooleanEncodedValue create() { + return new SimpleBooleanEncodedValue(KEY, false); + } +} diff --git a/ors-engine/src/main/java/org/heigit/ors/config/profile/BuildProperties.java b/ors-engine/src/main/java/org/heigit/ors/config/profile/BuildProperties.java index 1da286ba64..c8d3eeff51 100644 --- a/ors-engine/src/main/java/org/heigit/ors/config/profile/BuildProperties.java +++ b/ors-engine/src/main/java/org/heigit/ors/config/profile/BuildProperties.java @@ -94,6 +94,9 @@ public void initExtStorages() { if (storage != null) { ExtendedStorageName extendedStorageName = ExtendedStorageName.getEnum(key); switch (extendedStorageName) { + case HEAVY_VEHICLE: + handleHeavyVehicle(storage); + break; case OSM_ID: if (encodedValues.getOsmWayId() == null) { encodedValues.setOsmWayId(true); @@ -114,6 +117,29 @@ public void initExtStorages() { } } + private void handleHeavyVehicle(ExtendedStorageProperties storage) { + if (storage.getRestrictions() != null && storage.getRestrictions()) { + //TODO: set encoded values for heavy vehicle restrictions + } + if (encodedValues.getAgriculturalAccess() == null) { + encodedValues.setAgriculturalAccess(true); + } + if (encodedValues.getBusAccess() == null) { + encodedValues.setBusAccess(true); + } + if (encodedValues.getDeliveryAccess() == null) { + encodedValues.setDeliveryAccess(true); + } + if (encodedValues.getForestryAccess() == null) { + encodedValues.setForestryAccess(true); + } + if (encodedValues.getGoodsAccess() == null) { + encodedValues.setGoodsAccess(true); + } + if (encodedValues.getHgvAccess() == null) { + encodedValues.setHgvAccess(true); + } + } private void handleWayCategory() { if (encodedValues.getHighway() == null) { encodedValues.setHighway(true); diff --git a/ors-engine/src/main/java/org/heigit/ors/config/profile/EncodedValuesProperties.java b/ors-engine/src/main/java/org/heigit/ors/config/profile/EncodedValuesProperties.java index 85e4a761e8..f4a704ae0a 100644 --- a/ors-engine/src/main/java/org/heigit/ors/config/profile/EncodedValuesProperties.java +++ b/ors-engine/src/main/java/org/heigit/ors/config/profile/EncodedValuesProperties.java @@ -27,6 +27,18 @@ public class EncodedValuesProperties { private Boolean waySurface; @JsonProperty("way_type") private Boolean wayType; + @JsonProperty("agricultural_access") + private Boolean agriculturalAccess; + @JsonProperty("bus_access") + private Boolean busAccess; + @JsonProperty("delivery_access") + private Boolean deliveryAccess; + @JsonProperty("forestry_access") + private Boolean forestryAccess; + @JsonProperty("goods_access") + private Boolean goodsAccess; + @JsonProperty("hgv_access") + private Boolean hgvAccess; public EncodedValuesProperties() { } @@ -37,7 +49,10 @@ public EncodedValuesProperties(String ignored) { @JsonIgnore public boolean isEmpty() { - return osmWayId == null && ford == null && highway == null && waySurface == null && wayType == null; + return osmWayId == null && + ford == null && highway == null && + waySurface == null && wayType == null && + agriculturalAccess == null && busAccess == null && deliveryAccess == null && forestryAccess == null && goodsAccess == null && hgvAccess == null; } @JsonIgnore @@ -58,6 +73,24 @@ public String toString() { if (Boolean.TRUE.equals(wayType)) { out.add(WayType.KEY); } + if (Boolean.TRUE.equals(agriculturalAccess)) { + out.add(AgriculturalAccess.KEY); + } + if (Boolean.TRUE.equals(busAccess)) { + out.add(BusAccess.KEY); + } + if (Boolean.TRUE.equals(deliveryAccess)) { + out.add(DeliveryAccess.KEY); + } + if (Boolean.TRUE.equals(forestryAccess)) { + out.add(ForestryAccess.KEY); + } + if (Boolean.TRUE.equals(goodsAccess)) { + out.add(GoodsAccess.KEY); + } + if (Boolean.TRUE.equals(hgvAccess)) { + out.add(HgvAccess.KEY); + } return String.join(",", out); } @@ -67,6 +100,12 @@ public void merge(EncodedValuesProperties other) { osmWayId = ofNullable(this.osmWayId).orElse(other.osmWayId); waySurface = ofNullable(this.waySurface).orElse(other.waySurface); wayType = ofNullable(this.wayType).orElse(other.wayType); + agriculturalAccess = ofNullable(this.agriculturalAccess).orElse(other.agriculturalAccess); + busAccess = ofNullable(this.busAccess).orElse(other.busAccess); + deliveryAccess = ofNullable(this.deliveryAccess).orElse(other.deliveryAccess); + forestryAccess = ofNullable(this.forestryAccess).orElse(other.forestryAccess); + goodsAccess = ofNullable(this.goodsAccess).orElse(other.goodsAccess); + hgvAccess = ofNullable(this.hgvAccess).orElse(other.hgvAccess); } } diff --git a/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/ORSGraphHopper.java b/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/ORSGraphHopper.java index 53657efde3..16b5b54909 100644 --- a/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/ORSGraphHopper.java +++ b/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/ORSGraphHopper.java @@ -56,7 +56,6 @@ import org.heigit.ors.routing.graphhopper.extensions.flagencoders.FlagEncoderNames; import org.heigit.ors.routing.graphhopper.extensions.manage.ORSGraphManager; import org.heigit.ors.routing.graphhopper.extensions.storages.GraphStorageUtils; -import org.heigit.ors.routing.graphhopper.extensions.storages.HeavyVehicleAttributesGraphStorage; import org.heigit.ors.routing.graphhopper.extensions.storages.TrafficGraphStorage; import org.heigit.ors.routing.graphhopper.extensions.storages.builders.GraphStorageBuilder; import org.heigit.ors.routing.graphhopper.extensions.storages.builders.HereTrafficGraphStorageBuilder; @@ -447,8 +446,7 @@ protected void initCHPreparationHandler() { Weighting weighting = createWeighting(profile, new PMap()); if (profile.getVehicle().equals(FlagEncoderNames.HEAVYVEHICLE)) { - HeavyVehicleAttributesGraphStorage hgvStorage = GraphStorageUtils.getGraphExtension(getGraphHopperStorage(), HeavyVehicleAttributesGraphStorage.class); - EdgeFilter hgvEdgeFilter = new HeavyVehicleEdgeFilter(HeavyVehicleAttributes.HGV, null, hgvStorage); + EdgeFilter hgvEdgeFilter = new HeavyVehicleEdgeFilter(HeavyVehicleAttributes.HGV, null, getGraphHopperStorage()); weighting = new HgvAccessWeighting(weighting, hgvEdgeFilter); } diff --git a/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/OrsTagParserFactory.java b/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/OrsTagParserFactory.java index ff0b40348c..ed528e84fb 100644 --- a/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/OrsTagParserFactory.java +++ b/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/OrsTagParserFactory.java @@ -21,6 +21,12 @@ public TagParser create(String name, PMap configuration) { case OsmWayId.KEY -> new OsmWayIdParser(); case WaySurface.KEY -> new WaySurfaceParser(); case WayType.KEY -> new WayTypeParser(); + case AgriculturalAccess.KEY -> new VehicleAccessParser(AgriculturalAccess.create(), HeavyVehicleAttributes.AGRICULTURE); + case BusAccess.KEY -> new VehicleAccessParser(BusAccess.create(), HeavyVehicleAttributes.BUS); + case DeliveryAccess.KEY -> new VehicleAccessParser(DeliveryAccess.create(), HeavyVehicleAttributes.DELIVERY); + case ForestryAccess.KEY -> new VehicleAccessParser(ForestryAccess.create(), HeavyVehicleAttributes.FORESTRY); + case GoodsAccess.KEY -> new VehicleAccessParser(GoodsAccess.create(), HeavyVehicleAttributes.GOODS); + case HgvAccess.KEY -> new VehicleAccessParser(HgvAccess.create(), HeavyVehicleAttributes.HGV); default -> throw e; }; } diff --git a/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/edgefilters/HeavyVehicleEdgeFilter.java b/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/edgefilters/HeavyVehicleEdgeFilter.java index 5ed34471b7..608c1398c0 100644 --- a/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/edgefilters/HeavyVehicleEdgeFilter.java +++ b/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/edgefilters/HeavyVehicleEdgeFilter.java @@ -13,6 +13,7 @@ */ package org.heigit.ors.routing.graphhopper.extensions.edgefilters; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.querygraph.EdgeIteratorStateHelper; import com.graphhopper.routing.util.EdgeFilter; import com.graphhopper.storage.GraphHopperStorage; @@ -36,8 +37,28 @@ public class HeavyVehicleEdgeFilter implements EdgeFilter { private final Integer[] indexLocs; private final int restCount; + private BooleanEncodedValue agriculturalAccessEnc = null; + private BooleanEncodedValue busAccessEnc = null; + private BooleanEncodedValue deliveryAccessEnc = null; + private BooleanEncodedValue forestryAccessEnc = null; + private BooleanEncodedValue goodsAccessEnc = null; + private BooleanEncodedValue hgvAccessEnc = null; + public HeavyVehicleEdgeFilter(int vehicleType, VehicleParameters vehicleParams, GraphHopperStorage graphStorage) { this(vehicleType, vehicleParams, GraphStorageUtils.getGraphExtension(graphStorage, HeavyVehicleAttributesGraphStorage.class)); + var encodingManager = graphStorage.getEncodingManager(); + if (encodingManager.hasEncodedValue(AgriculturalAccess.KEY)) + agriculturalAccessEnc = encodingManager.getBooleanEncodedValue(AgriculturalAccess.KEY); + if (encodingManager.hasEncodedValue(BusAccess.KEY)) + busAccessEnc = encodingManager.getBooleanEncodedValue(BusAccess.KEY); + if (encodingManager.hasEncodedValue(DeliveryAccess.KEY)) + deliveryAccessEnc = encodingManager.getBooleanEncodedValue(DeliveryAccess.KEY); + if (encodingManager.hasEncodedValue(ForestryAccess.KEY)) + forestryAccessEnc = encodingManager.getBooleanEncodedValue(ForestryAccess.KEY); + if (encodingManager.hasEncodedValue(GoodsAccess.KEY)) + goodsAccessEnc = encodingManager.getBooleanEncodedValue(GoodsAccess.KEY); + if (encodingManager.hasEncodedValue(HgvAccess.KEY)) + hgvAccessEnc = encodingManager.getBooleanEncodedValue(HgvAccess.KEY); } public HeavyVehicleEdgeFilter(int vehicleType, VehicleParameters vehicleParams, HeavyVehicleAttributesGraphStorage hgvStorage) { @@ -80,16 +101,13 @@ public HeavyVehicleEdgeFilter(int vehicleType, VehicleParameters vehicleParams, @Override public boolean accept(EdgeIteratorState iter) { - int edgeId = EdgeIteratorStateHelper.getOriginalEdge(iter); - - int vt = gsHeavyVehicles.getEdgeVehicleType(edgeId); + // test access restrictions for the given vehicle type + if (!acceptVehicleType(iter)) + return false; - // access restriction for to the given vehicle type - if (vt != HeavyVehicleAttributes.UNKNOWN && isVehicleType(vt, vehicleType)) { - return false; - } + int edgeId = EdgeIteratorStateHelper.getOriginalEdge(iter); - if (hasHazmat && isVehicleType(vt, HeavyVehicleAttributes.HAZMAT)) { + if (hasHazmat && isVehicleType(gsHeavyVehicles.getEdgeVehicleType(edgeId), HeavyVehicleAttributes.HAZMAT)) { return false; } @@ -111,6 +129,18 @@ public boolean accept(EdgeIteratorState iter) { return true; } + private boolean acceptVehicleType(EdgeIteratorState edge) { + return switch (vehicleType) { + case HeavyVehicleAttributes.AGRICULTURE -> agriculturalAccessEnc == null || edge.get(agriculturalAccessEnc); + case HeavyVehicleAttributes.BUS -> busAccessEnc == null || edge.get(busAccessEnc); + case HeavyVehicleAttributes.DELIVERY -> deliveryAccessEnc == null || edge.get(deliveryAccessEnc); + case HeavyVehicleAttributes.FORESTRY -> forestryAccessEnc == null || edge.get(forestryAccessEnc); + case HeavyVehicleAttributes.GOODS -> goodsAccessEnc == null || edge.get(goodsAccessEnc); + case HeavyVehicleAttributes.HGV -> hgvAccessEnc == null || edge.get(hgvAccessEnc); + default -> true; + }; + } + private boolean isVehicleType(int vt, int vehicleType) { return (vt & vehicleType) == vehicleType; } diff --git a/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/util/parsers/VehicleAccessParser.java b/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/util/parsers/VehicleAccessParser.java new file mode 100644 index 0000000000..09e4c76cd2 --- /dev/null +++ b/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/util/parsers/VehicleAccessParser.java @@ -0,0 +1,141 @@ +package org.heigit.ors.routing.graphhopper.extensions.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.*; +import com.graphhopper.routing.util.parsers.TagParser; +import com.graphhopper.storage.IntsRef; +import org.heigit.ors.routing.graphhopper.extensions.HeavyVehicleAttributes; + +import java.util.*; + + +public class VehicleAccessParser implements TagParser { + private final BooleanEncodedValue vehicleAccessEnc; + private final int targetVehicleType; + private int blockedVehicleTypes = 0; + private final List motorVehicleRestrictions = new ArrayList<>(5); + private final Set motorVehicleRestrictedValues = new HashSet<>(5); + private final Set motorVehicleHgvValues = new HashSet<>(6); + private final Set noValues = new HashSet<>(5); + private final Set yesValues = new HashSet<>(5); + + + public VehicleAccessParser(BooleanEncodedValue vehicleAccessEnc, int vehicleType) { + this.vehicleAccessEnc = vehicleAccessEnc; + this.targetVehicleType = vehicleType; + + motorVehicleRestrictions.addAll(Arrays.asList("motorcar", "motor_vehicle", "vehicle", "access")); + + motorVehicleRestrictedValues.add("private"); + motorVehicleRestrictedValues.add("no"); + motorVehicleRestrictedValues.add("restricted"); + motorVehicleRestrictedValues.add("military"); + + motorVehicleHgvValues.addAll(Arrays.asList("hgv", "goods", "bus", "agricultural", "forestry", "delivery")); + + noValues.addAll(Arrays.asList("no", "private")); + yesValues.addAll(Arrays.asList("yes", "designated")); + } + + @Override + public void createEncodedValues(EncodedValueLookup encodedValueLookup, List list) { + list.add(vehicleAccessEnc); + } + + @Override + public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay readerWay, boolean b, IntsRef relationFlags) { + processWay(readerWay); + boolean blocked = (blockedVehicleTypes & targetVehicleType) == targetVehicleType; + vehicleAccessEnc.setBool(false, edgeFlags, !blocked); + return edgeFlags; + } + + public void processWay(ReaderWay way) { + if (!way.hasTag("highway")) + return; + + blockedVehicleTypes = 0;// reset values + + // process motor vehicle restrictions before any more specific vehicle type tags which override the former + processMotorVehicleRestrictions(way); + + processSpecificVehicleTypes(way); + } + + private void processMotorVehicleRestrictions(ReaderWay way) { + // if there are any generic motor vehicle restrictions restrict all types ... + if (way.hasTag(motorVehicleRestrictions, motorVehicleRestrictedValues)) { + blockedVehicleTypes = HeavyVehicleAttributes.ANY; + } + //... except ones explicitly listed + if (way.hasTag(motorVehicleRestrictions, motorVehicleHgvValues)) { + int allowedTypes = getAllowedVehicleTypes(way); + blockedVehicleTypes = HeavyVehicleAttributes.ANY & ~allowedTypes; + } + } + + private int getAllowedVehicleTypes(ReaderWay way) { + int flag = 0; + for (String key : motorVehicleRestrictions) { + for (String val : way.getTagValues(key)) { + if (motorVehicleHgvValues.contains(val)) { + flag |= HeavyVehicleAttributes.getFromString(val); + } + } + } + return flag; + } + + private void processSpecificVehicleTypes(ReaderWay way) { + Iterator> it = way.getProperties(); + while (it.hasNext()) { + Map.Entry entry = it.next(); + String key = entry.getKey(); + if (!motorVehicleHgvValues.contains(key)) { + continue; + } + + String value = entry.getValue().toString(); + //TODO: account for :[forward/backward] keys + //TODO: allow access: as described in #703. Might be necessary to adjust the upstream PBF parsing part as well. + String vehicleType = getVehicleType(key, value); + String accessValue = getVehicleAccess(vehicleType, value); + + setAccessFlags(vehicleType, accessValue); + if (vehicleType.equals(value)) {// e.g. hgv=delivery implies that hgv other than delivery vehicles are blocked + setAccessFlags(key, "no"); + } + } + } + + private String getVehicleType(String key, String value) { + return motorVehicleHgvValues.contains(value) ? value : key;// hgv=[delivery/agricultural/forestry] + } + + private String getVehicleAccess(String vehicleType, String value) { + if (vehicleType.equals(value) || yesValues.contains(value)) + return "yes"; + else if (noValues.contains(value)) + return "no"; + + return null; + } + + /** + * Toggle on/off the bit corresponding to a given hgv type defined by {@code flag} inside binary restriction masks + * based on the value of {@code tag}. "no" sets the bit in {@code _hgvType}, while "yes" unsets it. + * + * @param vehicle a String describing one of the vehicle types defined in {@code HeavyVehicleAttributes} + * @param access a String describing the access restriction + */ + private void setAccessFlags(String vehicle, String access) { + int flag = HeavyVehicleAttributes.getFromString(vehicle); + if (access != null) { + if ("no".equals(access)) + blockedVehicleTypes |= flag; + else if ("yes".equals(access)) + blockedVehicleTypes &= ~flag; + } + } + +} From d0b07b1ed55a6398817bfcb395b1a26afadbe79b Mon Sep 17 00:00:00 2001 From: aoles Date: Thu, 2 Oct 2025 16:59:32 +0200 Subject: [PATCH 2/9] refactor: convert the binary flag "hazmat" into a separate encoded value --- .../graphhopper/routing/ev/HazmatAccess.java | 13 ++++++ .../ors/config/profile/BuildProperties.java | 3 ++ .../profile/EncodedValuesProperties.java | 9 +++- .../extensions/OrsTagParserFactory.java | 1 + .../edgefilters/HeavyVehicleEdgeFilter.java | 13 +++--- .../util/parsers/HazmatAccessParser.java | 45 +++++++++++++++++++ 6 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 ors-engine/src/main/java/com/graphhopper/routing/ev/HazmatAccess.java create mode 100644 ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/util/parsers/HazmatAccessParser.java diff --git a/ors-engine/src/main/java/com/graphhopper/routing/ev/HazmatAccess.java b/ors-engine/src/main/java/com/graphhopper/routing/ev/HazmatAccess.java new file mode 100644 index 0000000000..3bc53c5056 --- /dev/null +++ b/ors-engine/src/main/java/com/graphhopper/routing/ev/HazmatAccess.java @@ -0,0 +1,13 @@ +package com.graphhopper.routing.ev; + +public class HazmatAccess { + public static final String KEY = "hazmat_access"; + + private HazmatAccess() { + // do not instantiate + } + + public static BooleanEncodedValue create() { + return new SimpleBooleanEncodedValue(KEY, false); + } +} diff --git a/ors-engine/src/main/java/org/heigit/ors/config/profile/BuildProperties.java b/ors-engine/src/main/java/org/heigit/ors/config/profile/BuildProperties.java index c8d3eeff51..65400a524f 100644 --- a/ors-engine/src/main/java/org/heigit/ors/config/profile/BuildProperties.java +++ b/ors-engine/src/main/java/org/heigit/ors/config/profile/BuildProperties.java @@ -139,6 +139,9 @@ private void handleHeavyVehicle(ExtendedStorageProperties storage) { if (encodedValues.getHgvAccess() == null) { encodedValues.setHgvAccess(true); } + if (encodedValues.getHazmatAccess() == null) { + encodedValues.setHazmatAccess(true); + } } private void handleWayCategory() { if (encodedValues.getHighway() == null) { diff --git a/ors-engine/src/main/java/org/heigit/ors/config/profile/EncodedValuesProperties.java b/ors-engine/src/main/java/org/heigit/ors/config/profile/EncodedValuesProperties.java index f4a704ae0a..d27018f8b1 100644 --- a/ors-engine/src/main/java/org/heigit/ors/config/profile/EncodedValuesProperties.java +++ b/ors-engine/src/main/java/org/heigit/ors/config/profile/EncodedValuesProperties.java @@ -39,6 +39,8 @@ public class EncodedValuesProperties { private Boolean goodsAccess; @JsonProperty("hgv_access") private Boolean hgvAccess; + @JsonProperty("hazmat_access") + private Boolean hazmatAccess; public EncodedValuesProperties() { } @@ -52,7 +54,8 @@ public boolean isEmpty() { return osmWayId == null && ford == null && highway == null && waySurface == null && wayType == null && - agriculturalAccess == null && busAccess == null && deliveryAccess == null && forestryAccess == null && goodsAccess == null && hgvAccess == null; + agriculturalAccess == null && busAccess == null && deliveryAccess == null && forestryAccess == null && goodsAccess == null && hgvAccess == null && + hazmatAccess == null; } @JsonIgnore @@ -91,6 +94,9 @@ public String toString() { if (Boolean.TRUE.equals(hgvAccess)) { out.add(HgvAccess.KEY); } + if (Boolean.TRUE.equals(hazmatAccess)) { + out.add(HazmatAccess.KEY); + } return String.join(",", out); } @@ -106,6 +112,7 @@ public void merge(EncodedValuesProperties other) { forestryAccess = ofNullable(this.forestryAccess).orElse(other.forestryAccess); goodsAccess = ofNullable(this.goodsAccess).orElse(other.goodsAccess); hgvAccess = ofNullable(this.hgvAccess).orElse(other.hgvAccess); + hazmatAccess = ofNullable(this.hazmatAccess).orElse(other.hazmatAccess); } } diff --git a/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/OrsTagParserFactory.java b/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/OrsTagParserFactory.java index ed528e84fb..3934f3e926 100644 --- a/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/OrsTagParserFactory.java +++ b/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/OrsTagParserFactory.java @@ -27,6 +27,7 @@ public TagParser create(String name, PMap configuration) { case ForestryAccess.KEY -> new VehicleAccessParser(ForestryAccess.create(), HeavyVehicleAttributes.FORESTRY); case GoodsAccess.KEY -> new VehicleAccessParser(GoodsAccess.create(), HeavyVehicleAttributes.GOODS); case HgvAccess.KEY -> new VehicleAccessParser(HgvAccess.create(), HeavyVehicleAttributes.HGV); + case HazmatAccess.KEY -> new HazmatAccessParser(); default -> throw e; }; } diff --git a/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/edgefilters/HeavyVehicleEdgeFilter.java b/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/edgefilters/HeavyVehicleEdgeFilter.java index 608c1398c0..256aa56fb1 100644 --- a/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/edgefilters/HeavyVehicleEdgeFilter.java +++ b/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/edgefilters/HeavyVehicleEdgeFilter.java @@ -43,6 +43,8 @@ public class HeavyVehicleEdgeFilter implements EdgeFilter { private BooleanEncodedValue forestryAccessEnc = null; private BooleanEncodedValue goodsAccessEnc = null; private BooleanEncodedValue hgvAccessEnc = null; + private BooleanEncodedValue hazmatAccessEnc = null; + public HeavyVehicleEdgeFilter(int vehicleType, VehicleParameters vehicleParams, GraphHopperStorage graphStorage) { this(vehicleType, vehicleParams, GraphStorageUtils.getGraphExtension(graphStorage, HeavyVehicleAttributesGraphStorage.class)); @@ -59,6 +61,8 @@ public HeavyVehicleEdgeFilter(int vehicleType, VehicleParameters vehicleParams, goodsAccessEnc = encodingManager.getBooleanEncodedValue(GoodsAccess.KEY); if (encodingManager.hasEncodedValue(HgvAccess.KEY)) hgvAccessEnc = encodingManager.getBooleanEncodedValue(HgvAccess.KEY); + if (encodingManager.hasEncodedValue(HazmatAccess.KEY)) + hazmatAccessEnc = encodingManager.getBooleanEncodedValue(HazmatAccess.KEY); } public HeavyVehicleEdgeFilter(int vehicleType, VehicleParameters vehicleParams, HeavyVehicleAttributesGraphStorage hgvStorage) { @@ -105,12 +109,11 @@ public boolean accept(EdgeIteratorState iter) { if (!acceptVehicleType(iter)) return false; - int edgeId = EdgeIteratorStateHelper.getOriginalEdge(iter); - - if (hasHazmat && isVehicleType(gsHeavyVehicles.getEdgeVehicleType(edgeId), HeavyVehicleAttributes.HAZMAT)) { + if (hasHazmat && !(hazmatAccessEnc == null || iter.get(hazmatAccessEnc)) ) { return false; } + int edgeId = EdgeIteratorStateHelper.getOriginalEdge(iter); if (restCount != 0) { if (restCount == 1) { double value = gsHeavyVehicles.getEdgeRestrictionValue(edgeId, indexValues[0]); @@ -140,8 +143,4 @@ private boolean acceptVehicleType(EdgeIteratorState edge) { default -> true; }; } - - private boolean isVehicleType(int vt, int vehicleType) { - return (vt & vehicleType) == vehicleType; - } } diff --git a/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/util/parsers/HazmatAccessParser.java b/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/util/parsers/HazmatAccessParser.java new file mode 100644 index 0000000000..1dfaf7fce5 --- /dev/null +++ b/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/util/parsers/HazmatAccessParser.java @@ -0,0 +1,45 @@ +package org.heigit.ors.routing.graphhopper.extensions.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.*; +import com.graphhopper.routing.util.parsers.TagParser; +import com.graphhopper.storage.IntsRef; + +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +public class HazmatAccessParser implements TagParser { + private final BooleanEncodedValue hazmatAccessEnc; + private final Pattern patternHazmat; + + public HazmatAccessParser() { + this(HazmatAccess.create()); + } + + public HazmatAccessParser(BooleanEncodedValue hazmatAccessEnc) { + this.hazmatAccessEnc = hazmatAccessEnc; + patternHazmat = Pattern.compile("^hazmat(:[B-E])?$"); + } + + @Override + public void createEncodedValues(EncodedValueLookup encodedValueLookup, List list) { + list.add(hazmatAccessEnc); + } + + @Override + public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way, boolean b, IntsRef relationFlags) { + boolean hasHazmatRestriction = way.getTags().entrySet().stream().anyMatch(this::isHazmatRestriction); + + hazmatAccessEnc.setBool(false, edgeFlags, !hasHazmatRestriction); + + return edgeFlags; + } + + private boolean isHazmatRestriction(Map.Entry entry) { + String key = entry.getKey(); + String value = entry.getValue().toString(); + + return patternHazmat.matcher(key).matches() && "no".equals(value); + } +} From c6048e1d817882ff91c082ff095fec44674e41be Mon Sep 17 00:00:00 2001 From: aoles Date: Wed, 22 Oct 2025 14:52:59 +0200 Subject: [PATCH 3/9] refactor: query encoded values for vehicle parameters in edge filter --- .../ors/config/profile/BuildProperties.java | 16 +++- .../profile/EncodedValuesProperties.java | 33 +++++++- .../edgefilters/HeavyVehicleEdgeFilter.java | 83 +++++++++++-------- 3 files changed, 94 insertions(+), 38 deletions(-) diff --git a/ors-engine/src/main/java/org/heigit/ors/config/profile/BuildProperties.java b/ors-engine/src/main/java/org/heigit/ors/config/profile/BuildProperties.java index 65400a524f..1509254df6 100644 --- a/ors-engine/src/main/java/org/heigit/ors/config/profile/BuildProperties.java +++ b/ors-engine/src/main/java/org/heigit/ors/config/profile/BuildProperties.java @@ -119,7 +119,21 @@ public void initExtStorages() { private void handleHeavyVehicle(ExtendedStorageProperties storage) { if (storage.getRestrictions() != null && storage.getRestrictions()) { - //TODO: set encoded values for heavy vehicle restrictions + if (encodedValues.getMaxAxleLoad() == null) { + encodedValues.setMaxAxleLoad(true); + } + if (encodedValues.getMaxHeight() == null) { + encodedValues.setMaxHeight(true); + } + if (encodedValues.getMaxLength() == null) { + encodedValues.setMaxLength(true); + } + if (encodedValues.getMaxWeight() == null) { + encodedValues.setMaxWeight(true); + } + if (encodedValues.getMaxWidth() == null) { + encodedValues.setMaxWidth(true); + } } if (encodedValues.getAgriculturalAccess() == null) { encodedValues.setAgriculturalAccess(true); diff --git a/ors-engine/src/main/java/org/heigit/ors/config/profile/EncodedValuesProperties.java b/ors-engine/src/main/java/org/heigit/ors/config/profile/EncodedValuesProperties.java index d27018f8b1..5d9435ad4d 100644 --- a/ors-engine/src/main/java/org/heigit/ors/config/profile/EncodedValuesProperties.java +++ b/ors-engine/src/main/java/org/heigit/ors/config/profile/EncodedValuesProperties.java @@ -41,6 +41,16 @@ public class EncodedValuesProperties { private Boolean hgvAccess; @JsonProperty("hazmat_access") private Boolean hazmatAccess; + @JsonProperty("max_axle_load") + private Boolean maxAxleLoad; + @JsonProperty("max_height") + private Boolean maxHeight; + @JsonProperty("max_length") + private Boolean maxLength; + @JsonProperty("max_weight") + private Boolean maxWeight; + @JsonProperty("max_width") + private Boolean maxWidth; public EncodedValuesProperties() { } @@ -55,7 +65,8 @@ public boolean isEmpty() { ford == null && highway == null && waySurface == null && wayType == null && agriculturalAccess == null && busAccess == null && deliveryAccess == null && forestryAccess == null && goodsAccess == null && hgvAccess == null && - hazmatAccess == null; + hazmatAccess == null && + maxAxleLoad == null && maxHeight == null && maxLength == null && maxWeight == null && maxWidth == null; } @JsonIgnore @@ -97,6 +108,21 @@ public String toString() { if (Boolean.TRUE.equals(hazmatAccess)) { out.add(HazmatAccess.KEY); } + if (Boolean.TRUE.equals(maxAxleLoad)) { + out.add(MaxAxleLoad.KEY); + } + if (Boolean.TRUE.equals(maxHeight)) { + out.add(MaxHeight.KEY); + } + if (Boolean.TRUE.equals(maxLength)) { + out.add(MaxLength.KEY); + } + if (Boolean.TRUE.equals(maxWeight)) { + out.add(MaxWeight.KEY); + } + if (Boolean.TRUE.equals(maxWidth)) { + out.add(MaxWidth.KEY); + } return String.join(",", out); } @@ -113,6 +139,11 @@ public void merge(EncodedValuesProperties other) { goodsAccess = ofNullable(this.goodsAccess).orElse(other.goodsAccess); hgvAccess = ofNullable(this.hgvAccess).orElse(other.hgvAccess); hazmatAccess = ofNullable(this.hazmatAccess).orElse(other.hazmatAccess); + maxAxleLoad = ofNullable(this.maxAxleLoad).orElse(other.maxAxleLoad); + maxHeight = ofNullable(this.maxHeight).orElse(other.maxHeight); + maxLength = ofNullable(this.maxLength).orElse(other.maxLength); + maxWeight = ofNullable(this.maxWeight).orElse(other.maxWeight); + maxWidth = ofNullable(this.maxWidth).orElse(other.maxWidth); } } diff --git a/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/edgefilters/HeavyVehicleEdgeFilter.java b/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/edgefilters/HeavyVehicleEdgeFilter.java index 256aa56fb1..b21e750def 100644 --- a/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/edgefilters/HeavyVehicleEdgeFilter.java +++ b/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/edgefilters/HeavyVehicleEdgeFilter.java @@ -14,7 +14,6 @@ package org.heigit.ors.routing.graphhopper.extensions.edgefilters; import com.graphhopper.routing.ev.*; -import com.graphhopper.routing.querygraph.EdgeIteratorStateHelper; import com.graphhopper.routing.util.EdgeFilter; import com.graphhopper.storage.GraphHopperStorage; import com.graphhopper.util.EdgeIteratorState; @@ -26,15 +25,13 @@ import org.heigit.ors.routing.parameters.VehicleParameters; import java.util.ArrayList; +import java.util.List; public class HeavyVehicleEdgeFilter implements EdgeFilter { private final int vehicleType; private final boolean hasHazmat; - private final HeavyVehicleAttributesGraphStorage gsHeavyVehicles; private final float[] restrictionValues; - private final double[] retValues; private final Integer[] indexValues; - private final Integer[] indexLocs; private final int restCount; private BooleanEncodedValue agriculturalAccessEnc = null; @@ -44,6 +41,11 @@ public class HeavyVehicleEdgeFilter implements EdgeFilter { private BooleanEncodedValue goodsAccessEnc = null; private BooleanEncodedValue hgvAccessEnc = null; private BooleanEncodedValue hazmatAccessEnc = null; + private DecimalEncodedValue maxAxleLoadEnc = null; + private DecimalEncodedValue maxHeightEnc = null; + private DecimalEncodedValue maxLengthEnc = null; + private DecimalEncodedValue maxWeightEnc = null; + private DecimalEncodedValue maxWidthEnc = null; public HeavyVehicleEdgeFilter(int vehicleType, VehicleParameters vehicleParams, GraphHopperStorage graphStorage) { @@ -63,6 +65,16 @@ public HeavyVehicleEdgeFilter(int vehicleType, VehicleParameters vehicleParams, hgvAccessEnc = encodingManager.getBooleanEncodedValue(HgvAccess.KEY); if (encodingManager.hasEncodedValue(HazmatAccess.KEY)) hazmatAccessEnc = encodingManager.getBooleanEncodedValue(HazmatAccess.KEY); + if (encodingManager.hasEncodedValue(MaxAxleLoad.KEY)) + maxAxleLoadEnc = encodingManager.getDecimalEncodedValue(MaxAxleLoad.KEY); + if (encodingManager.hasEncodedValue(MaxHeight.KEY)) + maxHeightEnc = encodingManager.getDecimalEncodedValue(MaxHeight.KEY); + if (encodingManager.hasEncodedValue(MaxLength.KEY)) + maxLengthEnc = encodingManager.getDecimalEncodedValue(MaxLength.KEY); + if (encodingManager.hasEncodedValue(MaxWeight.KEY)) + maxWeightEnc = encodingManager.getDecimalEncodedValue(MaxWeight.KEY); + if (encodingManager.hasEncodedValue(MaxWidth.KEY)) + maxWidthEnc = encodingManager.getDecimalEncodedValue(MaxWidth.KEY); } public HeavyVehicleEdgeFilter(int vehicleType, VehicleParameters vehicleParams, HeavyVehicleAttributesGraphStorage hgvStorage) { @@ -80,27 +92,19 @@ public HeavyVehicleEdgeFilter(int vehicleType, VehicleParameters vehicleParams, this.hasHazmat = false; } - ArrayList idx = new ArrayList<>(); - ArrayList idxl = new ArrayList<>(); - + List idx = new ArrayList<>(); for (int i = 0; i < VehicleDimensionRestrictions.COUNT; i++) { float value = vehicleAttrs[i]; if (value > 0) { idx.add(i); - idxl.add(i); } } - retValues = new double[5]; - this.restrictionValues = vehicleAttrs; this.indexValues = idx.toArray(new Integer[0]); - this.indexLocs = idxl.toArray(new Integer[0]); this.restCount = indexValues.length; this.vehicleType = vehicleType; - - this.gsHeavyVehicles = hgvStorage; } @Override @@ -109,38 +113,45 @@ public boolean accept(EdgeIteratorState iter) { if (!acceptVehicleType(iter)) return false; - if (hasHazmat && !(hazmatAccessEnc == null || iter.get(hazmatAccessEnc)) ) { + if (hasHazmat && !(hazmatAccessEnc == null || iter.get(hazmatAccessEnc))) { return false; } - int edgeId = EdgeIteratorStateHelper.getOriginalEdge(iter); - if (restCount != 0) { - if (restCount == 1) { - double value = gsHeavyVehicles.getEdgeRestrictionValue(edgeId, indexValues[0]); - return value <= 0 || value >= restrictionValues[indexLocs[0]]; - } else { - if (gsHeavyVehicles.getEdgeRestrictionValues(edgeId, retValues)) { - for (int i = 0; i < restCount; i++) { - double value = retValues[indexLocs[i]]; - if (value > 0.0f && value < restrictionValues[indexLocs[i]]) { - return false; - } - } - } + for (int i = 0; i < restCount; i++) { + double value = getEdgeRestrictionValue(iter, indexValues[i]); + if (value > 0.0f && value < restrictionValues[indexValues[i]]) { + return false; } } + return true; } + private double getEdgeRestrictionValue(EdgeIteratorState iter, int valueIndex) { + DecimalEncodedValue decimalEncodedValue = switch (valueIndex) { + case VehicleDimensionRestrictions.MAX_AXLE_LOAD -> maxAxleLoadEnc; + case VehicleDimensionRestrictions.MAX_HEIGHT -> maxHeightEnc; + case VehicleDimensionRestrictions.MAX_LENGTH -> maxLengthEnc; + case VehicleDimensionRestrictions.MAX_WEIGHT -> maxWeightEnc; + case VehicleDimensionRestrictions.MAX_WIDTH -> maxWidthEnc; + default -> null; + }; + if (decimalEncodedValue == null) { + throw new IllegalStateException("Encoded value for vehicle restriction not found."); + } + return iter.get(decimalEncodedValue); + } + private boolean acceptVehicleType(EdgeIteratorState edge) { - return switch (vehicleType) { - case HeavyVehicleAttributes.AGRICULTURE -> agriculturalAccessEnc == null || edge.get(agriculturalAccessEnc); - case HeavyVehicleAttributes.BUS -> busAccessEnc == null || edge.get(busAccessEnc); - case HeavyVehicleAttributes.DELIVERY -> deliveryAccessEnc == null || edge.get(deliveryAccessEnc); - case HeavyVehicleAttributes.FORESTRY -> forestryAccessEnc == null || edge.get(forestryAccessEnc); - case HeavyVehicleAttributes.GOODS -> goodsAccessEnc == null || edge.get(goodsAccessEnc); - case HeavyVehicleAttributes.HGV -> hgvAccessEnc == null || edge.get(hgvAccessEnc); - default -> true; + BooleanEncodedValue vehicleTypeAccessEnc = switch (vehicleType) { + case HeavyVehicleAttributes.AGRICULTURE -> agriculturalAccessEnc; + case HeavyVehicleAttributes.BUS -> busAccessEnc; + case HeavyVehicleAttributes.DELIVERY -> deliveryAccessEnc; + case HeavyVehicleAttributes.FORESTRY -> forestryAccessEnc; + case HeavyVehicleAttributes.GOODS -> goodsAccessEnc; + case HeavyVehicleAttributes.HGV -> hgvAccessEnc; + default -> null; }; + return vehicleTypeAccessEnc == null || edge.get(vehicleTypeAccessEnc); } } From b35cf5fd5ff8cf3d1633f9d4a1cc1ee84960041c Mon Sep 17 00:00:00 2001 From: aoles Date: Thu, 23 Oct 2025 00:10:10 +0200 Subject: [PATCH 4/9] refactor: switch from ext storage to enc values in hgv core edge filter --- .../core/HeavyVehicleCoreEdgeFilter.java | 55 ++++++++++++++++--- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/edgefilters/core/HeavyVehicleCoreEdgeFilter.java b/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/edgefilters/core/HeavyVehicleCoreEdgeFilter.java index 3af138316b..ebd7702617 100644 --- a/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/edgefilters/core/HeavyVehicleCoreEdgeFilter.java +++ b/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/edgefilters/core/HeavyVehicleCoreEdgeFilter.java @@ -13,25 +13,66 @@ */ package org.heigit.ors.routing.graphhopper.extensions.edgefilters.core; -import com.graphhopper.routing.querygraph.EdgeIteratorStateHelper; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EdgeFilter; +import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.storage.GraphHopperStorage; import com.graphhopper.util.EdgeIteratorState; -import org.heigit.ors.routing.graphhopper.extensions.storages.GraphStorageUtils; -import org.heigit.ors.routing.graphhopper.extensions.storages.HeavyVehicleAttributesGraphStorage; + +import java.util.ArrayList; +import java.util.List; public class HeavyVehicleCoreEdgeFilter implements EdgeFilter { - private final HeavyVehicleAttributesGraphStorage storage; + private static final List accessEncodedValues = new ArrayList<>(); + private static final List paramsEncodedValues = new ArrayList<>(); public HeavyVehicleCoreEdgeFilter(GraphHopperStorage graphStorage) { - storage = GraphStorageUtils.getGraphExtension(graphStorage, HeavyVehicleAttributesGraphStorage.class); + EncodingManager encodingManager = graphStorage.getEncodingManager(); + + // Boolean access restrictions + List.of( + AgriculturalAccess.KEY, + BusAccess.KEY, + DeliveryAccess.KEY, + ForestryAccess.KEY, + GoodsAccess.KEY, + HgvAccess.KEY, + HazmatAccess.KEY + ).forEach(key -> { + if (encodingManager.hasEncodedValue(key)) + accessEncodedValues.add(encodingManager.getBooleanEncodedValue(key)); + }); + + // Decimal parameter restrictions + List.of( + MaxAxleLoad.KEY, + MaxHeight.KEY, + MaxLength.KEY, + MaxWeight.KEY, + MaxWidth.KEY + ).forEach(key -> { + if (encodingManager.hasEncodedValue(key)) + paramsEncodedValues.add(encodingManager.getDecimalEncodedValue(key)); + }); } @Override public final boolean accept(EdgeIteratorState iter) { - return !storage.hasEdgeRestriction(EdgeIteratorStateHelper.getOriginalEdge(iter)); - + return !hasEdgeRestriction(iter); } + private boolean hasEdgeRestriction(EdgeIteratorState iter) { + for (BooleanEncodedValue encodedValue : accessEncodedValues) { + if (iter.get(encodedValue)) { + return true; + } + } + for (DecimalEncodedValue encodedValue : paramsEncodedValues) { + if (!Double.isInfinite(iter.get(encodedValue))) { + return true; + } + } + return false; + } } From c8d5682c5d8bf1d5d3687f7792fa7e9b57734a3a Mon Sep 17 00:00:00 2001 From: aoles Date: Fri, 24 Oct 2025 01:13:52 +0200 Subject: [PATCH 5/9] refactor: simplify code of hgv edge filter --- .../edgefilters/HeavyVehicleEdgeFilter.java | 144 +++++------------- .../routing/parameters/VehicleParameters.java | 69 +++------ 2 files changed, 63 insertions(+), 150 deletions(-) diff --git a/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/edgefilters/HeavyVehicleEdgeFilter.java b/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/edgefilters/HeavyVehicleEdgeFilter.java index b21e750def..659a146402 100644 --- a/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/edgefilters/HeavyVehicleEdgeFilter.java +++ b/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/edgefilters/HeavyVehicleEdgeFilter.java @@ -18,93 +18,50 @@ import com.graphhopper.storage.GraphHopperStorage; import com.graphhopper.util.EdgeIteratorState; import org.heigit.ors.routing.graphhopper.extensions.HeavyVehicleAttributes; -import org.heigit.ors.routing.graphhopper.extensions.VehicleDimensionRestrictions; import org.heigit.ors.routing.graphhopper.extensions.VehicleLoadCharacteristicsFlags; -import org.heigit.ors.routing.graphhopper.extensions.storages.GraphStorageUtils; -import org.heigit.ors.routing.graphhopper.extensions.storages.HeavyVehicleAttributesGraphStorage; import org.heigit.ors.routing.parameters.VehicleParameters; -import java.util.ArrayList; -import java.util.List; +import java.util.HashMap; +import java.util.Map; public class HeavyVehicleEdgeFilter implements EdgeFilter { - private final int vehicleType; - private final boolean hasHazmat; - private final float[] restrictionValues; - private final Integer[] indexValues; - private final int restCount; - - private BooleanEncodedValue agriculturalAccessEnc = null; - private BooleanEncodedValue busAccessEnc = null; - private BooleanEncodedValue deliveryAccessEnc = null; - private BooleanEncodedValue forestryAccessEnc = null; - private BooleanEncodedValue goodsAccessEnc = null; - private BooleanEncodedValue hgvAccessEnc = null; + Map vehicleRestrictions = new HashMap<>(); + private BooleanEncodedValue vehicleAccessEnc = null; private BooleanEncodedValue hazmatAccessEnc = null; - private DecimalEncodedValue maxAxleLoadEnc = null; - private DecimalEncodedValue maxHeightEnc = null; - private DecimalEncodedValue maxLengthEnc = null; - private DecimalEncodedValue maxWeightEnc = null; - private DecimalEncodedValue maxWidthEnc = null; - + private boolean hasHazmat = false; public HeavyVehicleEdgeFilter(int vehicleType, VehicleParameters vehicleParams, GraphHopperStorage graphStorage) { - this(vehicleType, vehicleParams, GraphStorageUtils.getGraphExtension(graphStorage, HeavyVehicleAttributesGraphStorage.class)); var encodingManager = graphStorage.getEncodingManager(); - if (encodingManager.hasEncodedValue(AgriculturalAccess.KEY)) - agriculturalAccessEnc = encodingManager.getBooleanEncodedValue(AgriculturalAccess.KEY); - if (encodingManager.hasEncodedValue(BusAccess.KEY)) - busAccessEnc = encodingManager.getBooleanEncodedValue(BusAccess.KEY); - if (encodingManager.hasEncodedValue(DeliveryAccess.KEY)) - deliveryAccessEnc = encodingManager.getBooleanEncodedValue(DeliveryAccess.KEY); - if (encodingManager.hasEncodedValue(ForestryAccess.KEY)) - forestryAccessEnc = encodingManager.getBooleanEncodedValue(ForestryAccess.KEY); - if (encodingManager.hasEncodedValue(GoodsAccess.KEY)) - goodsAccessEnc = encodingManager.getBooleanEncodedValue(GoodsAccess.KEY); - if (encodingManager.hasEncodedValue(HgvAccess.KEY)) - hgvAccessEnc = encodingManager.getBooleanEncodedValue(HgvAccess.KEY); - if (encodingManager.hasEncodedValue(HazmatAccess.KEY)) - hazmatAccessEnc = encodingManager.getBooleanEncodedValue(HazmatAccess.KEY); - if (encodingManager.hasEncodedValue(MaxAxleLoad.KEY)) - maxAxleLoadEnc = encodingManager.getDecimalEncodedValue(MaxAxleLoad.KEY); - if (encodingManager.hasEncodedValue(MaxHeight.KEY)) - maxHeightEnc = encodingManager.getDecimalEncodedValue(MaxHeight.KEY); - if (encodingManager.hasEncodedValue(MaxLength.KEY)) - maxLengthEnc = encodingManager.getDecimalEncodedValue(MaxLength.KEY); - if (encodingManager.hasEncodedValue(MaxWeight.KEY)) - maxWeightEnc = encodingManager.getDecimalEncodedValue(MaxWeight.KEY); - if (encodingManager.hasEncodedValue(MaxWidth.KEY)) - maxWidthEnc = encodingManager.getDecimalEncodedValue(MaxWidth.KEY); - } - - public HeavyVehicleEdgeFilter(int vehicleType, VehicleParameters vehicleParams, HeavyVehicleAttributesGraphStorage hgvStorage) { - float[] vehicleAttrs = new float[VehicleDimensionRestrictions.COUNT]; - if (vehicleParams != null) { - this.hasHazmat = VehicleLoadCharacteristicsFlags.isSet(vehicleParams.getLoadCharacteristics(), VehicleLoadCharacteristicsFlags.HAZMAT); - - vehicleAttrs[VehicleDimensionRestrictions.MAX_HEIGHT] = (float) vehicleParams.getHeight(); - vehicleAttrs[VehicleDimensionRestrictions.MAX_WIDTH] = (float) vehicleParams.getWidth(); - vehicleAttrs[VehicleDimensionRestrictions.MAX_WEIGHT] = (float) vehicleParams.getWeight(); - vehicleAttrs[VehicleDimensionRestrictions.MAX_LENGTH] = (float) vehicleParams.getLength(); - vehicleAttrs[VehicleDimensionRestrictions.MAX_AXLE_LOAD] = (float) vehicleParams.getAxleload(); - } else { - this.hasHazmat = false; - } + String vehicleAccessKey = switch (vehicleType) { + case HeavyVehicleAttributes.AGRICULTURE -> AgriculturalAccess.KEY; + case HeavyVehicleAttributes.BUS -> BusAccess.KEY; + case HeavyVehicleAttributes.DELIVERY -> DeliveryAccess.KEY; + case HeavyVehicleAttributes.FORESTRY -> ForestryAccess.KEY; + case HeavyVehicleAttributes.GOODS -> GoodsAccess.KEY; + case HeavyVehicleAttributes.HGV -> HgvAccess.KEY; + default -> throw new IllegalArgumentException("Unsupported vehicle type for HeavyVehicleEdgeFilter."); + }; + if (encodingManager.hasEncodedValue(vehicleAccessKey)) + vehicleAccessEnc = encodingManager.getBooleanEncodedValue(vehicleAccessKey); - List idx = new ArrayList<>(); - for (int i = 0; i < VehicleDimensionRestrictions.COUNT; i++) { - float value = vehicleAttrs[i]; - if (value > 0) { - idx.add(i); - } - } + if (vehicleParams == null || !vehicleParams.hasAttributes()) + return; - this.restrictionValues = vehicleAttrs; - this.indexValues = idx.toArray(new Integer[0]); - this.restCount = indexValues.length; + hasHazmat = VehicleLoadCharacteristicsFlags.isSet(vehicleParams.getLoadCharacteristics(), VehicleLoadCharacteristicsFlags.HAZMAT); + if (encodingManager.hasEncodedValue(HazmatAccess.KEY)) + hazmatAccessEnc = encodingManager.getBooleanEncodedValue(HazmatAccess.KEY); - this.vehicleType = vehicleType; + if (vehicleParams.hasAxleload() && encodingManager.hasEncodedValue(HazmatAccess.KEY)) + vehicleRestrictions.put(encodingManager.getDecimalEncodedValue(MaxAxleLoad.KEY), vehicleParams.getAxleload()); + if (vehicleParams.hasHeight() && encodingManager.hasEncodedValue(MaxHeight.KEY)) + vehicleRestrictions.put(encodingManager.getDecimalEncodedValue(MaxHeight.KEY), vehicleParams.getHeight()); + if (vehicleParams.hasLength() && encodingManager.hasEncodedValue(MaxLength.KEY)) + vehicleRestrictions.put(encodingManager.getDecimalEncodedValue(MaxLength.KEY), vehicleParams.getLength()); + if (vehicleParams.hasWeight() && encodingManager.hasEncodedValue(MaxWeight.KEY)) + vehicleRestrictions.put(encodingManager.getDecimalEncodedValue(MaxWeight.KEY), vehicleParams.getWeight()); + if (vehicleParams.hasWidth() && encodingManager.hasEncodedValue(MaxWidth.KEY)) + vehicleRestrictions.put(encodingManager.getDecimalEncodedValue(MaxWidth.KEY), vehicleParams.getWidth()); } @Override @@ -112,46 +69,21 @@ public boolean accept(EdgeIteratorState iter) { // test access restrictions for the given vehicle type if (!acceptVehicleType(iter)) return false; - - if (hasHazmat && !(hazmatAccessEnc == null || iter.get(hazmatAccessEnc))) { + // test hazmat restriction + if (hasHazmat && hazmatAccessEnc != null && !iter.get(hazmatAccessEnc)) { return false; } - - for (int i = 0; i < restCount; i++) { - double value = getEdgeRestrictionValue(iter, indexValues[i]); - if (value > 0.0f && value < restrictionValues[indexValues[i]]) { + // test dimension restrictions + for (Map.Entry entry : vehicleRestrictions.entrySet()) { + double value = iter.get(entry.getKey()); + if (value > 0.0 && value < entry.getValue()) { return false; } } - return true; } - private double getEdgeRestrictionValue(EdgeIteratorState iter, int valueIndex) { - DecimalEncodedValue decimalEncodedValue = switch (valueIndex) { - case VehicleDimensionRestrictions.MAX_AXLE_LOAD -> maxAxleLoadEnc; - case VehicleDimensionRestrictions.MAX_HEIGHT -> maxHeightEnc; - case VehicleDimensionRestrictions.MAX_LENGTH -> maxLengthEnc; - case VehicleDimensionRestrictions.MAX_WEIGHT -> maxWeightEnc; - case VehicleDimensionRestrictions.MAX_WIDTH -> maxWidthEnc; - default -> null; - }; - if (decimalEncodedValue == null) { - throw new IllegalStateException("Encoded value for vehicle restriction not found."); - } - return iter.get(decimalEncodedValue); - } - private boolean acceptVehicleType(EdgeIteratorState edge) { - BooleanEncodedValue vehicleTypeAccessEnc = switch (vehicleType) { - case HeavyVehicleAttributes.AGRICULTURE -> agriculturalAccessEnc; - case HeavyVehicleAttributes.BUS -> busAccessEnc; - case HeavyVehicleAttributes.DELIVERY -> deliveryAccessEnc; - case HeavyVehicleAttributes.FORESTRY -> forestryAccessEnc; - case HeavyVehicleAttributes.GOODS -> goodsAccessEnc; - case HeavyVehicleAttributes.HGV -> hgvAccessEnc; - default -> null; - }; - return vehicleTypeAccessEnc == null || edge.get(vehicleTypeAccessEnc); + return vehicleAccessEnc == null || edge.get(vehicleAccessEnc); } } diff --git a/ors-engine/src/main/java/org/heigit/ors/routing/parameters/VehicleParameters.java b/ors-engine/src/main/java/org/heigit/ors/routing/parameters/VehicleParameters.java index fba1d5d0ec..bf32e8dad1 100644 --- a/ors-engine/src/main/java/org/heigit/ors/routing/parameters/VehicleParameters.java +++ b/ors-engine/src/main/java/org/heigit/ors/routing/parameters/VehicleParameters.java @@ -13,68 +13,49 @@ */ package org.heigit.ors.routing.parameters; +import lombok.Getter; +import lombok.Setter; import org.heigit.ors.routing.graphhopper.extensions.VehicleLoadCharacteristicsFlags; import java.util.List; +@Setter +@Getter public class VehicleParameters extends ProfileParameters { - private double length = 0.0; - private double height = 0.0; - private double width = 0.0; - private double weight = 0.0; - private double axleload = 0.0; - private int characteristics = VehicleLoadCharacteristicsFlags.NONE; - - public double getLength() { - return length; - } - - public void setLength(double length) { - this.length = length; - } - - public double getHeight() { - return height; - } - - public void setHeight(double height) { - this.height = height; - } - - public double getWidth() { - return width; - } - - public void setWidth(double width) { - this.width = width; - } - - public double getWeight() { - return weight; + public static final double UNSET_VALUE = 0.0; + private double length = UNSET_VALUE; + private double height = UNSET_VALUE; + private double width = UNSET_VALUE; + private double weight = UNSET_VALUE; + private double axleload = UNSET_VALUE; + private int loadCharacteristics = VehicleLoadCharacteristicsFlags.NONE; + + public boolean hasLength() { + return length > UNSET_VALUE; } - public void setWeight(double weight) { - this.weight = weight; + public boolean hasHeight() { + return height > UNSET_VALUE; } - public double getAxleload() { - return axleload; + public boolean hasWidth() { + return width > UNSET_VALUE; } - public void setAxleload(double axleload) { - this.axleload = axleload; + public boolean hasWeight() { + return weight > UNSET_VALUE; } - public int getLoadCharacteristics() { - return characteristics; + public boolean hasAxleload() { + return axleload > UNSET_VALUE; } - public void setLoadCharacteristics(int characteristics) { - this.characteristics = characteristics; + public boolean hasLoadCharacteristics() { + return loadCharacteristics != VehicleLoadCharacteristicsFlags.NONE; } public boolean hasAttributes() { - return height > 0.0 || length > 0.0 || width > 0.0 || weight > 0.0 || axleload > 0.0 || characteristics != 0; + return hasLength() || hasHeight() || hasWidth() || hasWeight() || hasAxleload() || hasLoadCharacteristics(); } @Override From 1cfcfe23ebacdc4cc8915e84bb1f21f8b540d955 Mon Sep 17 00:00:00 2001 From: aoles Date: Fri, 24 Oct 2025 01:26:01 +0200 Subject: [PATCH 6/9] refactor: prune hgv graph storage classes --- .../VehicleDimensionRestrictions.java | 26 -- .../HeavyVehicleAttributesGraphStorage.java | 177 ------------- .../HeavyVehicleGraphStorageBuilder.java | 244 ------------------ ...ions.storages.builders.GraphStorageBuilder | 1 - 4 files changed, 448 deletions(-) delete mode 100644 ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/VehicleDimensionRestrictions.java delete mode 100644 ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/storages/HeavyVehicleAttributesGraphStorage.java delete mode 100644 ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/storages/builders/HeavyVehicleGraphStorageBuilder.java diff --git a/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/VehicleDimensionRestrictions.java b/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/VehicleDimensionRestrictions.java deleted file mode 100644 index 511bbab3a1..0000000000 --- a/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/VehicleDimensionRestrictions.java +++ /dev/null @@ -1,26 +0,0 @@ -/* 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 . - */ -package org.heigit.ors.routing.graphhopper.extensions; - -public class VehicleDimensionRestrictions { - public static final int MAX_HEIGHT = 0; - public static final int MAX_WEIGHT = 1; - public static final int MAX_WIDTH = 2; - public static final int MAX_LENGTH = 3; - public static final int MAX_AXLE_LOAD = 4; - public static final int COUNT = 5; - - private VehicleDimensionRestrictions() { - } -} diff --git a/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/storages/HeavyVehicleAttributesGraphStorage.java b/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/storages/HeavyVehicleAttributesGraphStorage.java deleted file mode 100644 index b086329bd3..0000000000 --- a/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/storages/HeavyVehicleAttributesGraphStorage.java +++ /dev/null @@ -1,177 +0,0 @@ -/* 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 . - */ -package org.heigit.ors.routing.graphhopper.extensions.storages; - -import com.graphhopper.storage.DataAccess; -import com.graphhopper.storage.Directory; -import com.graphhopper.storage.Graph; -import com.graphhopper.storage.GraphExtension; -import com.graphhopper.util.BitUtil; -import org.heigit.ors.routing.graphhopper.extensions.VehicleDimensionRestrictions; - -public class HeavyVehicleAttributesGraphStorage implements GraphExtension { - private static final int EF_RESTRICTION_BYTES = 2; - private static final String MSG_EF_RESTRICTION_IS_NOT_SUPPORTED = "EF_RESTRICTION is not supported."; - - private final int efVehicleType; - private final int efRestrictions; - - private DataAccess orsEdges; - protected int edgeEntryIndex = 0; - protected int edgeEntryBytes; - protected int edgesCount; - - private static final double FACTOR = 100.0; - - public HeavyVehicleAttributesGraphStorage(boolean includeRestrictions) { - efVehicleType = nextBlockEntryIndex(1); - - if (includeRestrictions) - // first byte indicates whether any restrictions are given - efRestrictions = nextBlockEntryIndex(VehicleDimensionRestrictions.COUNT * EF_RESTRICTION_BYTES); - else - efRestrictions = -1; - - edgeEntryBytes = edgeEntryIndex; - edgesCount = 0; - } - - public void init(Graph graph, Directory dir) { - if (edgesCount > 0) - throw new AssertionError("The ext_hgv storage must be initialized only once."); - - this.orsEdges = dir.create("ext_hgv"); - } - - private int nextBlockEntryIndex(int size) { - int res = edgeEntryIndex; - edgeEntryIndex += size; - return res; - } - - public HeavyVehicleAttributesGraphStorage create(long initBytes) { - orsEdges.create(initBytes * edgeEntryBytes); - return this; - } - - public void flush() { - orsEdges.setHeader(0, edgeEntryBytes); - orsEdges.setHeader(4, edgesCount); - orsEdges.flush(); - } - - public void close() { - orsEdges.close(); - } - - public int entries() { - return edgesCount; - } - - public boolean loadExisting() { - if (!orsEdges.loadExisting()) - throw new IllegalStateException("Unable to load storage 'ext_hgv'. corrupt file or directory? "); - - edgeEntryBytes = orsEdges.getHeader(0); - edgesCount = orsEdges.getHeader(4); - return true; - } - - private void ensureEdgesIndex(int edgeIndex) { - orsEdges.ensureCapacity(((long) edgeIndex + 1) * edgeEntryBytes); - } - - public void setEdgeValue(int edgeId, int vehicleType, double[] restrictionValues) { - edgesCount++; - ensureEdgesIndex(edgeId); - - long edgePointer = (long) edgeId * edgeEntryBytes; - - orsEdges.setByte(edgePointer + efVehicleType, (byte) vehicleType); - - if (efRestrictions == -1) - throw new IllegalStateException(MSG_EF_RESTRICTION_IS_NOT_SUPPORTED); - - byte[] buffer = new byte[2]; - for (int i = 0; i < VehicleDimensionRestrictions.COUNT; i++) { - short shortValue = (short) (restrictionValues[i] * FACTOR); - BitUtil.LITTLE.fromShort(buffer, shortValue); - orsEdges.setBytes(edgePointer + efRestrictions + i * EF_RESTRICTION_BYTES, buffer, 2); - } - } - - public double getEdgeRestrictionValue(int edgeId, int valueIndex) { - long edgeBase = (long) edgeId * edgeEntryBytes; - - if (efRestrictions == -1) - throw new IllegalStateException(MSG_EF_RESTRICTION_IS_NOT_SUPPORTED); - - return getShort(edgeBase + efRestrictions + (long) valueIndex * EF_RESTRICTION_BYTES) / FACTOR; - } - - public boolean getEdgeRestrictionValues(int edgeId, double[] retValues) { - long edgeBase = (long) edgeId * edgeEntryBytes; - - if (efRestrictions == -1) - throw new IllegalStateException(MSG_EF_RESTRICTION_IS_NOT_SUPPORTED); - - for (int i = 0; i < VehicleDimensionRestrictions.COUNT; i++) - retValues[i] = getShort(edgeBase + efRestrictions + i * EF_RESTRICTION_BYTES) / FACTOR; - - return true; - } - - public int getEdgeVehicleType(int edgeId) { - long edgeBase = (long) edgeId * edgeEntryBytes; - - int result = orsEdges.getByte(edgeBase + efVehicleType); - if (result < 0) - result = result & 0xFF; - - return result; - } - - public boolean hasEdgeRestriction(int edgeId) { - long edgeBase = (long) edgeId * edgeEntryBytes; - - byte[] buffer = new byte[2]; - orsEdges.getBytes(edgeBase + efVehicleType, buffer, 2); - - if (buffer[0] != 0 || buffer[1] != 0) - return true; - - if (efRestrictions > 0) - for (int i = 0; i < VehicleDimensionRestrictions.COUNT; i++) - if (getShort(edgeBase + efRestrictions + i * EF_RESTRICTION_BYTES) != 0) - return true; - - return false; - } - - private short getShort(long bytePos) { - byte[] buffer = new byte[2]; - orsEdges.getBytes(bytePos, buffer, 2); - return BitUtil.LITTLE.toShort(buffer); - } - - @Override - public long getCapacity() { - return orsEdges.getCapacity(); - } - - @Override - public boolean isClosed() { - return false; - } -} diff --git a/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/storages/builders/HeavyVehicleGraphStorageBuilder.java b/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/storages/builders/HeavyVehicleGraphStorageBuilder.java deleted file mode 100644 index 35aa37a543..0000000000 --- a/ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/storages/builders/HeavyVehicleGraphStorageBuilder.java +++ /dev/null @@ -1,244 +0,0 @@ -/* 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 . - */ -package org.heigit.ors.routing.graphhopper.extensions.storages.builders; - -import com.graphhopper.GraphHopper; -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.storage.GraphExtension; -import com.graphhopper.util.EdgeIteratorState; -import org.heigit.ors.config.profile.ExtendedStorageProperties; -import org.heigit.ors.routing.graphhopper.extensions.HeavyVehicleAttributes; -import org.heigit.ors.routing.graphhopper.extensions.VehicleDimensionRestrictions; -import org.heigit.ors.routing.graphhopper.extensions.storages.HeavyVehicleAttributesGraphStorage; - -import java.util.*; -import java.util.Map.Entry; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class HeavyVehicleGraphStorageBuilder extends AbstractGraphStorageBuilder { - private boolean includeRestrictions = true; - private HeavyVehicleAttributesGraphStorage storage; - private int hgvType = 0; - private boolean hasRestrictionValues; - private final double[] restrictionValues = new double[VehicleDimensionRestrictions.COUNT]; - private final List motorVehicleRestrictions = new ArrayList<>(5); - private final Set motorVehicleRestrictedValues = new HashSet<>(5); - private final Set motorVehicleHgvValues = new HashSet<>(6); - - private final Set noValues = new HashSet<>(5); - private final Set yesValues = new HashSet<>(5); - private final Pattern patternDimension; - private final Pattern patternHazmat; - - public HeavyVehicleGraphStorageBuilder() { - motorVehicleRestrictions.addAll(Arrays.asList("motorcar", "motor_vehicle", "vehicle", "access")); - - motorVehicleRestrictedValues.add("private"); - motorVehicleRestrictedValues.add("no"); - motorVehicleRestrictedValues.add("restricted"); - motorVehicleRestrictedValues.add("military"); - - motorVehicleHgvValues.addAll(Arrays.asList("hgv", "goods", "bus", "agricultural", "forestry", "delivery")); - - noValues.addAll(Arrays.asList("no", "private")); - yesValues.addAll(Arrays.asList("yes", "designated")); - - patternDimension = Pattern.compile("(?:\\s*(\\d+)\\s*(?:feet|ft\\.|ft|'))?(?:(\\d+)\\s*(?:inches|in\\.|in|''|\"))?"); - patternHazmat = Pattern.compile("^hazmat(:[B-E])?$"); - } - - public GraphExtension init(GraphHopper graphhopper) throws Exception { - if (storage != null) - throw new Exception("GraphStorageBuilder has been already initialized."); - - ExtendedStorageProperties parameters; - parameters = this.parameters; - - if (parameters.getRestrictions() != null) { - includeRestrictions = parameters.getRestrictions(); - } - - storage = new HeavyVehicleAttributesGraphStorage(includeRestrictions); - - return storage; - } - - public void processWay(ReaderWay way) { - // reset values - hgvType = 0; - - if (hasRestrictionValues) { - restrictionValues[0] = 0.0; - restrictionValues[1] = 0.0; - restrictionValues[2] = 0.0; - restrictionValues[3] = 0.0; - restrictionValues[4] = 0.0; - hasRestrictionValues = false; - } - - boolean hasHighway = way.hasTag("highway"); - - if (hasHighway) { - // process motor vehicle restrictions before any more specific vehicle type tags which override the former - - // if there are any generic motor vehicle restrictions restrict all types... - if (way.hasTag(motorVehicleRestrictions, motorVehicleRestrictedValues)) - hgvType = HeavyVehicleAttributes.ANY; - - //...or all but the explicitly listed ones - if (way.hasTag(motorVehicleRestrictions, motorVehicleHgvValues)) { - int flag = 0; - for (String key : motorVehicleRestrictions) { - String[] values = way.getTagValues(key); - for (String val : values) { - if (motorVehicleHgvValues.contains(val)) - flag |= HeavyVehicleAttributes.getFromString(val); - } - } - hgvType = HeavyVehicleAttributes.ANY & ~flag; - } - - Iterator> it = way.getProperties(); - - while (it.hasNext()) { - Map.Entry pairs = it.next(); - String key = pairs.getKey(); - String value = pairs.getValue().toString(); - - /* - * https://wiki.openstreetmap.org/wiki/Restrictions - */ - - int valueIndex = -1; - - switch (key) { - case "maxheight" -> valueIndex = VehicleDimensionRestrictions.MAX_HEIGHT; - case "maxweight", "maxweight:hgv" -> valueIndex = VehicleDimensionRestrictions.MAX_WEIGHT; - case "maxwidth" -> valueIndex = VehicleDimensionRestrictions.MAX_WIDTH; - case "maxlength", "maxlength:hgv" -> valueIndex = VehicleDimensionRestrictions.MAX_LENGTH; - case "maxaxleload" -> valueIndex = VehicleDimensionRestrictions.MAX_AXLE_LOAD; - default -> { - } - } - - // given tag is a weight/dimension restriction - if (valueIndex >= 0 && includeRestrictions && !("none".equals(value) || "default".equals(value))) { - double parsedValue = -1; - - // sanitize decimal separators - if (value.contains(",")) - value = value.replace(',', '.'); - - // weight restrictions - if (valueIndex == VehicleDimensionRestrictions.MAX_WEIGHT || valueIndex == VehicleDimensionRestrictions.MAX_AXLE_LOAD) { - if (value.contains("t")) { - value = value.replace('t', ' '); - } else if (value.contains("lbs")) { - value = value.replace("lbs", " "); - parsedValue = parseDouble(value) / 2204.622; - } - } - - // dimension restrictions - else { - if (value.contains("m")) { - value = value.replace('m', ' '); - } else { - Matcher m = patternDimension.matcher(value); - if (m.matches() && m.lookingAt()) { - double feet = parseDouble(m.group(1)); - double inches = 0; - if (m.groupCount() > 1 && m.group(2) != null) { - inches = parseDouble(m.group(2)); - } - parsedValue = feet * 0.3048 + inches * 0.0254; - } - } - } - - if (parsedValue == -1) - parsedValue = parseDouble(value); - - // it was possible to extract a reasonable value - if (parsedValue > 0) { - restrictionValues[valueIndex] = parsedValue; - hasRestrictionValues = true; - } - } - - if (motorVehicleHgvValues.contains(key)) { - //TODO: account for :[forward/backward] keys - //TODO: allow access: as described in #703. Might be necessary to adjust the upstream PBF parsing part as well. - String vehicleType = getVehicleType(key, value); - String accessValue = getVehicleAccess(vehicleType, value); - setAccessFlags(vehicleType, accessValue); - if (vehicleType.equals(value))// e.g. hgv=delivery implies that hgv other than delivery vehicles are blocked - setAccessFlags(key, "no"); - } else if (patternHazmat.matcher(key).matches() && "no".equals(value)) { - hgvType |= HeavyVehicleAttributes.HAZMAT; - } - } - } - } - - public void processEdge(ReaderWay way, EdgeIteratorState edge) { - storage.setEdgeValue(edge.getEdge(), hgvType, restrictionValues); - } - - private String getVehicleType(String key, String value) { - return motorVehicleHgvValues.contains(value) ? value : key;// hgv=[delivery/agricultural/forestry] - } - - private String getVehicleAccess(String vehicleType, String value) { - if (vehicleType.equals(value) || yesValues.contains(value)) - return "yes"; - else if (noValues.contains(value)) - return "no"; - - return null; - } - - /** - * Toggle on/off the bit corresponding to a given hgv type defined by {@code flag} inside binary restriction masks - * based on the value of {@code tag}. "no" sets the bit in {@code _hgvType}, while "yes" unsets it. - * - * @param vehicle a String describing one of the vehicle types defined in {@code HeavyVehicleAttributes} - * @param access a String describing the access restriction - */ - private void setAccessFlags(String vehicle, String access) { - int flag = HeavyVehicleAttributes.getFromString(vehicle); - if (access != null) { - if ("no".equals(access)) - hgvType |= flag; - else if ("yes".equals(access)) - hgvType &= ~flag; - } - } - - private double parseDouble(String str) { - double d; - try { - d = Double.parseDouble(str); - } catch (NumberFormatException e) { - d = 0.0; - } - return d; - } - - @Override - public String getName() { - return "HeavyVehicle"; - } -} diff --git a/ors-engine/src/main/resources/META-INF/services/org.heigit.ors.routing.graphhopper.extensions.storages.builders.GraphStorageBuilder b/ors-engine/src/main/resources/META-INF/services/org.heigit.ors.routing.graphhopper.extensions.storages.builders.GraphStorageBuilder index 7dfaeabaf2..572c386b29 100644 --- a/ors-engine/src/main/resources/META-INF/services/org.heigit.ors.routing.graphhopper.extensions.storages.builders.GraphStorageBuilder +++ b/ors-engine/src/main/resources/META-INF/services/org.heigit.ors.routing.graphhopper.extensions.storages.builders.GraphStorageBuilder @@ -1,4 +1,3 @@ -org.heigit.ors.routing.graphhopper.extensions.storages.builders.HeavyVehicleGraphStorageBuilder org.heigit.ors.routing.graphhopper.extensions.storages.builders.HereTrafficGraphStorageBuilder org.heigit.ors.routing.graphhopper.extensions.storages.builders.HillIndexGraphStorageBuilder org.heigit.ors.routing.graphhopper.extensions.storages.builders.WheelchairGraphStorageBuilder From bc234fd2049ae89827470dbb4d0363f275122aa6 Mon Sep 17 00:00:00 2001 From: aoles Date: Fri, 24 Oct 2025 14:08:07 +0200 Subject: [PATCH 7/9] refactor: more concise code of `EncodedValuesProperties` for the sake of maintainability --- .../profile/EncodedValuesProperties.java | 131 +++++++----------- 1 file changed, 51 insertions(+), 80 deletions(-) diff --git a/ors-engine/src/main/java/org/heigit/ors/config/profile/EncodedValuesProperties.java b/ors-engine/src/main/java/org/heigit/ors/config/profile/EncodedValuesProperties.java index 5d9435ad4d..d90e798efb 100644 --- a/ors-engine/src/main/java/org/heigit/ors/config/profile/EncodedValuesProperties.java +++ b/ors-engine/src/main/java/org/heigit/ors/config/profile/EncodedValuesProperties.java @@ -8,8 +8,10 @@ import lombok.Getter; import lombok.Setter; -import java.util.ArrayList; -import java.util.List; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; import static java.util.Optional.ofNullable; @@ -17,39 +19,39 @@ @Setter @JsonInclude(JsonInclude.Include.NON_NULL) public class EncodedValuesProperties { - @JsonProperty("ford") + @JsonProperty(Ford.KEY) private Boolean ford; - @JsonProperty("highway") + @JsonProperty(Highway.KEY) private Boolean highway; - @JsonProperty("osm_way_id") + @JsonProperty(OsmWayId.KEY) private Boolean osmWayId; - @JsonProperty("way_surface") + @JsonProperty(WaySurface.KEY) private Boolean waySurface; - @JsonProperty("way_type") + @JsonProperty(WayType.KEY) private Boolean wayType; - @JsonProperty("agricultural_access") + @JsonProperty(AgriculturalAccess.KEY) private Boolean agriculturalAccess; - @JsonProperty("bus_access") + @JsonProperty(BusAccess.KEY) private Boolean busAccess; - @JsonProperty("delivery_access") + @JsonProperty(DeliveryAccess.KEY) private Boolean deliveryAccess; - @JsonProperty("forestry_access") + @JsonProperty(ForestryAccess.KEY) private Boolean forestryAccess; - @JsonProperty("goods_access") + @JsonProperty(GoodsAccess.KEY) private Boolean goodsAccess; - @JsonProperty("hgv_access") + @JsonProperty(HgvAccess.KEY) private Boolean hgvAccess; - @JsonProperty("hazmat_access") + @JsonProperty(HazmatAccess.KEY) private Boolean hazmatAccess; - @JsonProperty("max_axle_load") + @JsonProperty(MaxAxleLoad.KEY) private Boolean maxAxleLoad; - @JsonProperty("max_height") + @JsonProperty(MaxHeight.KEY) private Boolean maxHeight; - @JsonProperty("max_length") + @JsonProperty(MaxLength.KEY) private Boolean maxLength; - @JsonProperty("max_weight") + @JsonProperty(MaxWeight.KEY) private Boolean maxWeight; - @JsonProperty("max_width") + @JsonProperty(MaxWidth.KEY) private Boolean maxWidth; public EncodedValuesProperties() { @@ -59,71 +61,42 @@ public EncodedValuesProperties(String ignored) { // This constructor is used to create an empty object for the purpose of ignoring it in the JSON serialization. } + @JsonIgnore + private Map getProperties() { + Map properties = new HashMap<>(); + + properties.put(Ford.KEY, ford); + properties.put(Highway.KEY, highway); + properties.put(OsmWayId.KEY, osmWayId); + properties.put(WaySurface.KEY, waySurface); + properties.put(WayType.KEY, wayType); + properties.put(AgriculturalAccess.KEY, agriculturalAccess); + properties.put(BusAccess.KEY, busAccess); + properties.put(DeliveryAccess.KEY, deliveryAccess); + properties.put(ForestryAccess.KEY, forestryAccess); + properties.put(GoodsAccess.KEY, goodsAccess); + properties.put(HgvAccess.KEY, hgvAccess); + properties.put(HazmatAccess.KEY, hazmatAccess); + properties.put(MaxAxleLoad.KEY, maxAxleLoad); + properties.put(MaxHeight.KEY, maxHeight); + properties.put(MaxLength.KEY, maxLength); + properties.put(MaxWeight.KEY, maxWeight); + properties.put(MaxWidth.KEY, maxWidth); + + return properties; + } + @JsonIgnore public boolean isEmpty() { - return osmWayId == null && - ford == null && highway == null && - waySurface == null && wayType == null && - agriculturalAccess == null && busAccess == null && deliveryAccess == null && forestryAccess == null && goodsAccess == null && hgvAccess == null && - hazmatAccess == null && - maxAxleLoad == null && maxHeight == null && maxLength == null && maxWeight == null && maxWidth == null; + return getProperties().values().stream().allMatch(Objects::isNull); } @JsonIgnore public String toString() { - List out = new ArrayList<>(); - if (Boolean.TRUE.equals(ford)) { - out.add(Ford.KEY); - } - if (Boolean.TRUE.equals(highway)) { - out.add(Highway.KEY); - } - if (Boolean.TRUE.equals(osmWayId)) { - out.add(OsmWayId.KEY); - } - if (Boolean.TRUE.equals(waySurface)) { - out.add(WaySurface.KEY); - } - if (Boolean.TRUE.equals(wayType)) { - out.add(WayType.KEY); - } - if (Boolean.TRUE.equals(agriculturalAccess)) { - out.add(AgriculturalAccess.KEY); - } - if (Boolean.TRUE.equals(busAccess)) { - out.add(BusAccess.KEY); - } - if (Boolean.TRUE.equals(deliveryAccess)) { - out.add(DeliveryAccess.KEY); - } - if (Boolean.TRUE.equals(forestryAccess)) { - out.add(ForestryAccess.KEY); - } - if (Boolean.TRUE.equals(goodsAccess)) { - out.add(GoodsAccess.KEY); - } - if (Boolean.TRUE.equals(hgvAccess)) { - out.add(HgvAccess.KEY); - } - if (Boolean.TRUE.equals(hazmatAccess)) { - out.add(HazmatAccess.KEY); - } - if (Boolean.TRUE.equals(maxAxleLoad)) { - out.add(MaxAxleLoad.KEY); - } - if (Boolean.TRUE.equals(maxHeight)) { - out.add(MaxHeight.KEY); - } - if (Boolean.TRUE.equals(maxLength)) { - out.add(MaxLength.KEY); - } - if (Boolean.TRUE.equals(maxWeight)) { - out.add(MaxWeight.KEY); - } - if (Boolean.TRUE.equals(maxWidth)) { - out.add(MaxWidth.KEY); - } - return String.join(",", out); + return getProperties().entrySet().stream() + .filter(e -> Boolean.TRUE.equals(e.getValue())) + .map(Map.Entry::getKey) + .collect(Collectors.joining(",")); } public void merge(EncodedValuesProperties other) { @@ -146,5 +119,3 @@ public void merge(EncodedValuesProperties other) { maxWidth = ofNullable(this.maxWidth).orElse(other.maxWidth); } } - - From e3074d51768894095af070a747adf316849db441 Mon Sep 17 00:00:00 2001 From: aoles Date: Fri, 24 Oct 2025 18:40:45 +0200 Subject: [PATCH 8/9] refactor: reduce cognitive complexity --- .../ors/config/profile/BuildProperties.java | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/ors-engine/src/main/java/org/heigit/ors/config/profile/BuildProperties.java b/ors-engine/src/main/java/org/heigit/ors/config/profile/BuildProperties.java index 1509254df6..5f60930afc 100644 --- a/ors-engine/src/main/java/org/heigit/ors/config/profile/BuildProperties.java +++ b/ors-engine/src/main/java/org/heigit/ors/config/profile/BuildProperties.java @@ -95,7 +95,10 @@ public void initExtStorages() { ExtendedStorageName extendedStorageName = ExtendedStorageName.getEnum(key); switch (extendedStorageName) { case HEAVY_VEHICLE: - handleHeavyVehicle(storage); + handleVehicleAccess(); + if (Boolean.TRUE.equals(storage.getRestrictions())) { + handleVehicleRestrictions(); + } break; case OSM_ID: if (encodedValues.getOsmWayId() == null) { @@ -117,24 +120,7 @@ public void initExtStorages() { } } - private void handleHeavyVehicle(ExtendedStorageProperties storage) { - if (storage.getRestrictions() != null && storage.getRestrictions()) { - if (encodedValues.getMaxAxleLoad() == null) { - encodedValues.setMaxAxleLoad(true); - } - if (encodedValues.getMaxHeight() == null) { - encodedValues.setMaxHeight(true); - } - if (encodedValues.getMaxLength() == null) { - encodedValues.setMaxLength(true); - } - if (encodedValues.getMaxWeight() == null) { - encodedValues.setMaxWeight(true); - } - if (encodedValues.getMaxWidth() == null) { - encodedValues.setMaxWidth(true); - } - } + private void handleVehicleAccess() { if (encodedValues.getAgriculturalAccess() == null) { encodedValues.setAgriculturalAccess(true); } @@ -157,6 +143,25 @@ private void handleHeavyVehicle(ExtendedStorageProperties storage) { encodedValues.setHazmatAccess(true); } } + + private void handleVehicleRestrictions() { + if (encodedValues.getMaxAxleLoad() == null) { + encodedValues.setMaxAxleLoad(true); + } + if (encodedValues.getMaxHeight() == null) { + encodedValues.setMaxHeight(true); + } + if (encodedValues.getMaxLength() == null) { + encodedValues.setMaxLength(true); + } + if (encodedValues.getMaxWeight() == null) { + encodedValues.setMaxWeight(true); + } + if (encodedValues.getMaxWidth() == null) { + encodedValues.setMaxWidth(true); + } + } + private void handleWayCategory() { if (encodedValues.getHighway() == null) { encodedValues.setHighway(true); From ae3731e938983d6601f2c8b1e03886361be6a6f8 Mon Sep 17 00:00:00 2001 From: aoles Date: Fri, 24 Oct 2025 18:51:50 +0200 Subject: [PATCH 9/9] fix: mark HeavyVehicle storage as transfered to encoded values --- .../src/main/java/org/heigit/ors/plugins/PluginManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ors-engine/src/main/java/org/heigit/ors/plugins/PluginManager.java b/ors-engine/src/main/java/org/heigit/ors/plugins/PluginManager.java index 029cbbc3c0..e089a70e01 100644 --- a/ors-engine/src/main/java/org/heigit/ors/plugins/PluginManager.java +++ b/ors-engine/src/main/java/org/heigit/ors/plugins/PluginManager.java @@ -65,7 +65,7 @@ public List createInstances(Map parameters return result; } private boolean storageTransferredToEncodedValues(String storageName) { - return of("OsmId", "WayCategory", "WaySurfaceType") + return of("HeavyVehicle", "OsmId", "WayCategory", "WaySurfaceType") .anyMatch(s -> s.equalsIgnoreCase(storageName)); }