From 24460268af113c370492ed5d3756980ea5cecb39 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 18 Dec 2023 17:48:04 +0100 Subject: [PATCH 001/111] Sketch out possible solution to get Gmap data into StreetEdge --- .../apis/gtfs/mapping/RouteRequestMapper.java | 1 + .../graph_builder/GraphBuilder.java | 4 +++- .../graph_builder/module/osm/OsmModule.java | 8 ++++++- .../routing/api/request/RouteRequest.java | 5 +++++ .../street/model/edge/StreetEdge.java | 21 +++++++++++++++---- .../street/model/edge/StreetEdgeBuilder.java | 12 +++++++++++ .../search/request/StreetSearchRequest.java | 5 +++++ .../vehicle_rental/VehicleRentalUpdater.java | 5 +++++ .../opentripplanner/apis/gtfs/schema.graphqls | 8 +++++++ 9 files changed, 63 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java index ab9e8bce823..9c80486fff8 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java @@ -57,6 +57,7 @@ public static RouteRequest toRouteRequest( context.transitService().getTimeZone() ); + callWith.argument("wheelchair", request::setWheelchair); callWith.argument("wheelchair", request::setWheelchair); callWith.argument("numItineraries", request::setNumItineraries); callWith.argument("searchWindow", (Long m) -> request.setSearchWindow(Duration.ofSeconds(m))); diff --git a/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java b/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java index 8b2e55ffc2f..91a7ba97ba0 100644 --- a/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java +++ b/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java @@ -5,6 +5,7 @@ import static org.opentripplanner.datastore.api.FileType.OSM; import jakarta.inject.Inject; +import java.nio.file.Files; import java.time.Duration; import java.util.ArrayList; import java.util.List; @@ -96,7 +97,8 @@ public static GraphBuilder create( graphBuilder.hasTransitData = hasTransitData; if (hasOsm) { - graphBuilder.addModule(factory.osmModule()); + var profileData = Files.readString("mobility-profile.csv"); + graphBuilder.addModule(factory.osmModule(profileData)); } if (hasGtfs) { diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 9cc15d191fc..4e934f4b7d0 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -33,6 +33,7 @@ import org.opentripplanner.street.model.vertex.BarrierVertex; import org.opentripplanner.street.model.vertex.IntersectionVertex; import org.opentripplanner.street.model.vertex.Vertex; +import org.opentripplanner.street.model.vertex.VertexLabel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -522,6 +523,10 @@ private StreetEdge getEdgeForStreet( I18NString name = params.edgeNamer().getNameForWay(way, label); float carSpeed = way.getOsmProvider().getOsmTagMapper().getCarSpeedForWay(way, back); + + // do the cost lookup from the CSV input + var isWhatWeWant = startEndpoint.getLabel().equals(VertexLabel.osm(891723987129313l)); + StreetEdgeBuilder seb = new StreetEdgeBuilder<>() .withFromVertex(startEndpoint) .withToVertex(endEndpoint) @@ -535,7 +540,8 @@ private StreetEdge getEdgeForStreet( .withRoundabout(way.isRoundabout()) .withSlopeOverride(way.getOsmProvider().getWayPropertySet().getSlopeOverride(way)) .withStairs(way.isSteps()) - .withWheelchairAccessible(way.isWheelchairAccessible()); + .withWheelchairAccessible(way.isWheelchairAccessible()) + .withProfileCosts(Map.of(StreetEdge.MobilityProfile.Blind, 2f)); if (!way.hasTag("name") && !way.hasTag("ref")) { seb.withBogusName(true); diff --git a/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java b/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java index ce2fdb31c44..57328cfa182 100644 --- a/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java +++ b/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java @@ -25,6 +25,7 @@ import org.opentripplanner.routing.api.response.RoutingError; import org.opentripplanner.routing.api.response.RoutingErrorCode; import org.opentripplanner.routing.error.RoutingValidationException; +import org.opentripplanner.street.model.edge.StreetEdge; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -80,6 +81,7 @@ public class RouteRequest implements Cloneable, Serializable { private JourneyRequest journey = new JourneyRequest(); private boolean wheelchair = false; + private StreetEdge.MobilityProfile mobilityProfile; /* CONSTRUCTORS */ @@ -126,6 +128,9 @@ public boolean wheelchair() { public void setWheelchair(boolean wheelchair) { this.wheelchair = wheelchair; } + public void setMobilityProfile(StreetEdge.MobilityProfile profile) { + this.mobilityProfile = mobilityProfile; + } /** * The epoch date/time in seconds that the trip should depart (or arrive, for requests where diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index d0b0018898a..79e63f77376 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -4,8 +4,10 @@ import java.io.ObjectOutputStream; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.stream.Stream; @@ -85,6 +87,14 @@ public class StreetEdge */ private float bicycleSafetyFactor; + public enum MobilityProfile { + Wheelchair, + Blind, + ZimmerFrame + } + + public Map profileCost = new HashMap<>(); + /** * walkSafetyFactor = length * walkSafetyFactor. For example, a 100m street with a safety * factor of 2.0 will be considered in term of safety cost as the same as a 200m street with a @@ -146,6 +156,7 @@ protected StreetEdge(StreetEdgeBuilder builder) { inAngle = lineStringInOutAngles.inAngle(); outAngle = lineStringInOutAngles.outAngle(); elevationExtension = builder.streetElevationExtension(); + profileCost = builder.profileCosts(); } public StreetEdgeBuilder toBuilder() { @@ -1102,7 +1113,8 @@ private StateEditor doTraverse(State s0, TraverseMode traverseMode, boolean walk traverseMode, speed, walkingBike, - s0.getRequest().wheelchair() + s0.getRequest().wheelchair(), + s0.getRequest().mobilityProfile() ); default -> otherTraversalCosts(preferences, traverseMode, walkingBike, speed); }; @@ -1257,8 +1269,8 @@ private TraversalCosts walkingTraversalCosts( TraverseMode traverseMode, double speed, boolean walkingBike, - boolean wheelchair - ) { + boolean wheelchair, + MobilityProfile mobilityProfile) { double time, weight; if (wheelchair) { time = getEffectiveWalkDistance() / speed; @@ -1297,7 +1309,8 @@ private TraversalCosts walkingTraversalCosts( isStairs() ); } - + var profileMultiplier = profileCost.getOrDefault(mobilityProfile, 1f); + weight = weight * profileMultiplier; return new TraversalCosts(time, weight); } diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java index b3173b7bed5..daa2416ddef 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java @@ -11,6 +11,7 @@ import static org.opentripplanner.street.model.edge.StreetEdge.WALK_NOTHRUTRAFFIC; import static org.opentripplanner.street.model.edge.StreetEdge.WHEELCHAIR_ACCESSIBLE_FLAG_INDEX; +import java.util.Map; import org.locationtech.jts.geom.LineString; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.i18n.NonLocalizedString; @@ -38,6 +39,7 @@ public class StreetEdgeBuilder> { private float bicycleSafetyFactor; private short flags; private StreetElevationExtension streetElevationExtension; + private Map profileCosts; public StreetEdgeBuilder() { this.defaultLength = true; @@ -59,6 +61,7 @@ public StreetEdgeBuilder(StreetEdge original) { this.walkSafetyFactor = original.getWalkSafetyFactor(); this.bicycleSafetyFactor = original.getBicycleSafetyFactor(); this.flags = original.getFlags(); + this.profileCosts = original.profileCost; } public StreetEdge buildAndConnect() { @@ -248,6 +251,15 @@ public StreetElevationExtension streetElevationExtension() { return streetElevationExtension; } + public B withProfileCosts(Map costs) { + this.profileCosts= costs; + return instance(); + } + + public Map profileCosts() { + return profileCosts; + } + @SuppressWarnings("unchecked") final B instance() { return (B) this; diff --git a/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequest.java b/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequest.java index 6d8bd5783f3..0979d388fac 100644 --- a/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequest.java +++ b/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequest.java @@ -13,6 +13,7 @@ import org.opentripplanner.routing.api.request.StreetMode; import org.opentripplanner.routing.api.request.preference.RoutingPreferences; import org.opentripplanner.routing.api.request.request.VehicleRentalRequest; +import org.opentripplanner.street.model.edge.StreetEdge; import org.opentripplanner.street.model.vertex.Vertex; import org.opentripplanner.street.search.intersection_model.IntersectionTraversalCalculator; import org.opentripplanner.street.search.state.State; @@ -170,6 +171,10 @@ public boolean isCloseToStartOrEnd(Vertex vertex) { ); } + public StreetEdge.MobilityProfile mobilityProfile() { + return StreetEdge.MobilityProfile.Wheelchair; + } + @Nullable private static Envelope createEnvelope(GenericLocation location) { if (location == null) { diff --git a/src/main/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdater.java b/src/main/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdater.java index 1e037a7c1c2..c1be994ba86 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdater.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdater.java @@ -29,6 +29,7 @@ import org.opentripplanner.street.model.RentalRestrictionExtension; import org.opentripplanner.street.model.edge.StreetEdge; import org.opentripplanner.street.model.vertex.VertexFactory; +import org.opentripplanner.street.model.vertex.VertexLabel; import org.opentripplanner.street.search.TraverseMode; import org.opentripplanner.street.search.TraverseModeSet; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -231,6 +232,10 @@ public void run(Graph graph, TransitModel transitModel) { tempEdgesByStation.remove(station); } + var vertex = graph.getVertex(VertexLabel.osm(139204728343L)); + + vertex.getOutgoingStreetEdges().stream().filter(e -> e.getToVertex().) + // this check relies on the generated equals for the record which also recursively checks that // the JTS geometries are equal if (!geofencingZones.isEmpty() && !geofencingZones.equals(latestAppliedGeofencingZones)) { diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index d6cc164e3c1..df745db8f7e 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -2446,6 +2446,12 @@ enum Qualifier { HAIL } +enum MobilityProfile { + Wheelchair, + Blind, + ZimmerFrame +} + type QueryType { """Fetches an object given its ID""" node( @@ -3135,6 +3141,8 @@ type QueryType { "Preferences for vehicle parking" parking: VehicleParkingInput + mobilityProfile: MobilityProfile + ## Deprecated fields, none of these have any effect on the routing anymore """ From e83a379eb0f6c0d7f9d5995e813030c3f5b74f63 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 26 Dec 2023 17:21:58 -0500 Subject: [PATCH 002/111] feat(BuildConfig): Add support for specifying a mobility profile CSV. --- .../graph_builder/GraphBuilderDataSources.java | 12 ++++++++++++ .../standalone/config/BuildConfig.java | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/main/java/org/opentripplanner/graph_builder/GraphBuilderDataSources.java b/src/main/java/org/opentripplanner/graph_builder/GraphBuilderDataSources.java index eaa61fea139..681c09df9b0 100644 --- a/src/main/java/org/opentripplanner/graph_builder/GraphBuilderDataSources.java +++ b/src/main/java/org/opentripplanner/graph_builder/GraphBuilderDataSources.java @@ -195,6 +195,18 @@ public Optional stopConsolidationDataSource() { }); } + /** + * Returns the optional data source for the mobility profile routing costs. + */ + public Optional mobilityProfileDataSource() { + return Optional + .ofNullable(buildConfig.mobilityProfileFile) + .map(fileName -> { + var f = baseDirectory.toPath().resolve(fileName).toFile(); + return new FileDataSource(f, FileType.CONFIG); + }); + } + /** * Match the URI provided in the configuration with the URI of a datasource, * either by comparing directly the two URIs or by first prepending the OTP base directory diff --git a/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java b/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java index d62aa1bf41f..356e23ef84a 100644 --- a/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java @@ -183,6 +183,7 @@ public class BuildConfig implements OtpDataStoreConfig { public final ZoneId transitModelTimeZone; public final String stopConsolidationFile; + public final String mobilityProfileFile; /** * Set all parameters from the given Jackson JSON tree, applying defaults. Supplying @@ -611,6 +612,15 @@ that we support remote input files (cloud storage or arbitrary URLs) not all dat ) .asString(null); + mobilityProfileFile = + root + .of("mobilityProfileFile") + .since(V2_5) + .summary( + "Name of the CSV-formatted file in the build directory which contains the routing costs by mobility profile." + ) + .asString(null); + osmDefaults = OsmConfig.mapOsmDefaults(root, "osmDefaults"); osm = OsmConfig.mapOsmConfig(root, "osm", osmDefaults); demDefaults = DemConfig.mapDemDefaultsConfig(root, "demDefaults"); From f2a0c3298bef8c1b44fcd1e4d94c06ec4e04db06 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 26 Dec 2023 17:25:37 -0500 Subject: [PATCH 003/111] feat(MobilityProfileParser): Add class to parse mobility profile CSV. --- .../MobilityProfileParser.java | 88 +++++++++++++++++++ .../graph_builder/GraphBuilder.java | 20 ++++- .../graph_builder/module/osm/OsmModule.java | 6 ++ 3 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java new file mode 100644 index 00000000000..a23d36db15d --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java @@ -0,0 +1,88 @@ +package org.opentripplanner.ext.mobilityprofile; + +import com.csvreader.CsvReader; +import com.google.common.collect.ImmutableTable; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class MobilityProfileParser { + + private record MobilityProfileEntry( + String timestamp, // Not actively used, so processed as text for reference. + String fromNode, + String toNode, + double link, + Map weightByProfile + ) {} + + public static Map> parseData(InputStream is) { + List profileNames = List.of( + "None", + "Some", + "Device", + "WChairM", + "WChairE", + "MScooter", + "Vision", + "Vision+", + "Some-Vision", + "Device-Vision", + "WChairM-Vision", + "WChairE-Vision", + "MScooter-Vision", + "Some-Vision+", + "Device-Vision+", + "WChairM-Vision+", + "WChairE-Vision+", + "MScooter-Vision+" + ); + + try { + var reader = new CsvReader(is, StandardCharsets.UTF_8); + reader.setDelimiter(','); + + reader.readHeaders(); + + // Process rows from the CSV file first. + var entries = new ArrayList(); + while (reader.readRecord()) { + String timestamp = reader.get("Timestamp"); + String fromNode = reader.get("Upstream Node"); + String toNode = reader.get("Downstream Node"); + double linkWeight = Double.parseDouble(reader.get("Link")); + var weightMap = new HashMap(); + for (var profile : profileNames) { + weightMap.put(profile, Double.parseDouble(reader.get(profile))); + } + var entry = new MobilityProfileEntry( + timestamp, + fromNode, + toNode, + linkWeight, + weightMap + ); + entries.add(entry); + } + + // Build a map indexed by profile, where each value for a given profile is + // a table of weights indexed by both the from and the to node. + var processedMobilityData = new HashMap>(); + for (var profile : profileNames) { + ImmutableTable.Builder tableBuilder = ImmutableTable.builder(); + for (var entry : entries) { + tableBuilder.put(entry.fromNode, entry.toNode, entry.weightByProfile.get(profile)); + } + processedMobilityData.put(profile, tableBuilder.build()); + } + + return processedMobilityData; + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java b/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java index 91a7ba97ba0..58283fc31a9 100644 --- a/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java +++ b/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java @@ -5,12 +5,15 @@ import static org.opentripplanner.datastore.api.FileType.OSM; import jakarta.inject.Inject; -import java.nio.file.Files; +import java.io.IOException; import java.time.Duration; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.opentripplanner.datastore.api.DataSource; +import org.opentripplanner.ext.mobilityprofile.MobilityProfileParser; import org.opentripplanner.ext.emissions.EmissionsDataModel; import org.opentripplanner.ext.stopconsolidation.StopConsolidationRepository; import org.opentripplanner.framework.application.OTPFeature; @@ -21,6 +24,7 @@ import org.opentripplanner.graph_builder.issue.api.DataImportIssueSummary; import org.opentripplanner.graph_builder.model.GraphBuilderModule; import org.opentripplanner.graph_builder.module.configure.DaggerGraphBuilderFactory; +import org.opentripplanner.graph_builder.module.osm.OsmModule; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.service.worldenvelope.WorldEnvelopeRepository; import org.opentripplanner.standalone.config.BuildConfig; @@ -97,8 +101,18 @@ public static GraphBuilder create( graphBuilder.hasTransitData = hasTransitData; if (hasOsm) { - var profileData = Files.readString("mobility-profile.csv"); - graphBuilder.addModule(factory.osmModule(profileData)); + Optional mobilityDataSource = dataSources.mobilityProfileDataSource(); + OsmModule osmModule = factory.osmModule(); + if (mobilityDataSource.isPresent()) { + // Parse stuff from the mobility profile CSV + try (var inputStream = mobilityDataSource.get().asInputStream()) { + var mobilityProfileData = MobilityProfileParser.parseData(inputStream); + osmModule.setMobilityProfileData(mobilityProfileData); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + graphBuilder.addModule(osmModule); } if (hasGtfs) { diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 4e934f4b7d0..7c692994124 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -1,5 +1,6 @@ package org.opentripplanner.graph_builder.module.osm; +import com.google.common.collect.ImmutableTable; import com.google.common.collect.Iterables; import gnu.trove.iterator.TLongIterator; import java.util.ArrayList; @@ -56,6 +57,7 @@ public class OsmModule implements GraphBuilderModule { private final SafetyValueNormalizer normalizer; private final VertexGenerator vertexGenerator; private final OsmDatabase osmdb; + private Map> mobilityProfileData; OsmModule( Collection providers, @@ -108,6 +110,10 @@ public Map elevationDataOutput() { return elevationData; } + public void setMobilityProfileData(Map> mobilityProfileData) { + this.mobilityProfileData = mobilityProfileData; + } + private record StreetEdgePair(StreetEdge main, StreetEdge back) {} private void build() { From fc1a60a46e1bfdeb5664305ec717c5cf3524192a Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 27 Dec 2023 16:01:13 -0500 Subject: [PATCH 004/111] refactor(MobilityProfile): Extract enum of known mobility profiles. --- .../ext/mobilityprofile/MobilityProfile.java | 32 +++++++ .../MobilityProfileParser.java | 89 +++++++------------ .../graph_builder/module/osm/OsmModule.java | 19 ++-- .../street/model/edge/StreetEdge.java | 10 +-- .../street/model/edge/StreetEdgeBuilder.java | 9 +- 5 files changed, 84 insertions(+), 75 deletions(-) create mode 100644 src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfile.java diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfile.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfile.java new file mode 100644 index 00000000000..da50eb4d61d --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfile.java @@ -0,0 +1,32 @@ +package org.opentripplanner.ext.mobilityprofile; + +public enum MobilityProfile { + NONE("None"), + SOME("Some"), + DEVICE("Device"), + WCHAIRM("WChairM"), + WCHAIRE("WChairE"), + MSCOOTER("MScooter"), + VISION("Vision"), + VISIONPLUS("Vision+"), + SOME_VISION("Some-Vision"), + DEVICE_VISION("Device-Vision"), + WCHAIRM_VISION("WChairM-Vision"), + WCHAIRE_VISION("WChairE-Vision"), + MSCOOTER_VISION("MScooter-Vision"), + SOME_VISIONPLUS("Some-Vision+"), + DEVICE_VISIONPLUS("Device-Vision+"), + WCHAIRM_VISIONPLUS("WChairM-Vision+"), + WCHAIRE_VISIONPLUS("WChairE-Vision+"), + MSCOOTER_VISIONPLUS("MScooter-Vision+"); + + private final String text; + + MobilityProfile(String text) { + this.text = text; + } + + public String getText() { + return text; + } +} diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java index a23d36db15d..cf28697f584 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java @@ -5,82 +5,55 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.opentripplanner.street.model.vertex.VertexLabel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class MobilityProfileParser { - private record MobilityProfileEntry( - String timestamp, // Not actively used, so processed as text for reference. - String fromNode, - String toNode, - double link, - Map weightByProfile - ) {} + private static final Logger LOG = LoggerFactory.getLogger(MobilityProfileParser.class); - public static Map> parseData(InputStream is) { - List profileNames = List.of( - "None", - "Some", - "Device", - "WChairM", - "WChairE", - "MScooter", - "Vision", - "Vision+", - "Some-Vision", - "Device-Vision", - "WChairM-Vision", - "WChairE-Vision", - "MScooter-Vision", - "Some-Vision+", - "Device-Vision+", - "WChairM-Vision+", - "WChairE-Vision+", - "MScooter-Vision+" - ); + private MobilityProfileParser() {} + public static ImmutableTable> parseData(InputStream is) { try { var reader = new CsvReader(is, StandardCharsets.UTF_8); reader.setDelimiter(','); reader.readHeaders(); - // Process rows from the CSV file first. - var entries = new ArrayList(); + // Process rows from the CSV file and build a table indexed by both the up/downstream nodes, + // where each value is a map of costs by mobility profile. + ImmutableTable.Builder> tableBuilder = ImmutableTable.builder(); + int lineNumber = 1; while (reader.readRecord()) { - String timestamp = reader.get("Timestamp"); - String fromNode = reader.get("Upstream Node"); - String toNode = reader.get("Downstream Node"); - double linkWeight = Double.parseDouble(reader.get("Link")); - var weightMap = new HashMap(); - for (var profile : profileNames) { - weightMap.put(profile, Double.parseDouble(reader.get(profile))); + try { + long fromNode = Long.parseLong(reader.get("Upstream Node"), 10); + long toNode = Long.parseLong(reader.get("Downstream Node"), 10); + + var weightMap = new HashMap(); + for (var profile : MobilityProfile.values()) { + weightMap.put(profile, Float.parseFloat(reader.get(profile.getText()))); + } + + tableBuilder.put( + VertexLabel.osm(fromNode).toString(), + VertexLabel.osm(toNode).toString(), + weightMap + ); + } catch (NumberFormatException | NullPointerException e) { + LOG.warn( + "Skipping mobility profile data at line {}: missing/invalid data", + lineNumber + ); } - var entry = new MobilityProfileEntry( - timestamp, - fromNode, - toNode, - linkWeight, - weightMap - ); - entries.add(entry); + lineNumber++; } - // Build a map indexed by profile, where each value for a given profile is - // a table of weights indexed by both the from and the to node. - var processedMobilityData = new HashMap>(); - for (var profile : profileNames) { - ImmutableTable.Builder tableBuilder = ImmutableTable.builder(); - for (var entry : entries) { - tableBuilder.put(entry.fromNode, entry.toNode, entry.weightByProfile.get(profile)); - } - processedMobilityData.put(profile, tableBuilder.build()); - } - - return processedMobilityData; + return tableBuilder.build(); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 7c692994124..a9755e8c870 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -11,6 +11,7 @@ import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.LineString; +import org.opentripplanner.ext.mobilityprofile.MobilityProfile; import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.framework.geometry.SphericalDistanceLibrary; import org.opentripplanner.framework.i18n.I18NString; @@ -34,7 +35,6 @@ import org.opentripplanner.street.model.vertex.BarrierVertex; import org.opentripplanner.street.model.vertex.IntersectionVertex; import org.opentripplanner.street.model.vertex.Vertex; -import org.opentripplanner.street.model.vertex.VertexLabel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -57,7 +57,7 @@ public class OsmModule implements GraphBuilderModule { private final SafetyValueNormalizer normalizer; private final VertexGenerator vertexGenerator; private final OsmDatabase osmdb; - private Map> mobilityProfileData; + private ImmutableTable> mobilityProfileData; OsmModule( Collection providers, @@ -110,7 +110,7 @@ public Map elevationDataOutput() { return elevationData; } - public void setMobilityProfileData(Map> mobilityProfileData) { + public void setMobilityProfileData(ImmutableTable> mobilityProfileData) { this.mobilityProfileData = mobilityProfileData; } @@ -531,7 +531,11 @@ private StreetEdge getEdgeForStreet( // do the cost lookup from the CSV input - var isWhatWeWant = startEndpoint.getLabel().equals(VertexLabel.osm(891723987129313l)); + var edgeMobilityCostMap = mobilityProfileData.get( + startEndpoint.getLabel().toString(), + endEndpoint.getLabel().toString() + ); + StreetEdgeBuilder seb = new StreetEdgeBuilder<>() .withFromVertex(startEndpoint) @@ -546,8 +550,11 @@ private StreetEdge getEdgeForStreet( .withRoundabout(way.isRoundabout()) .withSlopeOverride(way.getOsmProvider().getWayPropertySet().getSlopeOverride(way)) .withStairs(way.isSteps()) - .withWheelchairAccessible(way.isWheelchairAccessible()) - .withProfileCosts(Map.of(StreetEdge.MobilityProfile.Blind, 2f)); + .withWheelchairAccessible(way.isWheelchairAccessible()); + + if (edgeMobilityCostMap != null) { + seb.withProfileCosts(edgeMobilityCostMap); + } if (!way.hasTag("name") && !way.hasTag("ref")) { seb.withBogusName(true); diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index 79e63f77376..d655c3634f3 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -14,6 +14,7 @@ import javax.annotation.Nonnull; import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.impl.PackedCoordinateSequence; +import org.opentripplanner.ext.mobilityprofile.MobilityProfile; import org.opentripplanner.framework.geometry.CompactLineStringUtils; import org.opentripplanner.framework.geometry.DirectionUtils; import org.opentripplanner.framework.geometry.GeometryUtils; @@ -87,12 +88,6 @@ public class StreetEdge */ private float bicycleSafetyFactor; - public enum MobilityProfile { - Wheelchair, - Blind, - ZimmerFrame - } - public Map profileCost = new HashMap<>(); /** @@ -1270,7 +1265,8 @@ private TraversalCosts walkingTraversalCosts( double speed, boolean walkingBike, boolean wheelchair, - MobilityProfile mobilityProfile) { + MobilityProfile mobilityProfile + ) { double time, weight; if (wheelchair) { time = getEffectiveWalkDistance() / speed; diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java index daa2416ddef..5d320d62c9c 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java @@ -13,6 +13,7 @@ import java.util.Map; import org.locationtech.jts.geom.LineString; +import org.opentripplanner.ext.mobilityprofile.MobilityProfile; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.framework.lang.BitSetUtils; @@ -39,7 +40,7 @@ public class StreetEdgeBuilder> { private float bicycleSafetyFactor; private short flags; private StreetElevationExtension streetElevationExtension; - private Map profileCosts; + private Map profileCosts; public StreetEdgeBuilder() { this.defaultLength = true; @@ -251,12 +252,12 @@ public StreetElevationExtension streetElevationExtension() { return streetElevationExtension; } - public B withProfileCosts(Map costs) { - this.profileCosts= costs; + public B withProfileCosts(Map costs) { + this.profileCosts = costs; return instance(); } - public Map profileCosts() { + public Map profileCosts() { return profileCosts; } From 50982d484132613baf1d6f66e863c20462bd4988 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 27 Dec 2023 16:04:47 -0500 Subject: [PATCH 005/111] refactor: Fix other type errors --- .../graph_builder/GraphBuilder.java | 3 +-- .../routing/api/request/RouteRequest.java | 17 +++++++++++++---- .../search/request/StreetSearchRequest.java | 6 +++--- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java b/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java index 58283fc31a9..0db1754c253 100644 --- a/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java +++ b/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java @@ -106,8 +106,7 @@ public static GraphBuilder create( if (mobilityDataSource.isPresent()) { // Parse stuff from the mobility profile CSV try (var inputStream = mobilityDataSource.get().asInputStream()) { - var mobilityProfileData = MobilityProfileParser.parseData(inputStream); - osmModule.setMobilityProfileData(mobilityProfileData); + osmModule.setMobilityProfileData(MobilityProfileParser.parseData(inputStream)); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java b/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java index 57328cfa182..010dfe62147 100644 --- a/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java +++ b/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java @@ -13,6 +13,7 @@ import java.util.Locale; import java.util.function.Consumer; import javax.annotation.Nullable; +import org.opentripplanner.ext.mobilityprofile.MobilityProfile; import org.opentripplanner.framework.collection.ListSection; import org.opentripplanner.framework.time.DateUtils; import org.opentripplanner.framework.tostring.ToStringBuilder; @@ -25,7 +26,6 @@ import org.opentripplanner.routing.api.response.RoutingError; import org.opentripplanner.routing.api.response.RoutingErrorCode; import org.opentripplanner.routing.error.RoutingValidationException; -import org.opentripplanner.street.model.edge.StreetEdge; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -81,7 +81,8 @@ public class RouteRequest implements Cloneable, Serializable { private JourneyRequest journey = new JourneyRequest(); private boolean wheelchair = false; - private StreetEdge.MobilityProfile mobilityProfile; + + private MobilityProfile mobilityProfile; /* CONSTRUCTORS */ @@ -128,8 +129,16 @@ public boolean wheelchair() { public void setWheelchair(boolean wheelchair) { this.wheelchair = wheelchair; } - public void setMobilityProfile(StreetEdge.MobilityProfile profile) { - this.mobilityProfile = mobilityProfile; + + /** + * Applicable mobility profile for street routing + */ + public MobilityProfile mobilityProfile() { + return mobilityProfile; + } + + public void setMobilityProfile(MobilityProfile profile) { + this.mobilityProfile = profile; } /** diff --git a/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequest.java b/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequest.java index 0979d388fac..bd33e6d3a21 100644 --- a/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequest.java +++ b/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequest.java @@ -7,13 +7,13 @@ import org.locationtech.jts.geom.Envelope; import org.opentripplanner.astar.spi.AStarRequest; import org.opentripplanner.ext.dataoverlay.routing.DataOverlayContext; +import org.opentripplanner.ext.mobilityprofile.MobilityProfile; import org.opentripplanner.framework.geometry.SphericalDistanceLibrary; import org.opentripplanner.model.GenericLocation; import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.routing.api.request.StreetMode; import org.opentripplanner.routing.api.request.preference.RoutingPreferences; import org.opentripplanner.routing.api.request.request.VehicleRentalRequest; -import org.opentripplanner.street.model.edge.StreetEdge; import org.opentripplanner.street.model.vertex.Vertex; import org.opentripplanner.street.search.intersection_model.IntersectionTraversalCalculator; import org.opentripplanner.street.search.state.State; @@ -171,8 +171,8 @@ public boolean isCloseToStartOrEnd(Vertex vertex) { ); } - public StreetEdge.MobilityProfile mobilityProfile() { - return StreetEdge.MobilityProfile.Wheelchair; + public MobilityProfile mobilityProfile() { + return MobilityProfile.WCHAIRM; } @Nullable From 10f2b5845409689205c833121b45b92350945590 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 27 Dec 2023 17:04:33 -0500 Subject: [PATCH 006/111] improvement(OsmModule): List unused mobility profile costs. --- .../graph_builder/module/osm/OsmModule.java | 54 ++++++++++++++++--- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index a9755e8c870..979e434d759 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -58,6 +58,7 @@ public class OsmModule implements GraphBuilderModule { private final VertexGenerator vertexGenerator; private final OsmDatabase osmdb; private ImmutableTable> mobilityProfileData; + private HashMap mappedMobilityProfileEntries; OsmModule( Collection providers, @@ -149,6 +150,8 @@ private void build() { // figure out which nodes that are actually intersections vertexGenerator.initIntersectionNodes(); + mappedMobilityProfileEntries = new HashMap<>(); + buildBasicGraph(); buildWalkableAreas(!params.areaVisibility()); validateBarriers(); @@ -178,6 +181,31 @@ private void build() { params.edgeNamer().postprocess(); normalizer.applySafetyFactors(); + + listUnusedMobilityCosts(); + } + + /** + * Lists unused entries from the mobility profile data. + */ + private void listUnusedMobilityCosts() { + var unusedEntries = new ArrayList(); + + for (var cell : mobilityProfileData.cellSet()) { + String key = getNodeKey(cell.getRowKey(), cell.getColumnKey()); + Boolean exists = mappedMobilityProfileEntries.get(key); + if (exists == null || !exists) { + unusedEntries.add(key); + } + } + + if (!unusedEntries.isEmpty()) { + StringBuilder sb = new StringBuilder(); + for (var entry : unusedEntries) { + sb.append(String.format("%n- %s", entry)); + } + LOG.warn("The following mobility profile entries were not used:{}", sb); + } } /** @@ -529,14 +557,6 @@ private StreetEdge getEdgeForStreet( I18NString name = params.edgeNamer().getNameForWay(way, label); float carSpeed = way.getOsmProvider().getOsmTagMapper().getCarSpeedForWay(way, back); - - // do the cost lookup from the CSV input - var edgeMobilityCostMap = mobilityProfileData.get( - startEndpoint.getLabel().toString(), - endEndpoint.getLabel().toString() - ); - - StreetEdgeBuilder seb = new StreetEdgeBuilder<>() .withFromVertex(startEndpoint) .withToVertex(endEndpoint) @@ -552,8 +572,22 @@ private StreetEdge getEdgeForStreet( .withStairs(way.isSteps()) .withWheelchairAccessible(way.isWheelchairAccessible()); + // Lookup costs by mobility profile, if any were defined. + String startId = startEndpoint.getLabel().toString(); + String endId = endEndpoint.getLabel().toString(); + var edgeMobilityCostMap = mobilityProfileData.get( + startId, + endId + ); if (edgeMobilityCostMap != null) { seb.withProfileCosts(edgeMobilityCostMap); + LOG.info( + "Applied mobility profile costs between nodes {}-{}", + startId, + endId + ); + // Keep tab of node pairs that have been mapped. + mappedMobilityProfileEntries.put(getNodeKey(startId, endId), true); } if (!way.hasTag("name") && !way.hasTag("ref")) { @@ -570,4 +604,8 @@ private StreetEdge getEdgeForStreet( return street; } + + private static String getNodeKey(String startId, String endId) { + return String.format("%s=>%s", startId, endId); + } } From 939b1bf5b3958f5a249e9914f8711a2c8de1fdfa Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 27 Dec 2023 20:49:04 -0500 Subject: [PATCH 007/111] refactor(StreetEdgeBuilder): Initialize costs to empty map. --- .../opentripplanner/graph_builder/module/osm/OsmModule.java | 2 +- .../opentripplanner/street/model/edge/StreetEdgeBuilder.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 979e434d759..a2c4f661ed8 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -204,7 +204,7 @@ private void listUnusedMobilityCosts() { for (var entry : unusedEntries) { sb.append(String.format("%n- %s", entry)); } - LOG.warn("The following mobility profile entries were not used:{}", sb); + LOG.warn("{} mobility profile entries were not used:{}", unusedEntries.size(), sb); } } diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java index 5d320d62c9c..221aca661f8 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java @@ -11,6 +11,7 @@ import static org.opentripplanner.street.model.edge.StreetEdge.WALK_NOTHRUTRAFFIC; import static org.opentripplanner.street.model.edge.StreetEdge.WHEELCHAIR_ACCESSIBLE_FLAG_INDEX; +import java.util.EnumMap; import java.util.Map; import org.locationtech.jts.geom.LineString; import org.opentripplanner.ext.mobilityprofile.MobilityProfile; @@ -40,7 +41,7 @@ public class StreetEdgeBuilder> { private float bicycleSafetyFactor; private short flags; private StreetElevationExtension streetElevationExtension; - private Map profileCosts; + private Map profileCosts = new EnumMap<>(MobilityProfile.class); public StreetEdgeBuilder() { this.defaultLength = true; From e4cfd24cf23174cdd16a50eec8ddfd5c824ae018 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 28 Dec 2023 10:22:49 -0500 Subject: [PATCH 008/111] refactor(MobilityProfile): Add various JS docs, update graphql schema. --- .../ext/mobilityprofile/MobilityProfile.java | 3 + .../MobilityProfileParser.java | 61 +++++++++++-------- .../graph_builder/module/osm/OsmModule.java | 2 +- .../opentripplanner/apis/gtfs/schema.graphqls | 21 ++++++- 4 files changed, 58 insertions(+), 29 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfile.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfile.java index da50eb4d61d..720249b550b 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfile.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfile.java @@ -1,5 +1,8 @@ package org.opentripplanner.ext.mobilityprofile; +/** + * Enumeration for the mobility profiles, and their associated column names for CSV parsing. + */ public enum MobilityProfile { NONE("None"), SOME("Some"), diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java index cf28697f584..863e4f0a92b 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java @@ -5,51 +5,35 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.List; +import java.util.EnumMap; import java.util.Map; import org.opentripplanner.street.model.vertex.VertexLabel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * Helper class that processes CSV files containing profile-based OSM costs. + */ public class MobilityProfileParser { private static final Logger LOG = LoggerFactory.getLogger(MobilityProfileParser.class); private MobilityProfileParser() {} + /** + * Process rows from the given CSV stream and build a table indexed by both the + * upstream/downstream nodes, where each value is a map of costs by mobility profile. + */ public static ImmutableTable> parseData(InputStream is) { try { var reader = new CsvReader(is, StandardCharsets.UTF_8); reader.setDelimiter(','); - reader.readHeaders(); - // Process rows from the CSV file and build a table indexed by both the up/downstream nodes, - // where each value is a map of costs by mobility profile. ImmutableTable.Builder> tableBuilder = ImmutableTable.builder(); int lineNumber = 1; while (reader.readRecord()) { - try { - long fromNode = Long.parseLong(reader.get("Upstream Node"), 10); - long toNode = Long.parseLong(reader.get("Downstream Node"), 10); - - var weightMap = new HashMap(); - for (var profile : MobilityProfile.values()) { - weightMap.put(profile, Float.parseFloat(reader.get(profile.getText()))); - } - - tableBuilder.put( - VertexLabel.osm(fromNode).toString(), - VertexLabel.osm(toNode).toString(), - weightMap - ); - } catch (NumberFormatException | NullPointerException e) { - LOG.warn( - "Skipping mobility profile data at line {}: missing/invalid data", - lineNumber - ); - } + parseRow(lineNumber, reader, tableBuilder); lineNumber++; } @@ -58,4 +42,31 @@ public static ImmutableTable> parseD throw new RuntimeException(e); } } + + private static void parseRow( + int lineNumber, + CsvReader reader, + ImmutableTable.Builder> tableBuilder + ) throws IOException { + try { + long fromNode = Long.parseLong(reader.get("Upstream Node"), 10); + long toNode = Long.parseLong(reader.get("Downstream Node"), 10); + + var weightMap = new EnumMap(MobilityProfile.class); + for (var profile : MobilityProfile.values()) { + weightMap.put(profile, Float.parseFloat(reader.get(profile.getText()))); + } + + tableBuilder.put( + VertexLabel.osm(fromNode).toString(), + VertexLabel.osm(toNode).toString(), + weightMap + ); + } catch (NumberFormatException | NullPointerException e) { + LOG.warn( + "Skipping mobility profile data at line {}: missing/invalid data", + lineNumber + ); + } + } } diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index a2c4f661ed8..678ecc966f3 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -586,7 +586,7 @@ private StreetEdge getEdgeForStreet( startId, endId ); - // Keep tab of node pairs that have been mapped. + // Keep tab of node pairs for which mobility profile costs have been mapped. mappedMobilityProfileEntries.put(getNodeKey(startId, endId), true); } diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index df745db8f7e..d53297d55a0 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -2447,9 +2447,24 @@ enum Qualifier { } enum MobilityProfile { - Wheelchair, - Blind, - ZimmerFrame + NONE, + SOME, + DEVICE, + WCHAIRM, + WCHAIRE, + MSCOOTER, + VISION, + VISIONPLUS, + SOME_VISION, + DEVICE_VISION, + WCHAIRM_VISION, + WCHAIRE_VISION, + MSCOOTER_VISION, + SOME_VISIONPLUS, + DEVICE_VISIONPLUS, + WCHAIRM_VISIONPLUS, + WCHAIRE_VISIONPLUS, + MSCOOTER_VISIONPLUS; } type QueryType { From e20090981fd5e213c1dee4df31b8ec068963874d Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 28 Dec 2023 10:55:56 -0500 Subject: [PATCH 009/111] fix(schema.graphqls): Fix syntax --- .../resources/org/opentripplanner/apis/gtfs/schema.graphqls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index d53297d55a0..8e3b0902199 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -2464,7 +2464,7 @@ enum MobilityProfile { DEVICE_VISIONPLUS, WCHAIRM_VISIONPLUS, WCHAIRE_VISIONPLUS, - MSCOOTER_VISIONPLUS; + MSCOOTER_VISIONPLUS } type QueryType { From 80b7c5ea6e564bb7da3874b4bf7e596fde3a3501 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 28 Dec 2023 14:55:17 -0500 Subject: [PATCH 010/111] refactor(RouteRequest): Propagate mobilityProfile plan query param. --- .../ext/mobilityprofile/MobilityProfile.java | 14 ++++++++++++++ .../apis/gtfs/mapping/RouteRequestMapper.java | 1 + .../routing/api/request/RouteRequest.java | 4 ++++ .../street/search/request/StreetSearchRequest.java | 5 ++++- .../search/request/StreetSearchRequestBuilder.java | 9 +++++++++ .../search/request/StreetSearchRequestMapper.java | 3 ++- .../org/opentripplanner/apis/gtfs/schema.graphqls | 2 +- 7 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfile.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfile.java index 720249b550b..ec1bc5434e7 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfile.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfile.java @@ -32,4 +32,18 @@ public enum MobilityProfile { public String getText() { return text; } + + @Override + public String toString() { + return text; + } + + public static MobilityProfile fromString(String value) { + for (MobilityProfile p : MobilityProfile.values()) { + if (p.text.equals(value)) { + return p; + } + } + throw new RuntimeException(String.format("Invalid mobility profile '%s'", value)); + } } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java index 9c80486fff8..d5f8df1983f 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java @@ -252,6 +252,7 @@ public static RouteRequest toRouteRequest( "locale", (String v) -> request.setLocale(GraphQLUtils.getLocale(environment, v)) ); + callWith.argument("mobilityProfile", request::setMobilityProfileFromString); return request; } diff --git a/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java b/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java index 010dfe62147..cf111aa6c02 100644 --- a/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java +++ b/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java @@ -141,6 +141,10 @@ public void setMobilityProfile(MobilityProfile profile) { this.mobilityProfile = profile; } + public void setMobilityProfileFromString(String profile) { + this.mobilityProfile = MobilityProfile.fromString(profile); + } + /** * The epoch date/time in seconds that the trip should depart (or arrive, for requests where * arriveBy is true) diff --git a/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequest.java b/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequest.java index bd33e6d3a21..3fbfa96da19 100644 --- a/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequest.java +++ b/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequest.java @@ -52,6 +52,8 @@ public class StreetSearchRequest implements AStarRequest { private DataOverlayContext dataOverlayContext; + private MobilityProfile mobilityProfile; + /** * Constructor only used for creating a default instance. */ @@ -79,6 +81,7 @@ private StreetSearchRequest() { this.fromEnvelope = createEnvelope(from); this.to = builder.to; this.toEnvelope = createEnvelope(to); + this.mobilityProfile = builder.mobilityProfile; } @Nonnull @@ -172,7 +175,7 @@ public boolean isCloseToStartOrEnd(Vertex vertex) { } public MobilityProfile mobilityProfile() { - return MobilityProfile.WCHAIRM; + return mobilityProfile; } @Nullable diff --git a/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequestBuilder.java b/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequestBuilder.java index 439e65a3289..329f4a03a32 100644 --- a/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequestBuilder.java +++ b/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequestBuilder.java @@ -2,6 +2,7 @@ import java.time.Instant; import java.util.function.Consumer; +import org.opentripplanner.ext.mobilityprofile.MobilityProfile; import org.opentripplanner.model.GenericLocation; import org.opentripplanner.routing.api.request.StreetMode; import org.opentripplanner.routing.api.request.preference.RoutingPreferences; @@ -18,6 +19,8 @@ public class StreetSearchRequestBuilder { GenericLocation from; GenericLocation to; + MobilityProfile mobilityProfile; + StreetSearchRequestBuilder(StreetSearchRequest original) { this.startTime = original.startTime(); this.mode = original.mode(); @@ -27,6 +30,7 @@ public class StreetSearchRequestBuilder { this.rental = original.rental(); this.from = original.from(); this.to = original.to(); + this.mobilityProfile = original.mobilityProfile(); } public StreetSearchRequestBuilder withStartTime(Instant startTime) { @@ -58,6 +62,11 @@ public StreetSearchRequestBuilder withWheelchair(boolean wheelchair) { return this; } + public StreetSearchRequestBuilder withMobilityProfile(MobilityProfile profile) { + this.mobilityProfile = profile; + return this; + } + public StreetSearchRequestBuilder withRental(VehicleRentalRequest rental) { this.rental = rental; return this; diff --git a/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequestMapper.java b/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequestMapper.java index 9f1f3c567f8..b2266693d79 100644 --- a/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequestMapper.java +++ b/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequestMapper.java @@ -13,7 +13,8 @@ public static StreetSearchRequestBuilder map(RouteRequest opt) { .withWheelchair(opt.wheelchair()) .withRental(opt.journey().rental()) .withFrom(opt.from()) - .withTo(opt.to()); + .withTo(opt.to()) + .withMobilityProfile(opt.mobilityProfile()); } public static StreetSearchRequestBuilder mapToTransferRequest(RouteRequest opt) { diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 8e3b0902199..62aa31ccfa2 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -3156,7 +3156,7 @@ type QueryType { "Preferences for vehicle parking" parking: VehicleParkingInput - mobilityProfile: MobilityProfile + mobilityProfile: String ## Deprecated fields, none of these have any effect on the routing anymore From 922bac7728b52b70aa66c51e1fe3d5850e9040c5 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 28 Dec 2023 16:08:17 -0500 Subject: [PATCH 011/111] refactor(VehicleRentalUpdater): Revert changes --- .../updater/vehicle_rental/VehicleRentalUpdater.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdater.java b/src/main/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdater.java index c1be994ba86..1e037a7c1c2 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdater.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdater.java @@ -29,7 +29,6 @@ import org.opentripplanner.street.model.RentalRestrictionExtension; import org.opentripplanner.street.model.edge.StreetEdge; import org.opentripplanner.street.model.vertex.VertexFactory; -import org.opentripplanner.street.model.vertex.VertexLabel; import org.opentripplanner.street.search.TraverseMode; import org.opentripplanner.street.search.TraverseModeSet; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -232,10 +231,6 @@ public void run(Graph graph, TransitModel transitModel) { tempEdgesByStation.remove(station); } - var vertex = graph.getVertex(VertexLabel.osm(139204728343L)); - - vertex.getOutgoingStreetEdges().stream().filter(e -> e.getToVertex().) - // this check relies on the generated equals for the record which also recursively checks that // the JTS geometries are equal if (!geofencingZones.isEmpty() && !geofencingZones.equals(latestAppliedGeofencingZones)) { From 64d521f8fa59570666fcd53eec629db5a5699fd4 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 12 Jan 2024 15:20:52 -0500 Subject: [PATCH 012/111] refactor(OsmModule): Apply refactor suggestions. --- .../apis/gtfs/mapping/RouteRequestMapper.java | 1 - .../graph_builder/module/osm/OsmModule.java | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java index 8d0a754f75a..e7a2e7c1d1b 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java @@ -58,7 +58,6 @@ public static RouteRequest toRouteRequest( context.transitService().getTimeZone() ); - callWith.argument("wheelchair", request::setWheelchair); callWith.argument("wheelchair", request::setWheelchair); callWith.argument("numItineraries", request::setNumItineraries); callWith.argument("searchWindow", (Long m) -> request.setSearchWindow(Duration.ofSeconds(m))); diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 678ecc966f3..d3188e0523f 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import org.locationtech.jts.geom.Coordinate; @@ -58,7 +59,7 @@ public class OsmModule implements GraphBuilderModule { private final VertexGenerator vertexGenerator; private final OsmDatabase osmdb; private ImmutableTable> mobilityProfileData; - private HashMap mappedMobilityProfileEntries; + private HashSet mappedMobilityProfileEntries; OsmModule( Collection providers, @@ -150,7 +151,7 @@ private void build() { // figure out which nodes that are actually intersections vertexGenerator.initIntersectionNodes(); - mappedMobilityProfileEntries = new HashMap<>(); + mappedMobilityProfileEntries = new HashSet<>(); buildBasicGraph(); buildWalkableAreas(!params.areaVisibility()); @@ -193,8 +194,7 @@ private void listUnusedMobilityCosts() { for (var cell : mobilityProfileData.cellSet()) { String key = getNodeKey(cell.getRowKey(), cell.getColumnKey()); - Boolean exists = mappedMobilityProfileEntries.get(key); - if (exists == null || !exists) { + if (!mappedMobilityProfileEntries.contains(key)) { unusedEntries.add(key); } } @@ -587,7 +587,7 @@ private StreetEdge getEdgeForStreet( endId ); // Keep tab of node pairs for which mobility profile costs have been mapped. - mappedMobilityProfileEntries.put(getNodeKey(startId, endId), true); + mappedMobilityProfileEntries.add(getNodeKey(startId, endId)); } if (!way.hasTag("name") && !way.hasTag("ref")) { From 8c4e90b9792d8b8d3063b79b80b75e10991841ce Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 12 Jan 2024 15:28:15 -0500 Subject: [PATCH 013/111] refactor(schema.graphqls): Remove MobilityProfile enum. --- .../opentripplanner/apis/gtfs/schema.graphqls | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index eca37564aa6..4ee1a5fbd92 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -2459,27 +2459,6 @@ enum Qualifier { HAIL } -enum MobilityProfile { - NONE, - SOME, - DEVICE, - WCHAIRM, - WCHAIRE, - MSCOOTER, - VISION, - VISIONPLUS, - SOME_VISION, - DEVICE_VISION, - WCHAIRM_VISION, - WCHAIRE_VISION, - MSCOOTER_VISION, - SOME_VISIONPLUS, - DEVICE_VISIONPLUS, - WCHAIRM_VISIONPLUS, - WCHAIRE_VISIONPLUS, - MSCOOTER_VISIONPLUS -} - type QueryType { """Fetches an object given its ID""" node( From a7ca12416a90cd3c4a72d26c3c3a291d51ee6cad Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 12 Jan 2024 15:45:22 -0500 Subject: [PATCH 014/111] refactor: Apply prettier formatting --- .../mobilityprofile/MobilityProfileParser.java | 9 ++++----- .../graph_builder/GraphBuilder.java | 2 +- .../graph_builder/module/osm/OsmModule.java | 15 +++++---------- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java index 863e4f0a92b..e534b72bd30 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java @@ -24,7 +24,9 @@ private MobilityProfileParser() {} * Process rows from the given CSV stream and build a table indexed by both the * upstream/downstream nodes, where each value is a map of costs by mobility profile. */ - public static ImmutableTable> parseData(InputStream is) { + public static ImmutableTable> parseData( + InputStream is + ) { try { var reader = new CsvReader(is, StandardCharsets.UTF_8); reader.setDelimiter(','); @@ -63,10 +65,7 @@ private static void parseRow( weightMap ); } catch (NumberFormatException | NullPointerException e) { - LOG.warn( - "Skipping mobility profile data at line {}: missing/invalid data", - lineNumber - ); + LOG.warn("Skipping mobility profile data at line {}: missing/invalid data", lineNumber); } } } diff --git a/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java b/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java index 0db1754c253..fd61872bf53 100644 --- a/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java +++ b/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java @@ -13,8 +13,8 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.datastore.api.DataSource; -import org.opentripplanner.ext.mobilityprofile.MobilityProfileParser; import org.opentripplanner.ext.emissions.EmissionsDataModel; +import org.opentripplanner.ext.mobilityprofile.MobilityProfileParser; import org.opentripplanner.ext.stopconsolidation.StopConsolidationRepository; import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.framework.application.OtpAppException; diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index d3188e0523f..273b7c90ff2 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -112,7 +112,9 @@ public Map elevationDataOutput() { return elevationData; } - public void setMobilityProfileData(ImmutableTable> mobilityProfileData) { + public void setMobilityProfileData( + ImmutableTable> mobilityProfileData + ) { this.mobilityProfileData = mobilityProfileData; } @@ -575,17 +577,10 @@ private StreetEdge getEdgeForStreet( // Lookup costs by mobility profile, if any were defined. String startId = startEndpoint.getLabel().toString(); String endId = endEndpoint.getLabel().toString(); - var edgeMobilityCostMap = mobilityProfileData.get( - startId, - endId - ); + var edgeMobilityCostMap = mobilityProfileData.get(startId, endId); if (edgeMobilityCostMap != null) { seb.withProfileCosts(edgeMobilityCostMap); - LOG.info( - "Applied mobility profile costs between nodes {}-{}", - startId, - endId - ); + LOG.info("Applied mobility profile costs between nodes {}-{}", startId, endId); // Keep tab of node pairs for which mobility profile costs have been mapped. mappedMobilityProfileEntries.add(getNodeKey(startId, endId)); } From db44c89cb329b14b1658644d4fb036804495c2b0 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 22 Jan 2024 17:59:49 -0500 Subject: [PATCH 015/111] fix(profileCost): Revert to use HashMap instead of EnumMap. --- .../ext/mobilityprofile/MobilityProfileParser.java | 4 ++-- .../opentripplanner/street/model/edge/StreetEdgeBuilder.java | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java index e534b72bd30..42048bf067c 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java @@ -5,7 +5,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.util.EnumMap; +import java.util.HashMap; import java.util.Map; import org.opentripplanner.street.model.vertex.VertexLabel; import org.slf4j.Logger; @@ -54,7 +54,7 @@ private static void parseRow( long fromNode = Long.parseLong(reader.get("Upstream Node"), 10); long toNode = Long.parseLong(reader.get("Downstream Node"), 10); - var weightMap = new EnumMap(MobilityProfile.class); + var weightMap = new HashMap(); for (var profile : MobilityProfile.values()) { weightMap.put(profile, Float.parseFloat(reader.get(profile.getText()))); } diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java index 221aca661f8..c7d20dd3c70 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java @@ -12,6 +12,7 @@ import static org.opentripplanner.street.model.edge.StreetEdge.WHEELCHAIR_ACCESSIBLE_FLAG_INDEX; import java.util.EnumMap; +import java.util.HashMap; import java.util.Map; import org.locationtech.jts.geom.LineString; import org.opentripplanner.ext.mobilityprofile.MobilityProfile; @@ -41,7 +42,7 @@ public class StreetEdgeBuilder> { private float bicycleSafetyFactor; private short flags; private StreetElevationExtension streetElevationExtension; - private Map profileCosts = new EnumMap<>(MobilityProfile.class); + private Map profileCosts = new HashMap<>(); // new EnumMap<>(MobilityProfile.class); public StreetEdgeBuilder() { this.defaultLength = true; From cdb152d86259ddc5ca3ffecebc83e009d6b88a0d Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 24 Jan 2024 16:58:21 -0500 Subject: [PATCH 016/111] refactor(MobilityProfileParser): Adjust CSV headers and improve logging of CSV parsing outcome --- .../ext/mobilityprofile/MobilityProfile.java | 24 +++++++++---------- .../MobilityProfileParser.java | 10 ++++++-- .../graph_builder/GraphBuilder.java | 8 ++++++- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfile.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfile.java index ec1bc5434e7..49b8df97169 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfile.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfile.java @@ -10,18 +10,18 @@ public enum MobilityProfile { WCHAIRM("WChairM"), WCHAIRE("WChairE"), MSCOOTER("MScooter"), - VISION("Vision"), - VISIONPLUS("Vision+"), - SOME_VISION("Some-Vision"), - DEVICE_VISION("Device-Vision"), - WCHAIRM_VISION("WChairM-Vision"), - WCHAIRE_VISION("WChairE-Vision"), - MSCOOTER_VISION("MScooter-Vision"), - SOME_VISIONPLUS("Some-Vision+"), - DEVICE_VISIONPLUS("Device-Vision+"), - WCHAIRM_VISIONPLUS("WChairM-Vision+"), - WCHAIRE_VISIONPLUS("WChairE-Vision+"), - MSCOOTER_VISIONPLUS("MScooter-Vision+"); + LOW_VISION("LowVision"), + BLIND("Blind"), + SOME_LOW_VISION("Some-LowVision"), + DEVICE_LOW_VISION("Device-LowVision"), + WCHAIRM_LOW_VISION("WChairM-LowVIsion"), // Typo in CSV header + WCHAIRE_LOW_VISION("WChairE-LowVision"), + MSCOOTER_LOW_VISION("Mscooter-LowVision"), // Typo in the CSV header + SOME_BLIND("Some-Blind"), + DEVICE_BLIND("Device-Blind"), + WCHAIRM_BLIND("WChairM-Blind"), + WCHAIRE_BLIND("WChairE-Blind"), + MSCOOTER_BLIND("Mscooter-Blind"); // Typo in the CSV header private final String text; diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java index 42048bf067c..11e156d1da5 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java @@ -50,13 +50,15 @@ private static void parseRow( CsvReader reader, ImmutableTable.Builder> tableBuilder ) throws IOException { + String currentColumnHeader = ""; try { long fromNode = Long.parseLong(reader.get("Upstream Node"), 10); long toNode = Long.parseLong(reader.get("Downstream Node"), 10); var weightMap = new HashMap(); for (var profile : MobilityProfile.values()) { - weightMap.put(profile, Float.parseFloat(reader.get(profile.getText()))); + currentColumnHeader = profile.getText(); + weightMap.put(profile, Float.parseFloat(reader.get(currentColumnHeader))); } tableBuilder.put( @@ -65,7 +67,11 @@ private static void parseRow( weightMap ); } catch (NumberFormatException | NullPointerException e) { - LOG.warn("Skipping mobility profile data at line {}: missing/invalid data", lineNumber); + LOG.warn( + "Skipping mobility profile data at line {}: missing/invalid data in column {}.", + lineNumber, + currentColumnHeader + ); } } } diff --git a/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java b/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java index fd61872bf53..0cc96bd9ad3 100644 --- a/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java +++ b/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java @@ -4,16 +4,19 @@ import static org.opentripplanner.datastore.api.FileType.NETEX; import static org.opentripplanner.datastore.api.FileType.OSM; +import com.google.common.collect.ImmutableTable; import jakarta.inject.Inject; import java.io.IOException; import java.time.Duration; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Optional; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.datastore.api.DataSource; import org.opentripplanner.ext.emissions.EmissionsDataModel; +import org.opentripplanner.ext.mobilityprofile.MobilityProfile; import org.opentripplanner.ext.mobilityprofile.MobilityProfileParser; import org.opentripplanner.ext.stopconsolidation.StopConsolidationRepository; import org.opentripplanner.framework.application.OTPFeature; @@ -106,7 +109,10 @@ public static GraphBuilder create( if (mobilityDataSource.isPresent()) { // Parse stuff from the mobility profile CSV try (var inputStream = mobilityDataSource.get().asInputStream()) { - osmModule.setMobilityProfileData(MobilityProfileParser.parseData(inputStream)); + ImmutableTable> mobilityProfileData = + MobilityProfileParser.parseData(inputStream); + LOG.info("Imported {} rows from mobility-profile.csv", mobilityProfileData.rowKeySet().size()); + osmModule.setMobilityProfileData(mobilityProfileData); } catch (IOException e) { throw new RuntimeException(e); } From ad406f46440993e3ea82e1daff3e9a5dbbc09bb8 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 25 Jan 2024 13:29:50 -0500 Subject: [PATCH 017/111] refactor(MobilityProfileParser): Move logging rows to parser class. --- .../ext/mobilityprofile/MobilityProfileParser.java | 4 +++- .../java/org/opentripplanner/graph_builder/GraphBuilder.java | 5 +---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java index 11e156d1da5..e626867fad8 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java @@ -39,7 +39,9 @@ public static ImmutableTable> parseD lineNumber++; } - return tableBuilder.build(); + var result = tableBuilder.build(); + LOG.info("Imported {} rows from mobility-profile.csv", result.size()); + return result; } catch (IOException e) { throw new RuntimeException(e); } diff --git a/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java b/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java index 0cc96bd9ad3..e0ef7c25a41 100644 --- a/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java +++ b/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java @@ -109,10 +109,7 @@ public static GraphBuilder create( if (mobilityDataSource.isPresent()) { // Parse stuff from the mobility profile CSV try (var inputStream = mobilityDataSource.get().asInputStream()) { - ImmutableTable> mobilityProfileData = - MobilityProfileParser.parseData(inputStream); - LOG.info("Imported {} rows from mobility-profile.csv", mobilityProfileData.rowKeySet().size()); - osmModule.setMobilityProfileData(mobilityProfileData); + osmModule.setMobilityProfileData(MobilityProfileParser.parseData(inputStream)); } catch (IOException e) { throw new RuntimeException(e); } From 5de005927e87b850bc89051a7a63d8a10ed127c5 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 26 Jan 2024 17:06:39 -0500 Subject: [PATCH 018/111] improvement(OsmModule): Check reverse key for cost profile existence. --- .../graph_builder/module/osm/OsmModule.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 273b7c90ff2..03000af298d 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -196,7 +196,8 @@ private void listUnusedMobilityCosts() { for (var cell : mobilityProfileData.cellSet()) { String key = getNodeKey(cell.getRowKey(), cell.getColumnKey()); - if (!mappedMobilityProfileEntries.contains(key)) { + String reverseKey = getNodeKey(cell.getColumnKey(), cell.getRowKey()); + if (!mappedMobilityProfileEntries.contains(key) && !mappedMobilityProfileEntries.contains(reverseKey)) { unusedEntries.add(key); } } @@ -575,9 +576,13 @@ private StreetEdge getEdgeForStreet( .withWheelchairAccessible(way.isWheelchairAccessible()); // Lookup costs by mobility profile, if any were defined. + // Note that edges are bidirectional, so we check both directions if mobility data exist. String startId = startEndpoint.getLabel().toString(); String endId = endEndpoint.getLabel().toString(); var edgeMobilityCostMap = mobilityProfileData.get(startId, endId); + if (edgeMobilityCostMap == null) { + edgeMobilityCostMap = mobilityProfileData.get(endId, startId); + } if (edgeMobilityCostMap != null) { seb.withProfileCosts(edgeMobilityCostMap); LOG.info("Applied mobility profile costs between nodes {}-{}", startId, endId); From 6a71d349e7c1b47cdbd60ca90cbadedf67486ea2 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 29 Jan 2024 15:45:37 -0500 Subject: [PATCH 019/111] fix(RouteRequest): Initialize with mobility profile 'None'. --- .../org/opentripplanner/routing/api/request/RouteRequest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java b/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java index cf111aa6c02..fde5c1f1e5f 100644 --- a/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java +++ b/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java @@ -82,7 +82,7 @@ public class RouteRequest implements Cloneable, Serializable { private boolean wheelchair = false; - private MobilityProfile mobilityProfile; + private MobilityProfile mobilityProfile = MobilityProfile.NONE; /* CONSTRUCTORS */ From 9691d98b2577992810fdc5dc9596f19ae1989f39 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 31 Jan 2024 14:56:02 -0500 Subject: [PATCH 020/111] improvement(OsmModule): Add indication of cost profile use between 2 OSM nodes. --- .../org/opentripplanner/graph_builder/module/osm/OsmModule.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 03000af298d..9e1abcd37c8 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -585,6 +585,8 @@ private StreetEdge getEdgeForStreet( } if (edgeMobilityCostMap != null) { seb.withProfileCosts(edgeMobilityCostMap); + // Append an indication that this edge uses a profile cost. + seb.withName(String.format("%s ☑", name)); LOG.info("Applied mobility profile costs between nodes {}-{}", startId, endId); // Keep tab of node pairs for which mobility profile costs have been mapped. mappedMobilityProfileEntries.add(getNodeKey(startId, endId)); From 53751c8c3da22dc2fb76ed44030226b021303fb5 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 1 Feb 2024 15:36:48 -0500 Subject: [PATCH 021/111] improvement(OsmModule): Print OSM node ids in edge names for testing. --- .../graph_builder/module/osm/OsmModule.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 9e1abcd37c8..8794e5f7496 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -575,10 +575,17 @@ private StreetEdge getEdgeForStreet( .withStairs(way.isSteps()) .withWheelchairAccessible(way.isWheelchairAccessible()); - // Lookup costs by mobility profile, if any were defined. - // Note that edges are bidirectional, so we check both directions if mobility data exist. String startId = startEndpoint.getLabel().toString(); String endId = endEndpoint.getLabel().toString(); + + // For testing, indicate the OSM node ids (remove prefixes). + String startShortId = startId.replace("osm:node:", ""); + String endShortId = endId.replace("osm:node:", ""); + String nameWithNodeIds = String.format("%s (%s→%s)", name, startShortId, endShortId); + seb.withName(nameWithNodeIds); + + // Lookup costs by mobility profile, if any were defined. + // Note that edges are bidirectional, so we check for mobility data exist in both directions. var edgeMobilityCostMap = mobilityProfileData.get(startId, endId); if (edgeMobilityCostMap == null) { edgeMobilityCostMap = mobilityProfileData.get(endId, startId); @@ -586,8 +593,8 @@ private StreetEdge getEdgeForStreet( if (edgeMobilityCostMap != null) { seb.withProfileCosts(edgeMobilityCostMap); // Append an indication that this edge uses a profile cost. - seb.withName(String.format("%s ☑", name)); - LOG.info("Applied mobility profile costs between nodes {}-{}", startId, endId); + seb.withName(String.format("%s ☑", nameWithNodeIds)); + LOG.info("Applied mobility profile costs between nodes {}-{}", startShortId, endShortId); // Keep tab of node pairs for which mobility profile costs have been mapped. mappedMobilityProfileEntries.add(getNodeKey(startId, endId)); } From 1d1acef9128a3b53f85cd9c5df74705ef2d8e636 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 5 Feb 2024 12:46:11 -0500 Subject: [PATCH 022/111] feat(MobilityProfileRouting): Add default speeds per profile, use in edge travel time. --- .../MobilityProfileRoutingTest.java | 12 ++++++ .../MobilityProfileRouting.java | 40 +++++++++++++++++++ .../street/model/edge/StreetEdge.java | 11 ++++- 3 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java create mode 100644 src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java diff --git a/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java b/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java new file mode 100644 index 00000000000..bf043624a5a --- /dev/null +++ b/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java @@ -0,0 +1,12 @@ +package org.opentripplanner.ext.mobilityprofile; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class MobilityProfileRoutingTest { + @Test + void canComputeTravelTime() { + assertEquals(0.250 / 1.609 / 2.5, MobilityProfileRouting.computeTravelHours(250, MobilityProfile.NONE)); + } +} diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java new file mode 100644 index 00000000000..93fe22f1114 --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java @@ -0,0 +1,40 @@ +package org.opentripplanner.ext.mobilityprofile; + +import static java.util.Map.entry; + +import java.util.EnumMap; +import java.util.Map; + +public class MobilityProfileRouting { + private static final Map TRAVEL_SPEED_MPH_BY_PROFILE = new EnumMap<>(Map.ofEntries( + entry(MobilityProfile.NONE, 2.5f), + entry(MobilityProfile.SOME, 2.0f), + entry(MobilityProfile.DEVICE, 1.5f), + entry(MobilityProfile.WCHAIRM, 3.0f), + entry(MobilityProfile.WCHAIRE, 4.0f), + entry(MobilityProfile.MSCOOTER, 5.0f), + entry(MobilityProfile.LOW_VISION, 2.0f), + entry(MobilityProfile.BLIND, 1.5f), + entry(MobilityProfile.SOME_LOW_VISION, 2.0f), + entry(MobilityProfile.DEVICE_LOW_VISION, 1.5f), + entry(MobilityProfile.WCHAIRM_LOW_VISION, 2.0f), + entry(MobilityProfile.WCHAIRE_LOW_VISION,2.0f), + entry(MobilityProfile.MSCOOTER_LOW_VISION, 2.0f), + entry(MobilityProfile.SOME_BLIND, 2.0f), + entry(MobilityProfile.DEVICE_BLIND, 1.5f), + entry(MobilityProfile.WCHAIRM_BLIND, 2.0f), + entry(MobilityProfile.WCHAIRE_BLIND, 1.5f), + entry(MobilityProfile.MSCOOTER_BLIND, 2.0f) + )); + + public static final double ONE_MILE_IN_KILOMETERS = 1.609; + + private MobilityProfileRouting() { + // Np public constructor. + } + + /** Computes the travel time, in minutes, for the given distance and mobility profile. */ + public static float computeTravelHours(double meters, MobilityProfile mobilityProfile) { + return (float)(meters / 1000 / ONE_MILE_IN_KILOMETERS / TRAVEL_SPEED_MPH_BY_PROFILE.getOrDefault(mobilityProfile, TRAVEL_SPEED_MPH_BY_PROFILE.get(MobilityProfile.NONE))); + } +} diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index eb984a9e501..5c03fbe2b37 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -15,6 +15,7 @@ import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.impl.PackedCoordinateSequence; import org.opentripplanner.ext.mobilityprofile.MobilityProfile; +import org.opentripplanner.ext.mobilityprofile.MobilityProfileRouting; import org.opentripplanner.framework.geometry.CompactLineStringUtils; import org.opentripplanner.framework.geometry.DirectionUtils; import org.opentripplanner.framework.geometry.GeometryUtils; @@ -1307,8 +1308,14 @@ private TraversalCosts walkingTraversalCosts( isStairs() ); } - var profileMultiplier = profileCost.getOrDefault(mobilityProfile, 1f); - weight = weight * profileMultiplier; + + if (profileCost != null) { + // G-MAP-specific: Travel time on select street edges are provided through profileCost + // (assuming a pre-determined travel speed for each profile) + // and are used to overwrite the time calculated above (convert from hours to seconds). + var travelTimeHours = profileCost.getOrDefault(mobilityProfile, MobilityProfileRouting.computeTravelHours(getEffectiveWalkDistance(), mobilityProfile)); + time = travelTimeHours * 3600; + } return new TraversalCosts(time, weight); } From 7776c978ce60768ee125df466fe3f1dd69568267 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 5 Feb 2024 12:53:01 -0500 Subject: [PATCH 023/111] style: Apply prettier --- .../MobilityProfileRoutingTest.java | 6 ++- .../MobilityProfileRouting.java | 53 +++++++++++-------- .../graph_builder/module/osm/OsmModule.java | 5 +- .../street/model/edge/StreetEdge.java | 5 +- 4 files changed, 45 insertions(+), 24 deletions(-) diff --git a/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java b/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java index bf043624a5a..78cd6c5b5d3 100644 --- a/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java @@ -5,8 +5,12 @@ import org.junit.jupiter.api.Test; class MobilityProfileRoutingTest { + @Test void canComputeTravelTime() { - assertEquals(0.250 / 1.609 / 2.5, MobilityProfileRouting.computeTravelHours(250, MobilityProfile.NONE)); + assertEquals( + 0.250 / 1.609 / 2.5, + MobilityProfileRouting.computeTravelHours(250, MobilityProfile.NONE) + ); } } diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java index 93fe22f1114..4c9ed6f0971 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java @@ -6,26 +6,29 @@ import java.util.Map; public class MobilityProfileRouting { - private static final Map TRAVEL_SPEED_MPH_BY_PROFILE = new EnumMap<>(Map.ofEntries( - entry(MobilityProfile.NONE, 2.5f), - entry(MobilityProfile.SOME, 2.0f), - entry(MobilityProfile.DEVICE, 1.5f), - entry(MobilityProfile.WCHAIRM, 3.0f), - entry(MobilityProfile.WCHAIRE, 4.0f), - entry(MobilityProfile.MSCOOTER, 5.0f), - entry(MobilityProfile.LOW_VISION, 2.0f), - entry(MobilityProfile.BLIND, 1.5f), - entry(MobilityProfile.SOME_LOW_VISION, 2.0f), - entry(MobilityProfile.DEVICE_LOW_VISION, 1.5f), - entry(MobilityProfile.WCHAIRM_LOW_VISION, 2.0f), - entry(MobilityProfile.WCHAIRE_LOW_VISION,2.0f), - entry(MobilityProfile.MSCOOTER_LOW_VISION, 2.0f), - entry(MobilityProfile.SOME_BLIND, 2.0f), - entry(MobilityProfile.DEVICE_BLIND, 1.5f), - entry(MobilityProfile.WCHAIRM_BLIND, 2.0f), - entry(MobilityProfile.WCHAIRE_BLIND, 1.5f), - entry(MobilityProfile.MSCOOTER_BLIND, 2.0f) - )); + + private static final Map TRAVEL_SPEED_MPH_BY_PROFILE = new EnumMap<>( + Map.ofEntries( + entry(MobilityProfile.NONE, 2.5f), + entry(MobilityProfile.SOME, 2.0f), + entry(MobilityProfile.DEVICE, 1.5f), + entry(MobilityProfile.WCHAIRM, 3.0f), + entry(MobilityProfile.WCHAIRE, 4.0f), + entry(MobilityProfile.MSCOOTER, 5.0f), + entry(MobilityProfile.LOW_VISION, 2.0f), + entry(MobilityProfile.BLIND, 1.5f), + entry(MobilityProfile.SOME_LOW_VISION, 2.0f), + entry(MobilityProfile.DEVICE_LOW_VISION, 1.5f), + entry(MobilityProfile.WCHAIRM_LOW_VISION, 2.0f), + entry(MobilityProfile.WCHAIRE_LOW_VISION, 2.0f), + entry(MobilityProfile.MSCOOTER_LOW_VISION, 2.0f), + entry(MobilityProfile.SOME_BLIND, 2.0f), + entry(MobilityProfile.DEVICE_BLIND, 1.5f), + entry(MobilityProfile.WCHAIRM_BLIND, 2.0f), + entry(MobilityProfile.WCHAIRE_BLIND, 1.5f), + entry(MobilityProfile.MSCOOTER_BLIND, 2.0f) + ) + ); public static final double ONE_MILE_IN_KILOMETERS = 1.609; @@ -35,6 +38,14 @@ private MobilityProfileRouting() { /** Computes the travel time, in minutes, for the given distance and mobility profile. */ public static float computeTravelHours(double meters, MobilityProfile mobilityProfile) { - return (float)(meters / 1000 / ONE_MILE_IN_KILOMETERS / TRAVEL_SPEED_MPH_BY_PROFILE.getOrDefault(mobilityProfile, TRAVEL_SPEED_MPH_BY_PROFILE.get(MobilityProfile.NONE))); + return (float) ( + meters / + 1000 / + ONE_MILE_IN_KILOMETERS / + TRAVEL_SPEED_MPH_BY_PROFILE.getOrDefault( + mobilityProfile, + TRAVEL_SPEED_MPH_BY_PROFILE.get(MobilityProfile.NONE) + ) + ); } } diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 8794e5f7496..129b3b049cb 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -197,7 +197,10 @@ private void listUnusedMobilityCosts() { for (var cell : mobilityProfileData.cellSet()) { String key = getNodeKey(cell.getRowKey(), cell.getColumnKey()); String reverseKey = getNodeKey(cell.getColumnKey(), cell.getRowKey()); - if (!mappedMobilityProfileEntries.contains(key) && !mappedMobilityProfileEntries.contains(reverseKey)) { + if ( + !mappedMobilityProfileEntries.contains(key) && + !mappedMobilityProfileEntries.contains(reverseKey) + ) { unusedEntries.add(key); } } diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index 5c03fbe2b37..2f759a3fc2e 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -1313,7 +1313,10 @@ private TraversalCosts walkingTraversalCosts( // G-MAP-specific: Travel time on select street edges are provided through profileCost // (assuming a pre-determined travel speed for each profile) // and are used to overwrite the time calculated above (convert from hours to seconds). - var travelTimeHours = profileCost.getOrDefault(mobilityProfile, MobilityProfileRouting.computeTravelHours(getEffectiveWalkDistance(), mobilityProfile)); + var travelTimeHours = profileCost.getOrDefault( + mobilityProfile, + MobilityProfileRouting.computeTravelHours(getEffectiveWalkDistance(), mobilityProfile) + ); time = travelTimeHours * 3600; } return new TraversalCosts(time, weight); From 85d68f9395255db1ea779c27c18f351ab4d5fc2a Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 5 Feb 2024 13:05:22 -0500 Subject: [PATCH 024/111] fix(StreetEdge): Fix condition for returning default time. --- .../street/model/edge/StreetEdge.java | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index 2f759a3fc2e..3c87531973d 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -1272,7 +1272,6 @@ private TraversalCosts walkingTraversalCosts( ) { double time, weight; if (wheelchair) { - time = getEffectiveWalkDistance() / speed; weight = (getEffectiveBikeDistance() / speed) * StreetEdgeReluctanceCalculator.computeWheelchairReluctance( @@ -1284,14 +1283,13 @@ private TraversalCosts walkingTraversalCosts( } else { if (walkingBike) { // take slopes into account when walking bikes - time = weight = (getEffectiveBikeDistance() / speed); + weight = (getEffectiveBikeDistance() / speed); if (isStairs()) { // we do allow walking the bike across a stairs but there is a very high default penalty weight *= preferences.bike().walking().stairsReluctance(); } } else { // take slopes into account when walking - time = getEffectiveWalkDistance() / speed; weight = getEffectiveWalkSafetyDistance() * preferences.walk().safetyFactor() + @@ -1309,15 +1307,20 @@ private TraversalCosts walkingTraversalCosts( ); } + // G-MAP-specific: Tabulated travel times are provided through profileCost + // (assuming a pre-determined travel speed for each profile) + // and are used to overwrite the time calculated above (convert from hours to seconds). + // If no tabulated times are available for the edge, compute them using the + // travel speeds for the given mobility profile. + var defaultTravelHours = MobilityProfileRouting.computeTravelHours( + getEffectiveWalkDistance(), + mobilityProfile + ); if (profileCost != null) { - // G-MAP-specific: Travel time on select street edges are provided through profileCost - // (assuming a pre-determined travel speed for each profile) - // and are used to overwrite the time calculated above (convert from hours to seconds). - var travelTimeHours = profileCost.getOrDefault( - mobilityProfile, - MobilityProfileRouting.computeTravelHours(getEffectiveWalkDistance(), mobilityProfile) - ); + var travelTimeHours = profileCost.getOrDefault(mobilityProfile, defaultTravelHours); time = travelTimeHours * 3600; + } else { + time = defaultTravelHours * 3600; } return new TraversalCosts(time, weight); } From 3aedec0be339f33845991bd48ad04ab2698d3d03 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 5 Feb 2024 13:19:05 -0500 Subject: [PATCH 025/111] refactor(OsmModule): Accommodate for null mobility profile test cases. --- .../graph_builder/module/osm/OsmModule.java | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 129b3b049cb..19a801c03a4 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -589,17 +589,19 @@ private StreetEdge getEdgeForStreet( // Lookup costs by mobility profile, if any were defined. // Note that edges are bidirectional, so we check for mobility data exist in both directions. - var edgeMobilityCostMap = mobilityProfileData.get(startId, endId); - if (edgeMobilityCostMap == null) { - edgeMobilityCostMap = mobilityProfileData.get(endId, startId); - } - if (edgeMobilityCostMap != null) { - seb.withProfileCosts(edgeMobilityCostMap); - // Append an indication that this edge uses a profile cost. - seb.withName(String.format("%s ☑", nameWithNodeIds)); - LOG.info("Applied mobility profile costs between nodes {}-{}", startShortId, endShortId); - // Keep tab of node pairs for which mobility profile costs have been mapped. - mappedMobilityProfileEntries.add(getNodeKey(startId, endId)); + if (mobilityProfileData != null) { + var edgeMobilityCostMap = mobilityProfileData.get(startId, endId); + if (edgeMobilityCostMap == null) { + edgeMobilityCostMap = mobilityProfileData.get(endId, startId); + } + if (edgeMobilityCostMap != null) { + seb.withProfileCosts(edgeMobilityCostMap); + // Append an indication that this edge uses a profile cost. + seb.withName(String.format("%s ☑", nameWithNodeIds)); + LOG.info("Applied mobility profile costs between nodes {}-{}", startShortId, endShortId); + // Keep tab of node pairs for which mobility profile costs have been mapped. + mappedMobilityProfileEntries.add(getNodeKey(startId, endId)); + } } if (!way.hasTag("name") && !way.hasTag("ref")) { From 8d4ca0263281361102c77c47edf49004c82d8251 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 5 Feb 2024 13:26:02 -0500 Subject: [PATCH 026/111] fix(OsmModule): Accommodate null mobility profile data. --- .../graph_builder/module/osm/OsmModule.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 19a801c03a4..0d683d2fcc9 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -194,14 +194,16 @@ private void build() { private void listUnusedMobilityCosts() { var unusedEntries = new ArrayList(); - for (var cell : mobilityProfileData.cellSet()) { - String key = getNodeKey(cell.getRowKey(), cell.getColumnKey()); - String reverseKey = getNodeKey(cell.getColumnKey(), cell.getRowKey()); - if ( - !mappedMobilityProfileEntries.contains(key) && - !mappedMobilityProfileEntries.contains(reverseKey) - ) { - unusedEntries.add(key); + if (mobilityProfileData != null) { + for (var cell : mobilityProfileData.cellSet()) { + String key = getNodeKey(cell.getRowKey(), cell.getColumnKey()); + String reverseKey = getNodeKey(cell.getColumnKey(), cell.getRowKey()); + if ( + !mappedMobilityProfileEntries.contains(key) && + !mappedMobilityProfileEntries.contains(reverseKey) + ) { + unusedEntries.add(key); + } } } From 246a786af5dd948512ab9ceafdf40eca9caa7dc4 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 5 Feb 2024 14:58:59 -0500 Subject: [PATCH 027/111] style(OsmModule): Apply prettier --- .../org/opentripplanner/graph_builder/module/osm/OsmModule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 0d683d2fcc9..472c3affab3 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -200,7 +200,7 @@ private void listUnusedMobilityCosts() { String reverseKey = getNodeKey(cell.getColumnKey(), cell.getRowKey()); if ( !mappedMobilityProfileEntries.contains(key) && - !mappedMobilityProfileEntries.contains(reverseKey) + !mappedMobilityProfileEntries.contains(reverseKey) ) { unusedEntries.add(key); } From 072f384d560feb4290651d642f49d0ad282405d7 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 20 Feb 2024 13:42:33 -0500 Subject: [PATCH 028/111] refactor(MobilityProfile): Accommodate fixed enum typos. --- .../ext/mobilityprofile/MobilityProfile.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfile.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfile.java index 49b8df97169..7e7788f57ab 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfile.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfile.java @@ -14,14 +14,14 @@ public enum MobilityProfile { BLIND("Blind"), SOME_LOW_VISION("Some-LowVision"), DEVICE_LOW_VISION("Device-LowVision"), - WCHAIRM_LOW_VISION("WChairM-LowVIsion"), // Typo in CSV header + WCHAIRM_LOW_VISION("WChairM-LowVision"), WCHAIRE_LOW_VISION("WChairE-LowVision"), - MSCOOTER_LOW_VISION("Mscooter-LowVision"), // Typo in the CSV header + MSCOOTER_LOW_VISION("MScooter-LowVision"), SOME_BLIND("Some-Blind"), DEVICE_BLIND("Device-Blind"), WCHAIRM_BLIND("WChairM-Blind"), WCHAIRE_BLIND("WChairE-Blind"), - MSCOOTER_BLIND("Mscooter-Blind"); // Typo in the CSV header + MSCOOTER_BLIND("MScooter-Blind"); private final String text; From 454d7ac510df306d62615a0eda04d9e52826c59c Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 20 Feb 2024 13:50:10 -0500 Subject: [PATCH 029/111] improvement(MobilityProfileParser): Process mobility profile csv rows as much as possible --- .../ext/mobilityprofile/MobilityProfileParser.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java index e626867fad8..a5b57e7ef86 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java @@ -60,7 +60,15 @@ private static void parseRow( var weightMap = new HashMap(); for (var profile : MobilityProfile.values()) { currentColumnHeader = profile.getText(); - weightMap.put(profile, Float.parseFloat(reader.get(currentColumnHeader))); + try { + weightMap.put(profile, Float.parseFloat(reader.get(currentColumnHeader))); + } catch (NumberFormatException | NullPointerException e) { + LOG.warn( + "Ignoring missing/invalid data at line {}, column {}.", + lineNumber, + currentColumnHeader + ); + } } tableBuilder.put( From 41a50776aa922014e31484f7edaa31d19f5a099a Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 5 Mar 2024 09:21:17 -0500 Subject: [PATCH 030/111] refactor(GraphBuilder): Add comment for using OTP-built-in store --- .../java/org/opentripplanner/graph_builder/GraphBuilder.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java b/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java index e0ef7c25a41..59cf4ff8457 100644 --- a/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java +++ b/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java @@ -4,19 +4,16 @@ import static org.opentripplanner.datastore.api.FileType.NETEX; import static org.opentripplanner.datastore.api.FileType.OSM; -import com.google.common.collect.ImmutableTable; import jakarta.inject.Inject; import java.io.IOException; import java.time.Duration; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.Optional; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.datastore.api.DataSource; import org.opentripplanner.ext.emissions.EmissionsDataModel; -import org.opentripplanner.ext.mobilityprofile.MobilityProfile; import org.opentripplanner.ext.mobilityprofile.MobilityProfileParser; import org.opentripplanner.ext.stopconsolidation.StopConsolidationRepository; import org.opentripplanner.framework.application.OTPFeature; @@ -104,6 +101,7 @@ public static GraphBuilder create( graphBuilder.hasTransitData = hasTransitData; if (hasOsm) { + // TODO: Use the OTP-built-in data source mechanism (see #5677). Optional mobilityDataSource = dataSources.mobilityProfileDataSource(); OsmModule osmModule = factory.osmModule(); if (mobilityDataSource.isPresent()) { From e55654ade29564c454a3b0b6732bdf7a1407bbfe Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 5 Mar 2024 18:45:34 -0500 Subject: [PATCH 031/111] improvement(MobilityProfileRouting): Forbid ped routing on non-footways. --- .../MobilityProfileRoutingTest.java | 45 ++++++++++++++++++- .../MobilityProfileRouting.java | 17 +++++++ .../graph_builder/module/osm/OsmModule.java | 12 +++-- 3 files changed, 69 insertions(+), 5 deletions(-) diff --git a/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java b/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java index 78cd6c5b5d3..203c4ed8193 100644 --- a/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java @@ -1,8 +1,12 @@ package org.opentripplanner.ext.mobilityprofile; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; +import org.opentripplanner.openstreetmap.model.OSMWay; +import org.opentripplanner.street.model.StreetTraversalPermission; class MobilityProfileRoutingTest { @@ -10,7 +14,46 @@ class MobilityProfileRoutingTest { void canComputeTravelTime() { assertEquals( 0.250 / 1.609 / 2.5, - MobilityProfileRouting.computeTravelHours(250, MobilityProfile.NONE) + MobilityProfileRouting.computeTravelHours(250, MobilityProfile.NONE), + 1e-6 + ); + } + + @Test + void canDetectHighwayFootwayTag() { + assertTrue(MobilityProfileRouting.isHighwayFootway(createFootway())); + assertFalse(MobilityProfileRouting.isHighwayFootway(createServiceWay())); + } + + private static OSMWay createServiceWay() { + OSMWay serviceWay = new OSMWay(); + serviceWay.addTag("highway", "service"); + return serviceWay; + } + + private static OSMWay createFootway() { + OSMWay footway = new OSMWay(); + footway.addTag("highway", "footway"); + return footway; + } + + @Test + void canRemoveWalkPermissionOnNonFootway() { + OSMWay serviceWay = createServiceWay(); + StreetTraversalPermission permissions = StreetTraversalPermission.ALL; + assertEquals( + StreetTraversalPermission.BICYCLE_AND_CAR, + MobilityProfileRouting.adjustPedestrianPermissions(serviceWay, permissions) + ); + } + + @Test + void canPreserveWalkPermissionOnFootway() { + OSMWay footway = createFootway(); + StreetTraversalPermission permissions = StreetTraversalPermission.ALL; + assertEquals( + StreetTraversalPermission.ALL, + MobilityProfileRouting.adjustPedestrianPermissions(footway, permissions) ); } } diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java index 4c9ed6f0971..be490b948d7 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java @@ -4,9 +4,15 @@ import java.util.EnumMap; import java.util.Map; +import org.opentripplanner.openstreetmap.model.OSMWay; +import org.opentripplanner.street.model.StreetTraversalPermission; public class MobilityProfileRouting { + public static final String HIGHWAY_TAG = "highway"; + + public static final String FOOTWAY_TAG_VALUE = "footway"; + private static final Map TRAVEL_SPEED_MPH_BY_PROFILE = new EnumMap<>( Map.ofEntries( entry(MobilityProfile.NONE, 2.5f), @@ -48,4 +54,15 @@ public static float computeTravelHours(double meters, MobilityProfile mobilityPr ) ); } + + public static boolean isHighwayFootway(OSMWay way) { + return way.hasTag(HIGHWAY_TAG) && FOOTWAY_TAG_VALUE.equals(way.getTag(HIGHWAY_TAG)); + } + + public static StreetTraversalPermission adjustPedestrianPermissions( + OSMWay way, + StreetTraversalPermission permissions + ) { + return isHighwayFootway(way) ? permissions : permissions.remove(StreetTraversalPermission.PEDESTRIAN); + } } diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 472c3affab3..21512c63705 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -13,6 +13,7 @@ import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.LineString; import org.opentripplanner.ext.mobilityprofile.MobilityProfile; +import org.opentripplanner.ext.mobilityprofile.MobilityProfileRouting; import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.framework.geometry.SphericalDistanceLibrary; import org.opentripplanner.framework.i18n.I18NString; @@ -564,6 +565,7 @@ private StreetEdge getEdgeForStreet( label = label.intern(); I18NString name = params.edgeNamer().getNameForWay(way, label); float carSpeed = way.getOsmProvider().getOsmTagMapper().getCarSpeedForWay(way, back); + StreetTraversalPermission perms = MobilityProfileRouting.adjustPedestrianPermissions(way, permissions); StreetEdgeBuilder seb = new StreetEdgeBuilder<>() .withFromVertex(startEndpoint) @@ -571,7 +573,7 @@ private StreetEdge getEdgeForStreet( .withGeometry(geometry) .withName(name) .withMeterLength(length) - .withPermission(permissions) + .withPermission(MobilityProfileRouting.adjustPedestrianPermissions(way, permissions)) .withBack(back) .withCarSpeed(carSpeed) .withLink(way.isLink()) @@ -586,7 +588,7 @@ private StreetEdge getEdgeForStreet( // For testing, indicate the OSM node ids (remove prefixes). String startShortId = startId.replace("osm:node:", ""); String endShortId = endId.replace("osm:node:", ""); - String nameWithNodeIds = String.format("%s (%s→%s)", name, startShortId, endShortId); + String nameWithNodeIds = String.format("%s (%s, %s→%s)", name, way.getId(), startShortId, endShortId); seb.withName(nameWithNodeIds); // Lookup costs by mobility profile, if any were defined. @@ -599,12 +601,14 @@ private StreetEdge getEdgeForStreet( if (edgeMobilityCostMap != null) { seb.withProfileCosts(edgeMobilityCostMap); // Append an indication that this edge uses a profile cost. - seb.withName(String.format("%s ☑", nameWithNodeIds)); - LOG.info("Applied mobility profile costs between nodes {}-{}", startShortId, endShortId); + nameWithNodeIds = String.format("%s ☑", nameWithNodeIds); + seb.withName(nameWithNodeIds); + // LOG.info("Applied mobility profile costs between nodes {}-{}", startShortId, endShortId); // Keep tab of node pairs for which mobility profile costs have been mapped. mappedMobilityProfileEntries.add(getNodeKey(startId, endId)); } } + System.out.printf("Way: %s - %s%n", nameWithNodeIds, perms.name()); if (!way.hasTag("name") && !way.hasTag("ref")) { seb.withBogusName(true); From ecdb0e9c722ef0272a6f3f4b0f861b3a7a921129 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 5 Mar 2024 18:47:00 -0500 Subject: [PATCH 032/111] improvement(StreetEdge): Apply mobility profiles or penalties on walk modes only. --- .../street/model/edge/StreetEdge.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index 3c87531973d..c1eb0bfb67f 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -1316,9 +1316,17 @@ private TraversalCosts walkingTraversalCosts( getEffectiveWalkDistance(), mobilityProfile ); - if (profileCost != null) { - var travelTimeHours = profileCost.getOrDefault(mobilityProfile, defaultTravelHours); - time = travelTimeHours * 3600; + + if (traverseMode.isWalking()) { + if (profileCost != null) { + var travelTimeHours = profileCost.getOrDefault(mobilityProfile, defaultTravelHours); + time = travelTimeHours * 3600; + weight /= 20; + } else { + // Assign a high travel time (x1000) to non-tabulated ways. + time = 360000; + weight *= 10000; + } } else { time = defaultTravelHours * 3600; } From aa0700390ae069c196adb2184992ceca8edab7a3 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 7 Mar 2024 10:52:10 -0500 Subject: [PATCH 033/111] fix(StreetEdge): Use impedances as weight, use computed travel time otherwise. --- .../opentripplanner/street/model/edge/StreetEdge.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index c1eb0bfb67f..dd7a262b8a4 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -1319,13 +1319,13 @@ private TraversalCosts walkingTraversalCosts( if (traverseMode.isWalking()) { if (profileCost != null) { - var travelTimeHours = profileCost.getOrDefault(mobilityProfile, defaultTravelHours); - time = travelTimeHours * 3600; - weight /= 20; + var travelTimeHours = profileCost.getOrDefault(mobilityProfile, 10000f); + time = defaultTravelHours * 3600; + weight = travelTimeHours; } else { - // Assign a high travel time (x1000) to non-tabulated ways. + // Assign a high travel time and weight to non-tabulated ways. time = 360000; - weight *= 10000; + weight = 100000; } } else { time = defaultTravelHours * 3600; From 9c8cc4c25774c5f922058d0df23fbd5c89962b32 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 7 Mar 2024 18:17:34 -0500 Subject: [PATCH 034/111] improvement(TemporaryPartialStreetEdge): Use distance-prorated profile costs. --- .../MobilityProfileRoutingTest.java | 36 +++++++++++++++++++ .../MobilityProfileRouting.java | 12 +++++++ .../edge/TemporaryPartialStreetEdge.java | 2 ++ 3 files changed, 50 insertions(+) diff --git a/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java b/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java index 203c4ed8193..b134aa4df0c 100644 --- a/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java @@ -4,9 +4,17 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.HashMap; +import java.util.Map; import org.junit.jupiter.api.Test; import org.opentripplanner.openstreetmap.model.OSMWay; import org.opentripplanner.street.model.StreetTraversalPermission; +import org.opentripplanner.street.model.edge.StreetEdge; +import org.opentripplanner.street.model.edge.StreetEdgeBuilder; +import org.opentripplanner.street.model.edge.TemporaryPartialStreetEdge; +import org.opentripplanner.street.model.edge.TemporaryPartialStreetEdgeBuilder; +import org.opentripplanner.street.model.vertex.OsmVertex; +import org.opentripplanner.street.model.vertex.StreetVertex; class MobilityProfileRoutingTest { @@ -56,4 +64,32 @@ void canPreserveWalkPermissionOnFootway() { MobilityProfileRouting.adjustPedestrianPermissions(footway, permissions) ); } + + @Test + void canProRateProfileCosts() { + Map profileCost = new HashMap<>(); + profileCost.put(MobilityProfile.NONE, 10.0f); + profileCost.put(MobilityProfile.DEVICE, 100.0f); + + StreetVertex from = new OsmVertex(33.4, -84.5, 101); + StreetVertex to = new OsmVertex(33.5, -84.6, 102); + + StreetEdge edge = new StreetEdgeBuilder<>() + .withProfileCosts(profileCost) + .withFromVertex(from) + .withToVertex(to) + .withPermission(StreetTraversalPermission.ALL) + .withMeterLength(100) + .buildAndConnect(); + TemporaryPartialStreetEdge tmpEdge = new TemporaryPartialStreetEdgeBuilder() + .withParentEdge(edge) + .withFromVertex(from) + .withToVertex(to) + .withMeterLength(40) + .buildAndConnect(); + + Map proRatedProfileCost = MobilityProfileRouting.getProRatedProfileCosts(tmpEdge); + assertEquals(4.0f, proRatedProfileCost.get(MobilityProfile.NONE), 1e-6); + assertEquals(40.0f, proRatedProfileCost.get(MobilityProfile.DEVICE), 1e-6); + } } diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java index be490b948d7..abbfbdcee6e 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java @@ -6,6 +6,8 @@ import java.util.Map; import org.opentripplanner.openstreetmap.model.OSMWay; import org.opentripplanner.street.model.StreetTraversalPermission; +import org.opentripplanner.street.model.edge.StreetEdge; +import org.opentripplanner.street.model.edge.TemporaryPartialStreetEdge; public class MobilityProfileRouting { @@ -65,4 +67,14 @@ public static StreetTraversalPermission adjustPedestrianPermissions( ) { return isHighwayFootway(way) ? permissions : permissions.remove(StreetTraversalPermission.PEDESTRIAN); } + + /** Multiplies profile costs by the distance ratio between the given edge and its parent. */ + public static Map getProRatedProfileCosts(TemporaryPartialStreetEdge tmpEdge) { + StreetEdge parentEdge = tmpEdge.getParentEdge(); + float ratio = (float)(tmpEdge.getDistanceMeters() / parentEdge.getDistanceMeters()); + + Map result = new EnumMap<>(MobilityProfile.class); + parentEdge.profileCost.forEach((k, v) -> result.put(k, v * ratio)); + return result; + } } diff --git a/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java index 09846bb3aa1..21a9d7dee15 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java @@ -2,6 +2,7 @@ import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.LineString; +import org.opentripplanner.ext.mobilityprofile.MobilityProfileRouting; public final class TemporaryPartialStreetEdge extends StreetEdge implements TemporaryEdge { @@ -29,6 +30,7 @@ public final class TemporaryPartialStreetEdge extends StreetEdge implements Temp .addRentalRestriction(builder.parentEdge().getToVertex().rentalRestrictions()); this.parentEdge = builder.parentEdge(); this.geometry = super.getGeometry(); + this.profileCost = MobilityProfileRouting.getProRatedProfileCosts(this); } /** From 4aa887570cfc41a5dbcd642e8e69c738d579e434 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 7 Mar 2024 18:23:07 -0500 Subject: [PATCH 035/111] refactor(StreetEdge): Remove implied iswalking condition --- .../street/model/edge/StreetEdge.java | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index dd7a262b8a4..677bb6017ca 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -1317,18 +1317,14 @@ private TraversalCosts walkingTraversalCosts( mobilityProfile ); - if (traverseMode.isWalking()) { - if (profileCost != null) { - var travelTimeHours = profileCost.getOrDefault(mobilityProfile, 10000f); - time = defaultTravelHours * 3600; - weight = travelTimeHours; - } else { - // Assign a high travel time and weight to non-tabulated ways. - time = 360000; - weight = 100000; - } - } else { + if (profileCost != null) { + var travelTimeHours = profileCost.getOrDefault(mobilityProfile, 10000f); time = defaultTravelHours * 3600; + weight = travelTimeHours; + } else { + // Assign a high travel time and weight to non-tabulated ways. + time = 360000; + weight = 100000; } return new TraversalCosts(time, weight); } From b2226178cc6a1532ac9173b10348ab5a3c647307 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 11 Mar 2024 17:27:42 -0400 Subject: [PATCH 036/111] refactor(MobilityProfileParser): Map costs by way id/from/to info/ --- .../mobilityprofile/MobilityProfileData.java | 10 +++++ .../MobilityProfileParser.java | 34 +++++++++------ .../graph_builder/module/osm/OsmModule.java | 41 ++++++++++--------- 3 files changed, 54 insertions(+), 31 deletions(-) create mode 100644 src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileData.java diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileData.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileData.java new file mode 100644 index 00000000000..fea589ba48d --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileData.java @@ -0,0 +1,10 @@ +package org.opentripplanner.ext.mobilityprofile; + +import java.util.Map; +import javax.annotation.Nonnull; + +public record MobilityProfileData( + float lengthInMeters, + + @Nonnull Map costs +) {} diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java index a5b57e7ef86..fa92947eb83 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java @@ -1,7 +1,6 @@ package org.opentripplanner.ext.mobilityprofile; import com.csvreader.CsvReader; -import com.google.common.collect.ImmutableTable; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; @@ -24,7 +23,7 @@ private MobilityProfileParser() {} * Process rows from the given CSV stream and build a table indexed by both the * upstream/downstream nodes, where each value is a map of costs by mobility profile. */ - public static ImmutableTable> parseData( + public static Map parseData( InputStream is ) { try { @@ -32,31 +31,39 @@ public static ImmutableTable> parseD reader.setDelimiter(','); reader.readHeaders(); - ImmutableTable.Builder> tableBuilder = ImmutableTable.builder(); + Map map = new HashMap<>(); int lineNumber = 1; while (reader.readRecord()) { - parseRow(lineNumber, reader, tableBuilder); + parseRow(lineNumber, reader, map); lineNumber++; } - var result = tableBuilder.build(); - LOG.info("Imported {} rows from mobility-profile.csv", result.size()); - return result; + LOG.info("Imported {} rows from mobility-profile.csv", map.size()); + return map; } catch (IOException e) { throw new RuntimeException(e); } } + /** Helper to build a key of the form "id:from=>to" for an OSM way. */ + public static String getKey(String id, String from, String to) { + return String.format("%s:%s=>%s", id, from, to); + } + private static void parseRow( int lineNumber, CsvReader reader, - ImmutableTable.Builder> tableBuilder + Map map ) throws IOException { String currentColumnHeader = ""; try { long fromNode = Long.parseLong(reader.get("Upstream Node"), 10); long toNode = Long.parseLong(reader.get("Downstream Node"), 10); + String id = reader.get("Way Id"); + float length = Float.parseFloat(reader.get("Link Length")); + // The weight map has to be a HashMap instead of an EnumMap so that it is correctly + // persisted in the graph. var weightMap = new HashMap(); for (var profile : MobilityProfile.values()) { currentColumnHeader = profile.getText(); @@ -71,10 +78,13 @@ private static void parseRow( } } - tableBuilder.put( - VertexLabel.osm(fromNode).toString(), - VertexLabel.osm(toNode).toString(), - weightMap + map.put( + getKey( + id, + VertexLabel.osm(fromNode).toString(), + VertexLabel.osm(toNode).toString() + ), + new MobilityProfileData(length, weightMap) ); } catch (NumberFormatException | NullPointerException e) { LOG.warn( diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 21512c63705..f2131ffc89a 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -1,6 +1,5 @@ package org.opentripplanner.graph_builder.module.osm; -import com.google.common.collect.ImmutableTable; import com.google.common.collect.Iterables; import gnu.trove.iterator.TLongIterator; import java.util.ArrayList; @@ -12,7 +11,8 @@ import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.LineString; -import org.opentripplanner.ext.mobilityprofile.MobilityProfile; +import org.opentripplanner.ext.mobilityprofile.MobilityProfileData; +import org.opentripplanner.ext.mobilityprofile.MobilityProfileParser; import org.opentripplanner.ext.mobilityprofile.MobilityProfileRouting; import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.framework.geometry.SphericalDistanceLibrary; @@ -59,9 +59,11 @@ public class OsmModule implements GraphBuilderModule { private final SafetyValueNormalizer normalizer; private final VertexGenerator vertexGenerator; private final OsmDatabase osmdb; - private ImmutableTable> mobilityProfileData; + private Map mobilityProfileData; private HashSet mappedMobilityProfileEntries; + private Map> mappedWays; + OsmModule( Collection providers, Graph graph, @@ -114,7 +116,7 @@ public Map elevationDataOutput() { } public void setMobilityProfileData( - ImmutableTable> mobilityProfileData + Map mobilityProfileData ) { this.mobilityProfileData = mobilityProfileData; } @@ -155,6 +157,7 @@ private void build() { vertexGenerator.initIntersectionNodes(); mappedMobilityProfileEntries = new HashSet<>(); + mappedWays = new HashMap<>(); buildBasicGraph(); buildWalkableAreas(!params.areaVisibility()); @@ -196,12 +199,10 @@ private void listUnusedMobilityCosts() { var unusedEntries = new ArrayList(); if (mobilityProfileData != null) { - for (var cell : mobilityProfileData.cellSet()) { - String key = getNodeKey(cell.getRowKey(), cell.getColumnKey()); - String reverseKey = getNodeKey(cell.getColumnKey(), cell.getRowKey()); + for (var cell : mobilityProfileData.entrySet()) { + String key = cell.getKey(); if ( - !mappedMobilityProfileEntries.contains(key) && - !mappedMobilityProfileEntries.contains(reverseKey) + !mappedMobilityProfileEntries.contains(key) ) { unusedEntries.add(key); } @@ -592,23 +593,29 @@ private StreetEdge getEdgeForStreet( seb.withName(nameWithNodeIds); // Lookup costs by mobility profile, if any were defined. - // Note that edges are bidirectional, so we check for mobility data exist in both directions. + // Note that edges are bidirectional, so we check that mobility data exist in both directions. + String keyForWay = null; if (mobilityProfileData != null) { - var edgeMobilityCostMap = mobilityProfileData.get(startId, endId); + keyForWay = MobilityProfileParser.getKey(Long.toString(way.getId(), 10), startId, endId); + var edgeMobilityCostMap = mobilityProfileData.get(keyForWay); if (edgeMobilityCostMap == null) { - edgeMobilityCostMap = mobilityProfileData.get(endId, startId); + keyForWay = MobilityProfileParser.getKey(Long.toString(way.getId(), 10), endId, startId); + edgeMobilityCostMap = mobilityProfileData.get(keyForWay); } - if (edgeMobilityCostMap != null) { - seb.withProfileCosts(edgeMobilityCostMap); + if (keyForWay != null && edgeMobilityCostMap != null) { + seb.withProfileCosts(edgeMobilityCostMap.costs()); // Append an indication that this edge uses a profile cost. nameWithNodeIds = String.format("%s ☑", nameWithNodeIds); seb.withName(nameWithNodeIds); // LOG.info("Applied mobility profile costs between nodes {}-{}", startShortId, endShortId); // Keep tab of node pairs for which mobility profile costs have been mapped. - mappedMobilityProfileEntries.add(getNodeKey(startId, endId)); + mappedMobilityProfileEntries.add(keyForWay); } } System.out.printf("Way: %s - %s%n", nameWithNodeIds, perms.name()); + // Update list of mapped ways + + if (!way.hasTag("name") && !way.hasTag("ref")) { seb.withBogusName(true); @@ -624,8 +631,4 @@ private StreetEdge getEdgeForStreet( return street; } - - private static String getNodeKey(String startId, String endId) { - return String.format("%s=>%s", startId, endId); - } } From 0bc93b95f8d7f850bd2501cb8533d043b4c54edf Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 11 Mar 2024 17:42:52 -0400 Subject: [PATCH 037/111] refactor(MobilityProfileData): Add from and to nodes to data structure --- .../ext/mobilityprofile/MobilityProfileData.java | 4 ++++ .../ext/mobilityprofile/MobilityProfileParser.java | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileData.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileData.java index fea589ba48d..37cdd02d4f1 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileData.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileData.java @@ -6,5 +6,9 @@ public record MobilityProfileData( float lengthInMeters, + long fromNode, + + long toNode, + @Nonnull Map costs ) {} diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java index fa92947eb83..40b1292a19e 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java @@ -84,7 +84,7 @@ private static void parseRow( VertexLabel.osm(fromNode).toString(), VertexLabel.osm(toNode).toString() ), - new MobilityProfileData(length, weightMap) + new MobilityProfileData(length, fromNode, toNode, weightMap) ); } catch (NumberFormatException | NullPointerException e) { LOG.warn( From ed33ba12844fe01504395f5abe26c6a87482d4af Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 12 Mar 2024 15:15:25 -0400 Subject: [PATCH 038/111] fix(MobilityProfileRouting): Map mobility profile data using way id --- .../MobilityProfileParser.java | 13 ++-- .../MobilityProfileRouting.java | 9 ++- .../graph_builder/module/osm/OsmModule.java | 75 ++++++++++--------- 3 files changed, 51 insertions(+), 46 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java index 40b1292a19e..a52e42fd82f 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java @@ -6,7 +6,6 @@ import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; -import org.opentripplanner.street.model.vertex.VertexLabel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -17,6 +16,8 @@ public class MobilityProfileParser { private static final Logger LOG = LoggerFactory.getLogger(MobilityProfileParser.class); + private static final int ONE_MILE_IN_METERS = 1609; + private MobilityProfileParser() {} /** @@ -60,7 +61,7 @@ private static void parseRow( long fromNode = Long.parseLong(reader.get("Upstream Node"), 10); long toNode = Long.parseLong(reader.get("Downstream Node"), 10); String id = reader.get("Way Id"); - float length = Float.parseFloat(reader.get("Link Length")); + float lengthMeters = ONE_MILE_IN_METERS * Float.parseFloat(reader.get("Link Length")); // The weight map has to be a HashMap instead of an EnumMap so that it is correctly // persisted in the graph. @@ -79,12 +80,8 @@ private static void parseRow( } map.put( - getKey( - id, - VertexLabel.osm(fromNode).toString(), - VertexLabel.osm(toNode).toString() - ), - new MobilityProfileData(length, fromNode, toNode, weightMap) + id, + new MobilityProfileData(lengthMeters, fromNode, toNode, weightMap) ); } catch (NumberFormatException | NullPointerException e) { LOG.warn( diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java index abbfbdcee6e..9c96d18fe2a 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java @@ -3,6 +3,7 @@ import static java.util.Map.entry; import java.util.EnumMap; +import java.util.HashMap; import java.util.Map; import org.opentripplanner.openstreetmap.model.OSMWay; import org.opentripplanner.street.model.StreetTraversalPermission; @@ -72,9 +73,13 @@ public static StreetTraversalPermission adjustPedestrianPermissions( public static Map getProRatedProfileCosts(TemporaryPartialStreetEdge tmpEdge) { StreetEdge parentEdge = tmpEdge.getParentEdge(); float ratio = (float)(tmpEdge.getDistanceMeters() / parentEdge.getDistanceMeters()); + return getProRatedProfileCosts(parentEdge.profileCost, ratio); + } - Map result = new EnumMap<>(MobilityProfile.class); - parentEdge.profileCost.forEach((k, v) -> result.put(k, v * ratio)); + public static Map getProRatedProfileCosts(Map cost, float ratio) { + // Has to be a HashMap for graph serialization + Map result = new HashMap<>(); + cost.forEach((k, v) -> result.put(k, v * ratio)); return result; } } diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index f2131ffc89a..0a8c3ce076a 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -12,7 +12,6 @@ import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.LineString; import org.opentripplanner.ext.mobilityprofile.MobilityProfileData; -import org.opentripplanner.ext.mobilityprofile.MobilityProfileParser; import org.opentripplanner.ext.mobilityprofile.MobilityProfileRouting; import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.framework.geometry.SphericalDistanceLibrary; @@ -62,8 +61,6 @@ public class OsmModule implements GraphBuilderModule { private Map mobilityProfileData; private HashSet mappedMobilityProfileEntries; - private Map> mappedWays; - OsmModule( Collection providers, Graph graph, @@ -157,7 +154,6 @@ private void build() { vertexGenerator.initIntersectionNodes(); mappedMobilityProfileEntries = new HashSet<>(); - mappedWays = new HashMap<>(); buildBasicGraph(); buildWalkableAreas(!params.areaVisibility()); @@ -196,26 +192,19 @@ private void build() { * Lists unused entries from the mobility profile data. */ private void listUnusedMobilityCosts() { - var unusedEntries = new ArrayList(); - if (mobilityProfileData != null) { - for (var cell : mobilityProfileData.entrySet()) { - String key = cell.getKey(); - if ( - !mappedMobilityProfileEntries.contains(key) - ) { - unusedEntries.add(key); + List unusedEntries = mobilityProfileData.keySet().stream() + .filter(key -> !mappedMobilityProfileEntries.contains(key)) + .toList(); + + if (!unusedEntries.isEmpty()) { + StringBuilder sb = new StringBuilder(); + for (var entry : unusedEntries) { + sb.append(String.format("%n- %s", entry)); } + LOG.warn("{} mobility profile entries were not used:{}", unusedEntries.size(), sb); } } - - if (!unusedEntries.isEmpty()) { - StringBuilder sb = new StringBuilder(); - for (var entry : unusedEntries) { - sb.append(String.format("%n- %s", entry)); - } - LOG.warn("{} mobility profile entries were not used:{}", unusedEntries.size(), sb); - } } /** @@ -594,28 +583,42 @@ private StreetEdge getEdgeForStreet( // Lookup costs by mobility profile, if any were defined. // Note that edges are bidirectional, so we check that mobility data exist in both directions. - String keyForWay = null; if (mobilityProfileData != null) { - keyForWay = MobilityProfileParser.getKey(Long.toString(way.getId(), 10), startId, endId); - var edgeMobilityCostMap = mobilityProfileData.get(keyForWay); - if (edgeMobilityCostMap == null) { - keyForWay = MobilityProfileParser.getKey(Long.toString(way.getId(), 10), endId, startId); - edgeMobilityCostMap = mobilityProfileData.get(keyForWay); - } - if (keyForWay != null && edgeMobilityCostMap != null) { - seb.withProfileCosts(edgeMobilityCostMap.costs()); - // Append an indication that this edge uses a profile cost. - nameWithNodeIds = String.format("%s ☑", nameWithNodeIds); + String wayId = Long.toString(way.getId(), 10); + var edgeMobilityCostMap = mobilityProfileData.get(wayId); + if (edgeMobilityCostMap != null) { + // Check whether the nodes for this way match the nodes from mobility profile data. + if ( + startShortId.equals(Long.toString(edgeMobilityCostMap.fromNode(), 10)) && + endShortId.equals(Long.toString(edgeMobilityCostMap.toNode(), 10)) || + startShortId.equals(Long.toString(edgeMobilityCostMap.toNode(), 10)) && + endShortId.equals(Long.toString(edgeMobilityCostMap.fromNode(), 10)) + ) { + // If the from/to nodes match, then assign the cost directly + seb.withProfileCosts(edgeMobilityCostMap.costs()); + + // Append an indication that this edge uses a full profile cost. + nameWithNodeIds = String.format("%s ☑", nameWithNodeIds); + System.out.printf("Way (full length): %s%n", nameWithNodeIds); + } else { + // Otherwise, pro-rate the cost to the length of the edge. + float ratio = (float)length / edgeMobilityCostMap.lengthInMeters(); + seb.withProfileCosts(MobilityProfileRouting.getProRatedProfileCosts( + edgeMobilityCostMap.costs(), + ratio + )); + + // Append an indication that this edge uses a partial profile cost. + nameWithNodeIds = String.format("%s r%4.3f", nameWithNodeIds, ratio); + System.out.printf("Way (partial): %s%n", nameWithNodeIds); + } + seb.withName(nameWithNodeIds); // LOG.info("Applied mobility profile costs between nodes {}-{}", startShortId, endShortId); // Keep tab of node pairs for which mobility profile costs have been mapped. - mappedMobilityProfileEntries.add(keyForWay); + mappedMobilityProfileEntries.add(wayId); } } - System.out.printf("Way: %s - %s%n", nameWithNodeIds, perms.name()); - // Update list of mapped ways - - if (!way.hasTag("name") && !way.hasTag("ref")) { seb.withBogusName(true); From 5d1bb624df8f056608ba15e50dc5c10de6561fc6 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 14 Mar 2024 12:15:31 -0400 Subject: [PATCH 039/111] fix(StreetEdge): Copy mobility costs when splitting street edges --- .../java/org/opentripplanner/street/model/edge/StreetEdge.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index 677bb6017ca..ffb58089dc9 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -695,6 +695,9 @@ public SplitStreetEdge splitDestructively(SplitterVertex v) { seb1.withMilliMeterLength(l1); seb2.withMilliMeterLength(l2); + seb1.withProfileCosts(MobilityProfileRouting.getProRatedProfileCosts(profileCost, (float)l1 / length_mm)); + seb2.withProfileCosts(MobilityProfileRouting.getProRatedProfileCosts(profileCost, (float)l2 / length_mm)); + copyPropertiesToSplitEdge(seb1, 0, l1 / 1000.0); copyPropertiesToSplitEdge(seb2, l1 / 1000.0, getDistanceMeters()); From fbddde0fb878863359b923a485cce4e62edac688 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 27 Mar 2024 12:01:04 -0400 Subject: [PATCH 040/111] refactor: Print additional edge information. --- .../graph_builder/module/osm/OsmModule.java | 17 +++++++++++------ .../street/model/edge/StreetEdge.java | 11 +++++++++-- .../model/edge/TemporaryPartialStreetEdge.java | 3 +++ 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 0a8c3ce076a..3afed77ab52 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -11,6 +11,7 @@ import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.LineString; +import org.opentripplanner.ext.mobilityprofile.MobilityProfile; import org.opentripplanner.ext.mobilityprofile.MobilityProfileData; import org.opentripplanner.ext.mobilityprofile.MobilityProfileRouting; import org.opentripplanner.framework.geometry.GeometryUtils; @@ -599,18 +600,22 @@ private StreetEdge getEdgeForStreet( // Append an indication that this edge uses a full profile cost. nameWithNodeIds = String.format("%s ☑", nameWithNodeIds); - System.out.printf("Way (full length): %s%n", nameWithNodeIds); + // System.out.printf("Way (full length): %s size %d%n", nameWithNodeIds, edgeMobilityCostMap.costs().size()); + System.out.printf("%s %f%n", nameWithNodeIds, edgeMobilityCostMap.costs().get(MobilityProfile.WCHAIRE)); } else { // Otherwise, pro-rate the cost to the length of the edge. - float ratio = (float)length / edgeMobilityCostMap.lengthInMeters(); - seb.withProfileCosts(MobilityProfileRouting.getProRatedProfileCosts( + float ratio = (float)(length / edgeMobilityCostMap.lengthInMeters()); + + Map proRatedProfileCosts = MobilityProfileRouting.getProRatedProfileCosts( edgeMobilityCostMap.costs(), ratio - )); + ); + seb.withProfileCosts(proRatedProfileCosts); // Append an indication that this edge uses a partial profile cost. - nameWithNodeIds = String.format("%s r%4.3f", nameWithNodeIds, ratio); - System.out.printf("Way (partial): %s%n", nameWithNodeIds); + nameWithNodeIds = String.format("%s r%4.3f l%4.3f", nameWithNodeIds, ratio, length); + // System.out.printf("Way (partial): %s size %d%n", nameWithNodeIds, proRatedProfileCosts.size()); + System.out.printf("%s %f%n", nameWithNodeIds, proRatedProfileCosts.get(MobilityProfile.WCHAIRE)); } seb.withName(nameWithNodeIds); diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index ffb58089dc9..d0231707d81 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -695,8 +695,15 @@ public SplitStreetEdge splitDestructively(SplitterVertex v) { seb1.withMilliMeterLength(l1); seb2.withMilliMeterLength(l2); - seb1.withProfileCosts(MobilityProfileRouting.getProRatedProfileCosts(profileCost, (float)l1 / length_mm)); - seb2.withProfileCosts(MobilityProfileRouting.getProRatedProfileCosts(profileCost, (float)l2 / length_mm)); + if (!profileCost.isEmpty()) { + float ratio1 = (float) l1 / length_mm; + float ratio2 = (float) l2 / length_mm; + seb1.withProfileCosts(MobilityProfileRouting.getProRatedProfileCosts(profileCost, ratio1)); + seb2.withProfileCosts(MobilityProfileRouting.getProRatedProfileCosts(profileCost, ratio2)); + + seb1.withName(String.format("%s split r%4.3f l%4.3f", name, ratio1, l1 / 1000.0)); + seb2.withName(String.format("%s split r%4.3f l%4.3f", name, ratio2, l2 / 1000.0)); + } copyPropertiesToSplitEdge(seb1, 0, l1 / 1000.0); copyPropertiesToSplitEdge(seb2, l1 / 1000.0, getDistanceMeters()); diff --git a/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java index 21a9d7dee15..248758e88db 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java @@ -3,6 +3,7 @@ import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.LineString; import org.opentripplanner.ext.mobilityprofile.MobilityProfileRouting; +import org.opentripplanner.framework.i18n.LocalizedString; public final class TemporaryPartialStreetEdge extends StreetEdge implements TemporaryEdge { @@ -31,6 +32,8 @@ public final class TemporaryPartialStreetEdge extends StreetEdge implements Temp this.parentEdge = builder.parentEdge(); this.geometry = super.getGeometry(); this.profileCost = MobilityProfileRouting.getProRatedProfileCosts(this); + float ratio = (float)(getDistanceMeters() / getParentEdge().getDistanceMeters()); + this.setName(new LocalizedString(String.format("%s tmp r%4.3f l%4.3f", builder.parentEdge().getName(), ratio, getDistanceMeters()))); } /** From 3fcbd9d75935ccb4a8c97836138ba07fb325ca73 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 28 Mar 2024 12:01:30 -0400 Subject: [PATCH 041/111] style: Apply prettier --- .../MobilityProfileRoutingTest.java | 4 ++- .../MobilityProfileParser.java | 9 ++--- .../MobilityProfileRouting.java | 15 +++++--- .../graph_builder/module/osm/OsmModule.java | 35 ++++++++++++++----- .../edge/TemporaryPartialStreetEdge.java | 13 +++++-- 5 files changed, 53 insertions(+), 23 deletions(-) diff --git a/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java b/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java index b134aa4df0c..62ba94ca3af 100644 --- a/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java @@ -88,7 +88,9 @@ void canProRateProfileCosts() { .withMeterLength(40) .buildAndConnect(); - Map proRatedProfileCost = MobilityProfileRouting.getProRatedProfileCosts(tmpEdge); + Map proRatedProfileCost = MobilityProfileRouting.getProRatedProfileCosts( + tmpEdge + ); assertEquals(4.0f, proRatedProfileCost.get(MobilityProfile.NONE), 1e-6); assertEquals(40.0f, proRatedProfileCost.get(MobilityProfile.DEVICE), 1e-6); } diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java index a52e42fd82f..561afed18db 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java @@ -24,9 +24,7 @@ private MobilityProfileParser() {} * Process rows from the given CSV stream and build a table indexed by both the * upstream/downstream nodes, where each value is a map of costs by mobility profile. */ - public static Map parseData( - InputStream is - ) { + public static Map parseData(InputStream is) { try { var reader = new CsvReader(is, StandardCharsets.UTF_8); reader.setDelimiter(','); @@ -79,10 +77,7 @@ private static void parseRow( } } - map.put( - id, - new MobilityProfileData(lengthMeters, fromNode, toNode, weightMap) - ); + map.put(id, new MobilityProfileData(lengthMeters, fromNode, toNode, weightMap)); } catch (NumberFormatException | NullPointerException e) { LOG.warn( "Skipping mobility profile data at line {}: missing/invalid data in column {}.", diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java index 9c96d18fe2a..db37a85d4ac 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java @@ -66,17 +66,24 @@ public static StreetTraversalPermission adjustPedestrianPermissions( OSMWay way, StreetTraversalPermission permissions ) { - return isHighwayFootway(way) ? permissions : permissions.remove(StreetTraversalPermission.PEDESTRIAN); + return isHighwayFootway(way) + ? permissions + : permissions.remove(StreetTraversalPermission.PEDESTRIAN); } /** Multiplies profile costs by the distance ratio between the given edge and its parent. */ - public static Map getProRatedProfileCosts(TemporaryPartialStreetEdge tmpEdge) { + public static Map getProRatedProfileCosts( + TemporaryPartialStreetEdge tmpEdge + ) { StreetEdge parentEdge = tmpEdge.getParentEdge(); - float ratio = (float)(tmpEdge.getDistanceMeters() / parentEdge.getDistanceMeters()); + float ratio = (float) (tmpEdge.getDistanceMeters() / parentEdge.getDistanceMeters()); return getProRatedProfileCosts(parentEdge.profileCost, ratio); } - public static Map getProRatedProfileCosts(Map cost, float ratio) { + public static Map getProRatedProfileCosts( + Map cost, + float ratio + ) { // Has to be a HashMap for graph serialization Map result = new HashMap<>(); cost.forEach((k, v) -> result.put(k, v * ratio)); diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 21303eee32a..f12f9ea4b73 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -119,9 +119,7 @@ public Map elevationDataOutput() { return elevationData; } - public void setMobilityProfileData( - Map mobilityProfileData - ) { + public void setMobilityProfileData(Map mobilityProfileData) { this.mobilityProfileData = mobilityProfileData; } @@ -200,7 +198,9 @@ private void build() { */ private void listUnusedMobilityCosts() { if (mobilityProfileData != null) { - List unusedEntries = mobilityProfileData.keySet().stream() + List unusedEntries = mobilityProfileData + .keySet() + .stream() .filter(key -> !mappedMobilityProfileEntries.contains(key)) .toList(); @@ -562,7 +562,10 @@ private StreetEdge getEdgeForStreet( label = label.intern(); I18NString name = params.edgeNamer().getNameForWay(way, label); float carSpeed = way.getOsmProvider().getOsmTagMapper().getCarSpeedForWay(way, back); - StreetTraversalPermission perms = MobilityProfileRouting.adjustPedestrianPermissions(way, permissions); + StreetTraversalPermission perms = MobilityProfileRouting.adjustPedestrianPermissions( + way, + permissions + ); StreetEdgeBuilder seb = new StreetEdgeBuilder<>() .withFromVertex(startEndpoint) @@ -585,7 +588,13 @@ private StreetEdge getEdgeForStreet( // For testing, indicate the OSM node ids (remove prefixes). String startShortId = startId.replace("osm:node:", ""); String endShortId = endId.replace("osm:node:", ""); - String nameWithNodeIds = String.format("%s (%s, %s→%s)", name, way.getId(), startShortId, endShortId); + String nameWithNodeIds = String.format( + "%s (%s, %s→%s)", + name, + way.getId(), + startShortId, + endShortId + ); seb.withName(nameWithNodeIds); // Lookup costs by mobility profile, if any were defined. @@ -607,10 +616,14 @@ private StreetEdge getEdgeForStreet( // Append an indication that this edge uses a full profile cost. nameWithNodeIds = String.format("%s ☑", nameWithNodeIds); // System.out.printf("Way (full length): %s size %d%n", nameWithNodeIds, edgeMobilityCostMap.costs().size()); - System.out.printf("%s %f%n", nameWithNodeIds, edgeMobilityCostMap.costs().get(MobilityProfile.WCHAIRE)); + System.out.printf( + "%s %f%n", + nameWithNodeIds, + edgeMobilityCostMap.costs().get(MobilityProfile.WCHAIRE) + ); } else { // Otherwise, pro-rate the cost to the length of the edge. - float ratio = (float)(length / edgeMobilityCostMap.lengthInMeters()); + float ratio = (float) (length / edgeMobilityCostMap.lengthInMeters()); Map proRatedProfileCosts = MobilityProfileRouting.getProRatedProfileCosts( edgeMobilityCostMap.costs(), @@ -621,7 +634,11 @@ private StreetEdge getEdgeForStreet( // Append an indication that this edge uses a partial profile cost. nameWithNodeIds = String.format("%s r%4.3f l%4.3f", nameWithNodeIds, ratio, length); // System.out.printf("Way (partial): %s size %d%n", nameWithNodeIds, proRatedProfileCosts.size()); - System.out.printf("%s %f%n", nameWithNodeIds, proRatedProfileCosts.get(MobilityProfile.WCHAIRE)); + System.out.printf( + "%s %f%n", + nameWithNodeIds, + proRatedProfileCosts.get(MobilityProfile.WCHAIRE) + ); } seb.withName(nameWithNodeIds); diff --git a/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java index 21cff6eda4d..a9af8a25822 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java @@ -32,8 +32,17 @@ public final class TemporaryPartialStreetEdge extends StreetEdge implements Temp this.parentEdge = builder.parentEdge(); this.geometry = super.getGeometry(); this.profileCost = MobilityProfileRouting.getProRatedProfileCosts(this); - float ratio = (float)(getDistanceMeters() / getParentEdge().getDistanceMeters()); - this.setName(new LocalizedString(String.format("%s tmp r%4.3f l%4.3f", builder.parentEdge().getName(), ratio, getDistanceMeters()))); + float ratio = (float) (getDistanceMeters() / getParentEdge().getDistanceMeters()); + this.setName( + new LocalizedString( + String.format( + "%s tmp r%4.3f l%4.3f", + builder.parentEdge().getName(), + ratio, + getDistanceMeters() + ) + ) + ); } /** From 8a10dacce7e46968002817436aa7391201d9b430 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 28 Mar 2024 12:02:49 -0400 Subject: [PATCH 042/111] refactor(GraphBuilderDataSources): Add missing import --- .../opentripplanner/graph_builder/GraphBuilderDataSources.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/opentripplanner/graph_builder/GraphBuilderDataSources.java b/src/main/java/org/opentripplanner/graph_builder/GraphBuilderDataSources.java index de26a107fb5..46031f9f749 100644 --- a/src/main/java/org/opentripplanner/graph_builder/GraphBuilderDataSources.java +++ b/src/main/java/org/opentripplanner/graph_builder/GraphBuilderDataSources.java @@ -19,6 +19,7 @@ import org.opentripplanner.datastore.api.DataSource; import org.opentripplanner.datastore.api.FileType; import org.opentripplanner.datastore.api.OtpBaseDirectory; +import org.opentripplanner.datastore.file.FileDataSource; import org.opentripplanner.framework.application.OtpAppException; import org.opentripplanner.graph_builder.module.ned.parameter.DemExtractParameters; import org.opentripplanner.graph_builder.module.ned.parameter.DemExtractParametersBuilder; From fc51a61c91c32f5333d6cb1cdf2ab8fa0218d517 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 28 Mar 2024 12:15:31 -0400 Subject: [PATCH 043/111] docs(BuildConfiguration): Update documentation --- docs/BuildConfiguration.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/BuildConfiguration.md b/docs/BuildConfiguration.md index 3a3a6ff7ee4..8ddea513c75 100644 --- a/docs/BuildConfiguration.md +++ b/docs/BuildConfiguration.md @@ -33,6 +33,7 @@ Sections follow that describe particular settings in more depth. | maxElevationPropagationMeters | `integer` | The maximum distance to propagate elevation to vertices which have no elevation. | *Optional* | `2000` | 1.5 | | [maxStopToShapeSnapDistance](#maxStopToShapeSnapDistance) | `double` | Maximum distance between route shapes and their stops. | *Optional* | `150.0` | 2.1 | | maxTransferDuration | `duration` | Transfers up to this duration with the default walk speed value will be pre-calculated and included in the Graph. | *Optional* | `"PT30M"` | 2.1 | +| mobilityProfileFile | `string` | Name of the CSV-formatted file in the build directory which contains the routing costs by mobility profile. | *Optional* | | 2.5 | | [multiThreadElevationCalculations](#multiThreadElevationCalculations) | `boolean` | Configuring multi-threading during elevation calculations. | *Optional* | `false` | 2.0 | | [osmCacheDataInMem](#osmCacheDataInMem) | `boolean` | If OSM data should be cached in memory during processing. | *Optional* | `false` | 2.0 | | osmNaming | `string` | A custom OSM namer to use. | *Optional* | | 2.0 | From 26ec7f098a1d12e07f5a07d6806facba3f1d60bf Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 28 Mar 2024 12:22:45 -0400 Subject: [PATCH 044/111] test(speedtest): Update test results --- .../resources/speedtest/travelSearch-expected-results-bd.csv | 2 -- .../speedtest/travelSearch-expected-results-bdr.csv | 2 -- .../resources/speedtest/travelSearch-expected-results-bt.csv | 2 -- .../speedtest/travelSearch-expected-results-btr.csv | 4 ---- .../resources/speedtest/travelSearch-expected-results-mc.csv | 4 ---- .../resources/speedtest/travelSearch-expected-results-md.csv | 4 ---- .../resources/speedtest/travelSearch-expected-results-sr.csv | 4 ---- .../speedtest/travelSearch-expected-results-srr.csv | 5 ----- 8 files changed, 27 deletions(-) diff --git a/src/test/resources/speedtest/travelSearch-expected-results-bd.csv b/src/test/resources/speedtest/travelSearch-expected-results-bd.csv index 117eecc4a12..abe9e692b1f 100644 --- a/src/test/resources/speedtest/travelSearch-expected-results-bd.csv +++ b/src/test/resources/speedtest/travelSearch-expected-results-bd.csv @@ -1,3 +1 @@ tcId,nTransfers,duration,cost,walkDistance,startTime,endTime,agencies,modes,routes,stops,details -1,0,43m25s,-1,0,12:00:00,12:43:25,,,,,Unknown transit 0tx 43m25s -2,0,43m25s,-1,0,12:00:00,12:43:25,,,,,Unknown transit 0tx 43m25s diff --git a/src/test/resources/speedtest/travelSearch-expected-results-bdr.csv b/src/test/resources/speedtest/travelSearch-expected-results-bdr.csv index 4308492da33..abe9e692b1f 100644 --- a/src/test/resources/speedtest/travelSearch-expected-results-bdr.csv +++ b/src/test/resources/speedtest/travelSearch-expected-results-bdr.csv @@ -1,3 +1 @@ tcId,nTransfers,duration,cost,walkDistance,startTime,endTime,agencies,modes,routes,stops,details -1,0,43m3s,-1,0,13:16:57,14:00:00,,,,,Unknown transit 0tx 43m3s -2,0,43m3s,-1,0,13:16:57,14:00:00,,,,,Unknown transit 0tx 43m3s diff --git a/src/test/resources/speedtest/travelSearch-expected-results-bt.csv b/src/test/resources/speedtest/travelSearch-expected-results-bt.csv index 3d457aa7c22..abe9e692b1f 100644 --- a/src/test/resources/speedtest/travelSearch-expected-results-bt.csv +++ b/src/test/resources/speedtest/travelSearch-expected-results-bt.csv @@ -1,3 +1 @@ tcId,nTransfers,duration,cost,walkDistance,startTime,endTime,agencies,modes,routes,stops,details -1,0,1h35s,-1,0,12:00:00,13:00:35,,,,,Unknown transit 0tx 1h35s -2,0,1h35s,-1,0,12:00:00,13:00:35,,,,,Unknown transit 0tx 1h35s diff --git a/src/test/resources/speedtest/travelSearch-expected-results-btr.csv b/src/test/resources/speedtest/travelSearch-expected-results-btr.csv index 634b7b15b1e..abe9e692b1f 100644 --- a/src/test/resources/speedtest/travelSearch-expected-results-btr.csv +++ b/src/test/resources/speedtest/travelSearch-expected-results-btr.csv @@ -1,5 +1 @@ tcId,nTransfers,duration,cost,walkDistance,startTime,endTime,agencies,modes,routes,stops,details -1,0,1h11m14s,-1,0,12:48:46,14:00:00,,,,,Unknown transit 0tx 1h11m14s -1,1,1h9m14s,-1,0,12:50:46,14:00:00,,,,,Unknown transit 1tx 1h9m14s -2,0,1h11m14s,-1,0,12:48:46,14:00:00,,,,,Unknown transit 0tx 1h11m14s -2,1,1h9m14s,-1,0,12:50:46,14:00:00,,,,,Unknown transit 1tx 1h9m14s diff --git a/src/test/resources/speedtest/travelSearch-expected-results-mc.csv b/src/test/resources/speedtest/travelSearch-expected-results-mc.csv index 252d31eeeb3..abe9e692b1f 100644 --- a/src/test/resources/speedtest/travelSearch-expected-results-mc.csv +++ b/src/test/resources/speedtest/travelSearch-expected-results-mc.csv @@ -1,5 +1 @@ tcId,nTransfers,duration,cost,walkDistance,startTime,endTime,agencies,modes,routes,stops,details -1,0,56m49s,5095,1458,12:03:46,13:00:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:11:20 12:49:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s -1,0,56m49s,5095,1458,12:18:46,13:15:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:26:20 13:04:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s -1,0,56m49s,5095,1458,12:33:46,13:30:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:41:20 13:19:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s -2,0,56m49s,5095,1458,12:03:46,13:00:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:11:20 12:49:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s diff --git a/src/test/resources/speedtest/travelSearch-expected-results-md.csv b/src/test/resources/speedtest/travelSearch-expected-results-md.csv index 252d31eeeb3..abe9e692b1f 100644 --- a/src/test/resources/speedtest/travelSearch-expected-results-md.csv +++ b/src/test/resources/speedtest/travelSearch-expected-results-md.csv @@ -1,5 +1 @@ tcId,nTransfers,duration,cost,walkDistance,startTime,endTime,agencies,modes,routes,stops,details -1,0,56m49s,5095,1458,12:03:46,13:00:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:11:20 12:49:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s -1,0,56m49s,5095,1458,12:18:46,13:15:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:26:20 13:04:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s -1,0,56m49s,5095,1458,12:33:46,13:30:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:41:20 13:19:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s -2,0,56m49s,5095,1458,12:03:46,13:00:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:11:20 12:49:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s diff --git a/src/test/resources/speedtest/travelSearch-expected-results-sr.csv b/src/test/resources/speedtest/travelSearch-expected-results-sr.csv index faeebbb95ed..abe9e692b1f 100644 --- a/src/test/resources/speedtest/travelSearch-expected-results-sr.csv +++ b/src/test/resources/speedtest/travelSearch-expected-results-sr.csv @@ -1,5 +1 @@ tcId,nTransfers,duration,cost,walkDistance,startTime,endTime,agencies,modes,routes,stops,details -1,0,56m49s,0,1458,12:03:46,13:00:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:11:20 12:49:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s -1,0,56m49s,0,1458,12:18:46,13:15:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:26:20 13:04:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s -1,0,56m49s,0,1458,12:33:46,13:30:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:41:20 13:19:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s -2,0,58m8s,0,1507,12:02:27,13:00:35,TriMet,BUS,8,prt:6779|prt:6548,Walk 8m18s ~ NE 15th & Weidler(6779) ~ BUS 8 12:10:45 12:49:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s diff --git a/src/test/resources/speedtest/travelSearch-expected-results-srr.csv b/src/test/resources/speedtest/travelSearch-expected-results-srr.csv index 3b516abca45..abe9e692b1f 100644 --- a/src/test/resources/speedtest/travelSearch-expected-results-srr.csv +++ b/src/test/resources/speedtest/travelSearch-expected-results-srr.csv @@ -1,6 +1 @@ tcId,nTransfers,duration,cost,walkDistance,startTime,endTime,agencies,modes,routes,stops,details -1,0,56m49s,0,1458,12:03:46,13:00:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:11:20 12:49:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s -1,0,56m49s,0,1458,12:18:46,13:15:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:26:20 13:04:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s -1,0,56m49s,0,1458,12:33:46,13:30:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:41:20 13:19:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s -2,0,1h4m28s,0,1902,12:48:46,13:53:14,TriMet,BUS,8,prt:6789|prt:5028,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:56:20 13:36 ~ SW Sam Jackson Pk & OHSU(5028) ~ Walk 17m14s -2,1,1h2m28s,0,1488,12:50:46,13:53:14,TriMet,BUS,73|8,prt:7106|prt:2592|prt:5028,Walk 2m20s ~ NE 21st & Clackamas(7106) ~ BUS 73 12:53:06 13:01 ~ Rose Quarter Transit Center(2592) ~ BUS 8 13:04 13:36 ~ SW Sam Jackson Pk & OHSU(5028) ~ Walk 17m14s From 1aa614cc170fb3132bb7698b787a9de320571264 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 28 Mar 2024 12:42:16 -0400 Subject: [PATCH 045/111] test(StreetEdgeWheelchairCostTest): Assume high cost for any untabulated path. --- .../street/model/edge/StreetEdge.java | 5 +++-- .../model/edge/StreetEdgeWheelchairCostTest.java | 13 +++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index 91c80c5350e..3daf3599e05 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -68,6 +68,7 @@ public class StreetEdge static final int BICYCLE_NOTHRUTRAFFIC = 7; static final int WALK_NOTHRUTRAFFIC = 8; static final int CLASS_LINK = 9; + public static final float DEFAULT_LARGE_COST = 10000f; private StreetEdgeCostExtension costExtension; @@ -1333,13 +1334,13 @@ private TraversalCosts walkingTraversalCosts( ); if (profileCost != null) { - var travelTimeHours = profileCost.getOrDefault(mobilityProfile, 10000f); + var travelTimeHours = profileCost.getOrDefault(mobilityProfile, DEFAULT_LARGE_COST); time = defaultTravelHours * 3600; weight = travelTimeHours; } else { // Assign a high travel time and weight to non-tabulated ways. time = 360000; - weight = 100000; + weight = DEFAULT_LARGE_COST * 10; } return new TraversalCosts(time, weight); } diff --git a/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeWheelchairCostTest.java b/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeWheelchairCostTest.java index 7c2767a6935..9245c9b663a 100644 --- a/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeWheelchairCostTest.java +++ b/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeWheelchairCostTest.java @@ -17,6 +17,7 @@ import org.opentripplanner.street.search.state.State; class StreetEdgeWheelchairCostTest { + private static final long DEFAULT_LARGE_COST = 10000; StreetVertex V1; StreetVertex V2; @@ -103,7 +104,8 @@ public void shouldScaleCostWithMaxSlope(double slope, double reluctance, long ex ); State result = traverse(edge, req.build()); assertNotNull(result); - assertEquals(expectedCost, (long) result.weight); + // G-MAP specific: The cost for un-tabulated paths is the large default cost. + assertEquals(DEFAULT_LARGE_COST, (long) result.weight); } static Stream wheelchairStairsCases() { @@ -146,7 +148,8 @@ public void wheelchairStairsReluctance(double stairsReluctance, long expectedCos req.withPreferences(pref -> pref.withWalk(w -> w.withReluctance(1.0))); var result = traverse(stairEdge, req.build()); - assertEquals(expectedCost, (long) result.weight); + // G-MAP-specific: inaccessible paths have the default high cost. + assertEquals(DEFAULT_LARGE_COST, (long) result.weight); StreetEdge noStairsEdge = stairEdge.toBuilder().withStairs(false).buildAndConnect(); var notStairsResult = traverse(noStairsEdge, req.build()); @@ -191,7 +194,8 @@ public void inaccessibleStreet(float inaccessibleStreetReluctance, long expected ); var result = traverse(edge, req.build()); - assertEquals(expectedCost, (long) result.weight); + // G-MAP-specific: Wheelchair costs on un-tabulated paths are set to high value. + assertEquals(DEFAULT_LARGE_COST, (long) result.weight); // reluctance should have no effect when the edge is accessible StreetEdge accessibleEdge = edge.toBuilder().withWheelchairAccessible(true).buildAndConnect(); @@ -228,7 +232,8 @@ public void walkReluctance(double walkReluctance, long expectedCost) { req.withWheelchair(true); var result = traverse(edge, req.build()); - assertEquals(expectedCost, (long) result.weight); + // G-MAP-specific: Walking on un-tabulated paths has a high cost. + assertEquals(DEFAULT_LARGE_COST, (long) result.weight); assertEquals(8, result.getElapsedTimeSeconds()); } From a03c4fe967af6b52b52a6d3c50b395e8026cadb4 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 28 Mar 2024 13:30:54 -0400 Subject: [PATCH 046/111] test(StreetEdgeCost): Use default high cost in more places. --- .../street/model/edge/StreetEdge.java | 6 +++--- .../street/model/edge/StreetEdgeCostTest.java | 13 +++++++++---- .../model/edge/StreetEdgeWheelchairCostTest.java | 8 +++++--- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index 3daf3599e05..8e83e42fb6d 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -68,7 +68,7 @@ public class StreetEdge static final int BICYCLE_NOTHRUTRAFFIC = 7; static final int WALK_NOTHRUTRAFFIC = 8; static final int CLASS_LINK = 9; - public static final float DEFAULT_LARGE_COST = 10000f; + public static final long DEFAULT_LARGE_COST = 10000; private StreetEdgeCostExtension costExtension; @@ -1334,13 +1334,13 @@ private TraversalCosts walkingTraversalCosts( ); if (profileCost != null) { - var travelTimeHours = profileCost.getOrDefault(mobilityProfile, DEFAULT_LARGE_COST); + var travelTimeHours = profileCost.getOrDefault(mobilityProfile, (float) DEFAULT_LARGE_COST); time = defaultTravelHours * 3600; weight = travelTimeHours; } else { // Assign a high travel time and weight to non-tabulated ways. time = 360000; - weight = DEFAULT_LARGE_COST * 10; + weight = DEFAULT_LARGE_COST * 10.0; } return new TraversalCosts(time, weight); } diff --git a/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeCostTest.java b/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeCostTest.java index acd02653941..3983e853197 100644 --- a/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeCostTest.java +++ b/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeCostTest.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.opentripplanner.street.model._data.StreetModelForTest.V1; import static org.opentripplanner.street.model._data.StreetModelForTest.V2; +import static org.opentripplanner.street.model.edge.StreetEdge.DEFAULT_LARGE_COST; import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; @@ -42,7 +43,8 @@ public void walkReluctance(double walkReluctance, long expectedCost) { req.withPreferences(p -> p.withWalk(w -> w.withReluctance(walkReluctance))); State result = traverse(edge, req.withMode(StreetMode.WALK).build()); assertNotNull(result); - assertEquals(expectedCost, (long) result.weight); + // G-MAP-specific: Walking along untabulated paths has a high cost. + assertEquals(DEFAULT_LARGE_COST, (long) result.weight); assertEquals(76, result.getElapsedTimeSeconds()); } @@ -133,7 +135,8 @@ public void stairsReluctance(double stairsReluctance, long expectedCost) { req.withPreferences(p -> p.withWalk(w -> w.withStairsReluctance(stairsReluctance))); req.withMode(StreetMode.WALK); var result = traverse(stairsEdge, req.build()); - assertEquals(expectedCost, (long) result.weight); + // G-MAP-specific: Walking along untabulated paths has a high cost. + assertEquals(DEFAULT_LARGE_COST, (long) result.weight); assertEquals(23, result.getElapsedTimeSeconds()); @@ -166,7 +169,8 @@ public void bikeStairsReluctance(double stairsReluctance, long expectedCost) { ); req.withMode(StreetMode.BIKE); var result = traverse(stairsEdge, req.build()); - assertEquals(expectedCost, (long) result.weight); + // G-MAP-specific: Walking along untabulated paths has a high cost. + assertEquals(DEFAULT_LARGE_COST, (long) result.weight); assertEquals(23, result.getElapsedTimeSeconds()); @@ -197,7 +201,8 @@ public void walkSafetyFactor(double walkSafetyFactor, long expectedCost) { req.withPreferences(p -> p.withWalk(w -> w.withSafetyFactor(walkSafetyFactor))); req.withMode(StreetMode.WALK); var result = traverse(safeEdge, req.build()); - assertEquals(expectedCost, (long) result.weight); + // G-MAP-specific: Walking along untabulated paths has a high cost. + assertEquals(DEFAULT_LARGE_COST, (long) result.weight); assertEquals(8, result.getElapsedTimeSeconds()); diff --git a/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeWheelchairCostTest.java b/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeWheelchairCostTest.java index 9245c9b663a..943f3c68a93 100644 --- a/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeWheelchairCostTest.java +++ b/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeWheelchairCostTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.opentripplanner.street.model._data.StreetModelForTest.intersectionVertex; +import static org.opentripplanner.street.model.edge.StreetEdge.DEFAULT_LARGE_COST; import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; @@ -17,7 +18,6 @@ import org.opentripplanner.street.search.state.State; class StreetEdgeWheelchairCostTest { - private static final long DEFAULT_LARGE_COST = 10000; StreetVertex V1; StreetVertex V2; @@ -153,7 +153,8 @@ public void wheelchairStairsReluctance(double stairsReluctance, long expectedCos StreetEdge noStairsEdge = stairEdge.toBuilder().withStairs(false).buildAndConnect(); var notStairsResult = traverse(noStairsEdge, req.build()); - assertEquals(7, (long) notStairsResult.weight); + // G-MAP-specific: inaccessible paths have the default high cost. + assertEquals(DEFAULT_LARGE_COST, (long) notStairsResult.weight); } static Stream inaccessibleStreetCases() { @@ -200,7 +201,8 @@ public void inaccessibleStreet(float inaccessibleStreetReluctance, long expected // reluctance should have no effect when the edge is accessible StreetEdge accessibleEdge = edge.toBuilder().withWheelchairAccessible(true).buildAndConnect(); var accessibleResult = traverse(accessibleEdge, req.build()); - assertEquals(15, (long) accessibleResult.weight); + // G-MAP-specific: Wheelchair costs on un-tabulated paths are set to high value. + assertEquals(DEFAULT_LARGE_COST, (long) accessibleResult.weight); } static Stream walkReluctanceCases() { From 8d1bb280a206f035785c51c58783198136cef699 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 28 Mar 2024 15:10:39 -0400 Subject: [PATCH 047/111] test(travelSearch): Add back test results --- .../resources/speedtest/travelSearch-expected-results-bd.csv | 2 ++ .../speedtest/travelSearch-expected-results-bdr.csv | 2 ++ .../resources/speedtest/travelSearch-expected-results-bt.csv | 2 ++ .../speedtest/travelSearch-expected-results-btr.csv | 4 ++++ .../resources/speedtest/travelSearch-expected-results-mc.csv | 4 ++++ .../resources/speedtest/travelSearch-expected-results-md.csv | 4 ++++ .../resources/speedtest/travelSearch-expected-results-sr.csv | 4 ++++ .../speedtest/travelSearch-expected-results-srr.csv | 5 +++++ 8 files changed, 27 insertions(+) diff --git a/src/test/resources/speedtest/travelSearch-expected-results-bd.csv b/src/test/resources/speedtest/travelSearch-expected-results-bd.csv index abe9e692b1f..117eecc4a12 100644 --- a/src/test/resources/speedtest/travelSearch-expected-results-bd.csv +++ b/src/test/resources/speedtest/travelSearch-expected-results-bd.csv @@ -1 +1,3 @@ tcId,nTransfers,duration,cost,walkDistance,startTime,endTime,agencies,modes,routes,stops,details +1,0,43m25s,-1,0,12:00:00,12:43:25,,,,,Unknown transit 0tx 43m25s +2,0,43m25s,-1,0,12:00:00,12:43:25,,,,,Unknown transit 0tx 43m25s diff --git a/src/test/resources/speedtest/travelSearch-expected-results-bdr.csv b/src/test/resources/speedtest/travelSearch-expected-results-bdr.csv index abe9e692b1f..4308492da33 100644 --- a/src/test/resources/speedtest/travelSearch-expected-results-bdr.csv +++ b/src/test/resources/speedtest/travelSearch-expected-results-bdr.csv @@ -1 +1,3 @@ tcId,nTransfers,duration,cost,walkDistance,startTime,endTime,agencies,modes,routes,stops,details +1,0,43m3s,-1,0,13:16:57,14:00:00,,,,,Unknown transit 0tx 43m3s +2,0,43m3s,-1,0,13:16:57,14:00:00,,,,,Unknown transit 0tx 43m3s diff --git a/src/test/resources/speedtest/travelSearch-expected-results-bt.csv b/src/test/resources/speedtest/travelSearch-expected-results-bt.csv index abe9e692b1f..3d457aa7c22 100644 --- a/src/test/resources/speedtest/travelSearch-expected-results-bt.csv +++ b/src/test/resources/speedtest/travelSearch-expected-results-bt.csv @@ -1 +1,3 @@ tcId,nTransfers,duration,cost,walkDistance,startTime,endTime,agencies,modes,routes,stops,details +1,0,1h35s,-1,0,12:00:00,13:00:35,,,,,Unknown transit 0tx 1h35s +2,0,1h35s,-1,0,12:00:00,13:00:35,,,,,Unknown transit 0tx 1h35s diff --git a/src/test/resources/speedtest/travelSearch-expected-results-btr.csv b/src/test/resources/speedtest/travelSearch-expected-results-btr.csv index abe9e692b1f..634b7b15b1e 100644 --- a/src/test/resources/speedtest/travelSearch-expected-results-btr.csv +++ b/src/test/resources/speedtest/travelSearch-expected-results-btr.csv @@ -1 +1,5 @@ tcId,nTransfers,duration,cost,walkDistance,startTime,endTime,agencies,modes,routes,stops,details +1,0,1h11m14s,-1,0,12:48:46,14:00:00,,,,,Unknown transit 0tx 1h11m14s +1,1,1h9m14s,-1,0,12:50:46,14:00:00,,,,,Unknown transit 1tx 1h9m14s +2,0,1h11m14s,-1,0,12:48:46,14:00:00,,,,,Unknown transit 0tx 1h11m14s +2,1,1h9m14s,-1,0,12:50:46,14:00:00,,,,,Unknown transit 1tx 1h9m14s diff --git a/src/test/resources/speedtest/travelSearch-expected-results-mc.csv b/src/test/resources/speedtest/travelSearch-expected-results-mc.csv index abe9e692b1f..252d31eeeb3 100644 --- a/src/test/resources/speedtest/travelSearch-expected-results-mc.csv +++ b/src/test/resources/speedtest/travelSearch-expected-results-mc.csv @@ -1 +1,5 @@ tcId,nTransfers,duration,cost,walkDistance,startTime,endTime,agencies,modes,routes,stops,details +1,0,56m49s,5095,1458,12:03:46,13:00:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:11:20 12:49:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s +1,0,56m49s,5095,1458,12:18:46,13:15:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:26:20 13:04:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s +1,0,56m49s,5095,1458,12:33:46,13:30:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:41:20 13:19:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s +2,0,56m49s,5095,1458,12:03:46,13:00:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:11:20 12:49:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s diff --git a/src/test/resources/speedtest/travelSearch-expected-results-md.csv b/src/test/resources/speedtest/travelSearch-expected-results-md.csv index abe9e692b1f..252d31eeeb3 100644 --- a/src/test/resources/speedtest/travelSearch-expected-results-md.csv +++ b/src/test/resources/speedtest/travelSearch-expected-results-md.csv @@ -1 +1,5 @@ tcId,nTransfers,duration,cost,walkDistance,startTime,endTime,agencies,modes,routes,stops,details +1,0,56m49s,5095,1458,12:03:46,13:00:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:11:20 12:49:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s +1,0,56m49s,5095,1458,12:18:46,13:15:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:26:20 13:04:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s +1,0,56m49s,5095,1458,12:33:46,13:30:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:41:20 13:19:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s +2,0,56m49s,5095,1458,12:03:46,13:00:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:11:20 12:49:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s diff --git a/src/test/resources/speedtest/travelSearch-expected-results-sr.csv b/src/test/resources/speedtest/travelSearch-expected-results-sr.csv index abe9e692b1f..faeebbb95ed 100644 --- a/src/test/resources/speedtest/travelSearch-expected-results-sr.csv +++ b/src/test/resources/speedtest/travelSearch-expected-results-sr.csv @@ -1 +1,5 @@ tcId,nTransfers,duration,cost,walkDistance,startTime,endTime,agencies,modes,routes,stops,details +1,0,56m49s,0,1458,12:03:46,13:00:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:11:20 12:49:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s +1,0,56m49s,0,1458,12:18:46,13:15:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:26:20 13:04:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s +1,0,56m49s,0,1458,12:33:46,13:30:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:41:20 13:19:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s +2,0,58m8s,0,1507,12:02:27,13:00:35,TriMet,BUS,8,prt:6779|prt:6548,Walk 8m18s ~ NE 15th & Weidler(6779) ~ BUS 8 12:10:45 12:49:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s diff --git a/src/test/resources/speedtest/travelSearch-expected-results-srr.csv b/src/test/resources/speedtest/travelSearch-expected-results-srr.csv index abe9e692b1f..3b516abca45 100644 --- a/src/test/resources/speedtest/travelSearch-expected-results-srr.csv +++ b/src/test/resources/speedtest/travelSearch-expected-results-srr.csv @@ -1 +1,6 @@ tcId,nTransfers,duration,cost,walkDistance,startTime,endTime,agencies,modes,routes,stops,details +1,0,56m49s,0,1458,12:03:46,13:00:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:11:20 12:49:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s +1,0,56m49s,0,1458,12:18:46,13:15:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:26:20 13:04:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s +1,0,56m49s,0,1458,12:33:46,13:30:35,TriMet,BUS,8,prt:6789|prt:6548,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:41:20 13:19:19 ~ SW 11th & Gibbs(6548) ~ Walk 11m16s +2,0,1h4m28s,0,1902,12:48:46,13:53:14,TriMet,BUS,8,prt:6789|prt:5028,Walk 7m34s ~ NE 15th & Halsey(6789) ~ BUS 8 12:56:20 13:36 ~ SW Sam Jackson Pk & OHSU(5028) ~ Walk 17m14s +2,1,1h2m28s,0,1488,12:50:46,13:53:14,TriMet,BUS,73|8,prt:7106|prt:2592|prt:5028,Walk 2m20s ~ NE 21st & Clackamas(7106) ~ BUS 73 12:53:06 13:01 ~ Rose Quarter Transit Center(2592) ~ BUS 8 13:04 13:36 ~ SW Sam Jackson Pk & OHSU(5028) ~ Walk 17m14s From 99ae5a2eafc992906ff2f43de558969dabf51d49 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 28 Mar 2024 15:28:12 -0400 Subject: [PATCH 048/111] test(StreetEdge*Cost): Revert tests --- .../street/model/edge/StreetEdgeCostTest.java | 13 ++++--------- .../edge/StreetEdgeWheelchairCostTest.java | 19 ++++++------------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeCostTest.java b/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeCostTest.java index 3983e853197..acd02653941 100644 --- a/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeCostTest.java +++ b/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeCostTest.java @@ -4,7 +4,6 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.opentripplanner.street.model._data.StreetModelForTest.V1; import static org.opentripplanner.street.model._data.StreetModelForTest.V2; -import static org.opentripplanner.street.model.edge.StreetEdge.DEFAULT_LARGE_COST; import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; @@ -43,8 +42,7 @@ public void walkReluctance(double walkReluctance, long expectedCost) { req.withPreferences(p -> p.withWalk(w -> w.withReluctance(walkReluctance))); State result = traverse(edge, req.withMode(StreetMode.WALK).build()); assertNotNull(result); - // G-MAP-specific: Walking along untabulated paths has a high cost. - assertEquals(DEFAULT_LARGE_COST, (long) result.weight); + assertEquals(expectedCost, (long) result.weight); assertEquals(76, result.getElapsedTimeSeconds()); } @@ -135,8 +133,7 @@ public void stairsReluctance(double stairsReluctance, long expectedCost) { req.withPreferences(p -> p.withWalk(w -> w.withStairsReluctance(stairsReluctance))); req.withMode(StreetMode.WALK); var result = traverse(stairsEdge, req.build()); - // G-MAP-specific: Walking along untabulated paths has a high cost. - assertEquals(DEFAULT_LARGE_COST, (long) result.weight); + assertEquals(expectedCost, (long) result.weight); assertEquals(23, result.getElapsedTimeSeconds()); @@ -169,8 +166,7 @@ public void bikeStairsReluctance(double stairsReluctance, long expectedCost) { ); req.withMode(StreetMode.BIKE); var result = traverse(stairsEdge, req.build()); - // G-MAP-specific: Walking along untabulated paths has a high cost. - assertEquals(DEFAULT_LARGE_COST, (long) result.weight); + assertEquals(expectedCost, (long) result.weight); assertEquals(23, result.getElapsedTimeSeconds()); @@ -201,8 +197,7 @@ public void walkSafetyFactor(double walkSafetyFactor, long expectedCost) { req.withPreferences(p -> p.withWalk(w -> w.withSafetyFactor(walkSafetyFactor))); req.withMode(StreetMode.WALK); var result = traverse(safeEdge, req.build()); - // G-MAP-specific: Walking along untabulated paths has a high cost. - assertEquals(DEFAULT_LARGE_COST, (long) result.weight); + assertEquals(expectedCost, (long) result.weight); assertEquals(8, result.getElapsedTimeSeconds()); diff --git a/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeWheelchairCostTest.java b/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeWheelchairCostTest.java index 943f3c68a93..7c2767a6935 100644 --- a/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeWheelchairCostTest.java +++ b/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeWheelchairCostTest.java @@ -3,7 +3,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.opentripplanner.street.model._data.StreetModelForTest.intersectionVertex; -import static org.opentripplanner.street.model.edge.StreetEdge.DEFAULT_LARGE_COST; import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; @@ -104,8 +103,7 @@ public void shouldScaleCostWithMaxSlope(double slope, double reluctance, long ex ); State result = traverse(edge, req.build()); assertNotNull(result); - // G-MAP specific: The cost for un-tabulated paths is the large default cost. - assertEquals(DEFAULT_LARGE_COST, (long) result.weight); + assertEquals(expectedCost, (long) result.weight); } static Stream wheelchairStairsCases() { @@ -148,13 +146,11 @@ public void wheelchairStairsReluctance(double stairsReluctance, long expectedCos req.withPreferences(pref -> pref.withWalk(w -> w.withReluctance(1.0))); var result = traverse(stairEdge, req.build()); - // G-MAP-specific: inaccessible paths have the default high cost. - assertEquals(DEFAULT_LARGE_COST, (long) result.weight); + assertEquals(expectedCost, (long) result.weight); StreetEdge noStairsEdge = stairEdge.toBuilder().withStairs(false).buildAndConnect(); var notStairsResult = traverse(noStairsEdge, req.build()); - // G-MAP-specific: inaccessible paths have the default high cost. - assertEquals(DEFAULT_LARGE_COST, (long) notStairsResult.weight); + assertEquals(7, (long) notStairsResult.weight); } static Stream inaccessibleStreetCases() { @@ -195,14 +191,12 @@ public void inaccessibleStreet(float inaccessibleStreetReluctance, long expected ); var result = traverse(edge, req.build()); - // G-MAP-specific: Wheelchair costs on un-tabulated paths are set to high value. - assertEquals(DEFAULT_LARGE_COST, (long) result.weight); + assertEquals(expectedCost, (long) result.weight); // reluctance should have no effect when the edge is accessible StreetEdge accessibleEdge = edge.toBuilder().withWheelchairAccessible(true).buildAndConnect(); var accessibleResult = traverse(accessibleEdge, req.build()); - // G-MAP-specific: Wheelchair costs on un-tabulated paths are set to high value. - assertEquals(DEFAULT_LARGE_COST, (long) accessibleResult.weight); + assertEquals(15, (long) accessibleResult.weight); } static Stream walkReluctanceCases() { @@ -234,8 +228,7 @@ public void walkReluctance(double walkReluctance, long expectedCost) { req.withWheelchair(true); var result = traverse(edge, req.build()); - // G-MAP-specific: Walking on un-tabulated paths has a high cost. - assertEquals(DEFAULT_LARGE_COST, (long) result.weight); + assertEquals(expectedCost, (long) result.weight); assertEquals(8, result.getElapsedTimeSeconds()); } From 0fabe2390c0559ebac9ece4bd6af69b6bf67944d Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 28 Mar 2024 15:36:32 -0400 Subject: [PATCH 049/111] refactor(StreetEdge): Revert to upstream time calcs if mobility profile absent --- .../street/model/edge/StreetEdge.java | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index 8e83e42fb6d..40ef7675da3 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -1288,6 +1288,7 @@ private TraversalCosts walkingTraversalCosts( ) { double time, weight; if (wheelchair) { + time = getEffectiveWalkDistance() / speed; weight = (getEffectiveBikeDistance() / speed) * StreetEdgeReluctanceCalculator.computeWheelchairReluctance( @@ -1299,13 +1300,14 @@ private TraversalCosts walkingTraversalCosts( } else { if (walkingBike) { // take slopes into account when walking bikes - weight = (getEffectiveBikeDistance() / speed); + time = weight = (getEffectiveBikeDistance() / speed); if (isStairs()) { // we do allow walking the bike across a stairs but there is a very high default penalty weight *= preferences.bike().walking().stairsReluctance(); } } else { // take slopes into account when walking + time = getEffectiveWalkDistance() / speed; weight = getEffectiveWalkSafetyDistance() * preferences.walk().safetyFactor() + @@ -1328,19 +1330,21 @@ private TraversalCosts walkingTraversalCosts( // and are used to overwrite the time calculated above (convert from hours to seconds). // If no tabulated times are available for the edge, compute them using the // travel speeds for the given mobility profile. - var defaultTravelHours = MobilityProfileRouting.computeTravelHours( - getEffectiveWalkDistance(), - mobilityProfile - ); + if (mobilityProfile != null) { + var defaultTravelHours = MobilityProfileRouting.computeTravelHours( + getEffectiveWalkDistance(), + mobilityProfile + ); - if (profileCost != null) { - var travelTimeHours = profileCost.getOrDefault(mobilityProfile, (float) DEFAULT_LARGE_COST); - time = defaultTravelHours * 3600; - weight = travelTimeHours; - } else { - // Assign a high travel time and weight to non-tabulated ways. - time = 360000; - weight = DEFAULT_LARGE_COST * 10.0; + if (profileCost != null) { + var travelTimeHours = profileCost.getOrDefault(mobilityProfile, (float) DEFAULT_LARGE_COST); + time = defaultTravelHours * 3600; + weight = travelTimeHours; + } else { + // Assign a high travel time and weight to non-tabulated ways. + time = 360000; + weight = DEFAULT_LARGE_COST * 10.0; + } } return new TraversalCosts(time, weight); } From a6868ea3dd3b581b83b34e3563e6652282bf9e6c Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 28 Mar 2024 15:59:06 -0400 Subject: [PATCH 050/111] refactor(MobilityProfile): Default to null profile instead of NONE. --- .../opentripplanner/ext/mobilityprofile/MobilityProfile.java | 5 +++++ .../opentripplanner/routing/api/request/RouteRequest.java | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfile.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfile.java index 7e7788f57ab..7c6c1e3e10c 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfile.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfile.java @@ -1,5 +1,7 @@ package org.opentripplanner.ext.mobilityprofile; +import static org.apache.commons.lang3.StringUtils.isBlank; + /** * Enumeration for the mobility profiles, and their associated column names for CSV parsing. */ @@ -39,11 +41,14 @@ public String toString() { } public static MobilityProfile fromString(String value) { + if (isBlank(value)) return null; + for (MobilityProfile p : MobilityProfile.values()) { if (p.text.equals(value)) { return p; } } + throw new RuntimeException(String.format("Invalid mobility profile '%s'", value)); } } diff --git a/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java b/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java index fde5c1f1e5f..14f92dc6590 100644 --- a/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java +++ b/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java @@ -82,7 +82,7 @@ public class RouteRequest implements Cloneable, Serializable { private boolean wheelchair = false; - private MobilityProfile mobilityProfile = MobilityProfile.NONE; + private MobilityProfile mobilityProfile = null; /* CONSTRUCTORS */ From d420d65800fdbd3c39daaa64d79f3d9505815494 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 28 Mar 2024 16:49:25 -0400 Subject: [PATCH 051/111] refactor(TemporaryPartialStreetEdge): Set mobility debug name only if profile cost available. --- .../edge/TemporaryPartialStreetEdge.java | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java index a9af8a25822..f84edf43115 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java @@ -32,17 +32,19 @@ public final class TemporaryPartialStreetEdge extends StreetEdge implements Temp this.parentEdge = builder.parentEdge(); this.geometry = super.getGeometry(); this.profileCost = MobilityProfileRouting.getProRatedProfileCosts(this); - float ratio = (float) (getDistanceMeters() / getParentEdge().getDistanceMeters()); - this.setName( - new LocalizedString( - String.format( - "%s tmp r%4.3f l%4.3f", - builder.parentEdge().getName(), - ratio, - getDistanceMeters() + if (!this.profileCost.isEmpty()) { + float ratio = (float) (getDistanceMeters() / getParentEdge().getDistanceMeters()); + this.setName( + new LocalizedString( + String.format( + "%s tmp r%4.3f l%4.3f", + builder.parentEdge().getName(), + ratio, + getDistanceMeters() + ) ) - ) - ); + ); + } } /** From c142afbc148a503aac5852934c71ff569d808425 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 28 Mar 2024 16:53:48 -0400 Subject: [PATCH 052/111] refactor(OsmModule): Assign mobility debug names if profile costs available. --- .../graph_builder/module/osm/OsmModule.java | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index f12f9ea4b73..f4bb989ab36 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -562,10 +562,10 @@ private StreetEdge getEdgeForStreet( label = label.intern(); I18NString name = params.edgeNamer().getNameForWay(way, label); float carSpeed = way.getOsmProvider().getOsmTagMapper().getCarSpeedForWay(way, back); - StreetTraversalPermission perms = MobilityProfileRouting.adjustPedestrianPermissions( - way, - permissions - ); + + StreetTraversalPermission perms = mobilityProfileData != null + ? MobilityProfileRouting.adjustPedestrianPermissions(way, permissions) + : permissions; StreetEdgeBuilder seb = new StreetEdgeBuilder<>() .withFromVertex(startEndpoint) @@ -573,7 +573,7 @@ private StreetEdge getEdgeForStreet( .withGeometry(geometry) .withName(name) .withMeterLength(length) - .withPermission(MobilityProfileRouting.adjustPedestrianPermissions(way, permissions)) + .withPermission(perms) .withBack(back) .withCarSpeed(carSpeed) .withLink(way.isLink()) @@ -582,24 +582,24 @@ private StreetEdge getEdgeForStreet( .withStairs(way.isSteps()) .withWheelchairAccessible(way.isWheelchairAccessible()); - String startId = startEndpoint.getLabel().toString(); - String endId = endEndpoint.getLabel().toString(); - - // For testing, indicate the OSM node ids (remove prefixes). - String startShortId = startId.replace("osm:node:", ""); - String endShortId = endId.replace("osm:node:", ""); - String nameWithNodeIds = String.format( - "%s (%s, %s→%s)", - name, - way.getId(), - startShortId, - endShortId - ); - seb.withName(nameWithNodeIds); - // Lookup costs by mobility profile, if any were defined. // Note that edges are bidirectional, so we check that mobility data exist in both directions. if (mobilityProfileData != null) { + String startId = startEndpoint.getLabel().toString(); + String endId = endEndpoint.getLabel().toString(); + + // For testing, indicate the OSM node ids (remove prefixes). + String startShortId = startId.replace("osm:node:", ""); + String endShortId = endId.replace("osm:node:", ""); + String nameWithNodeIds = String.format( + "%s (%s, %s→%s)", + name, + way.getId(), + startShortId, + endShortId + ); + seb.withName(nameWithNodeIds); + String wayId = Long.toString(way.getId(), 10); var edgeMobilityCostMap = mobilityProfileData.get(wayId); if (edgeMobilityCostMap != null) { From 6c5e3eeee8ebd98ac8605238a4c1e14dc6e6a10c Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 10 Apr 2024 18:33:04 -0400 Subject: [PATCH 053/111] improvement(OsmModule): Implement two-way mobility costs. --- .../MobilityProfileParser.java | 8 +- .../graph_builder/module/osm/OsmModule.java | 128 ++++++++++-------- 2 files changed, 81 insertions(+), 55 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java index 561afed18db..4b2f88f4315 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java @@ -49,6 +49,11 @@ public static String getKey(String id, String from, String to) { return String.format("%s:%s=>%s", id, from, to); } + /** Helper to build a key of the form "id:from=>to" for an OSM way. */ + public static String getKey(String id, long from, long to) { + return String.format("%s:%d=>%d", id, from, to); + } + private static void parseRow( int lineNumber, CsvReader reader, @@ -59,6 +64,7 @@ private static void parseRow( long fromNode = Long.parseLong(reader.get("Upstream Node"), 10); long toNode = Long.parseLong(reader.get("Downstream Node"), 10); String id = reader.get("Way Id"); + String key = getKey(id, fromNode, toNode); float lengthMeters = ONE_MILE_IN_METERS * Float.parseFloat(reader.get("Link Length")); // The weight map has to be a HashMap instead of an EnumMap so that it is correctly @@ -77,7 +83,7 @@ private static void parseRow( } } - map.put(id, new MobilityProfileData(lengthMeters, fromNode, toNode, weightMap)); + map.put(key, new MobilityProfileData(lengthMeters, fromNode, toNode, weightMap)); } catch (NumberFormatException | NullPointerException e) { LOG.warn( "Skipping mobility profile data at line {}: missing/invalid data in column {}.", diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index f4bb989ab36..88a39d7c36f 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -2,6 +2,7 @@ import com.google.common.collect.Iterables; import gnu.trove.iterator.TLongIterator; +import gnu.trove.list.TLongList; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -15,6 +16,7 @@ import org.locationtech.jts.geom.LineString; import org.opentripplanner.ext.mobilityprofile.MobilityProfile; import org.opentripplanner.ext.mobilityprofile.MobilityProfileData; +import org.opentripplanner.ext.mobilityprofile.MobilityProfileParser; import org.opentripplanner.ext.mobilityprofile.MobilityProfileRouting; import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.framework.geometry.SphericalDistanceLibrary; @@ -588,63 +590,81 @@ private StreetEdge getEdgeForStreet( String startId = startEndpoint.getLabel().toString(); String endId = endEndpoint.getLabel().toString(); - // For testing, indicate the OSM node ids (remove prefixes). - String startShortId = startId.replace("osm:node:", ""); - String endShortId = endId.replace("osm:node:", ""); - String nameWithNodeIds = String.format( - "%s (%s, %s→%s)", - name, - way.getId(), - startShortId, - endShortId - ); - seb.withName(nameWithNodeIds); + try { + long startShortId = Long.parseLong(startId.replace("osm:node:", ""), 10); + long endShortId = Long.parseLong(endId.replace("osm:node:", ""), 10); + + // For testing, indicate the OSM node ids (remove prefixes). + String nameWithNodeIds = String.format( + "%s (%s, %s→%s)", + name, + way.getId(), + startShortId, + endShortId + ); + seb.withName(nameWithNodeIds); - String wayId = Long.toString(way.getId(), 10); - var edgeMobilityCostMap = mobilityProfileData.get(wayId); - if (edgeMobilityCostMap != null) { - // Check whether the nodes for this way match the nodes from mobility profile data. - if ( - startShortId.equals(Long.toString(edgeMobilityCostMap.fromNode(), 10)) && - endShortId.equals(Long.toString(edgeMobilityCostMap.toNode(), 10)) || - startShortId.equals(Long.toString(edgeMobilityCostMap.toNode(), 10)) && - endShortId.equals(Long.toString(edgeMobilityCostMap.fromNode(), 10)) - ) { - // If the from/to nodes match, then assign the cost directly - seb.withProfileCosts(edgeMobilityCostMap.costs()); - - // Append an indication that this edge uses a full profile cost. - nameWithNodeIds = String.format("%s ☑", nameWithNodeIds); - // System.out.printf("Way (full length): %s size %d%n", nameWithNodeIds, edgeMobilityCostMap.costs().size()); - System.out.printf( - "%s %f%n", - nameWithNodeIds, - edgeMobilityCostMap.costs().get(MobilityProfile.WCHAIRE) - ); - } else { - // Otherwise, pro-rate the cost to the length of the edge. - float ratio = (float) (length / edgeMobilityCostMap.lengthInMeters()); + String wayId = Long.toString(way.getId(), 10); + TLongList nodeRefs = way.getNodeRefs(); + int startIndex = nodeRefs.indexOf(startShortId); + int endIndex = nodeRefs.indexOf(endShortId); + boolean isReverse = endIndex < startIndex; + + // Use the start and end nodes of the OSM way per the OSM data to lookup the mobility costs. + long wayFromId = nodeRefs.get(0); + long wayToId = nodeRefs.get(nodeRefs.size() - 1); + String key = isReverse + ? MobilityProfileParser.getKey(wayId, wayToId, wayFromId) + : MobilityProfileParser.getKey(wayId, wayFromId, wayToId); + + var edgeMobilityCostMap = mobilityProfileData.get(key); + if (edgeMobilityCostMap != null) { + // Check whether the nodes for this way match the nodes from mobility profile data. + if ( + startShortId == edgeMobilityCostMap.fromNode() && + endShortId == edgeMobilityCostMap.toNode() || + startShortId == edgeMobilityCostMap.toNode() && + endShortId == edgeMobilityCostMap.fromNode() + ) { + // If the from/to nodes match, then assign the cost directly + seb.withProfileCosts(edgeMobilityCostMap.costs()); + + // Append an indication that this edge uses a full profile cost. + nameWithNodeIds = String.format("%s ☑", nameWithNodeIds); + // System.out.printf("Way (full length): %s size %d%n", nameWithNodeIds, edgeMobilityCostMap.costs().size()); + System.out.printf( + "%s %f%n", + nameWithNodeIds, + edgeMobilityCostMap.costs().get(MobilityProfile.WCHAIRE) + ); + } else { + // Otherwise, pro-rate the cost to the length of the edge. + float ratio = (float) (length / edgeMobilityCostMap.lengthInMeters()); + + Map proRatedProfileCosts = MobilityProfileRouting.getProRatedProfileCosts( + edgeMobilityCostMap.costs(), + ratio + ); + seb.withProfileCosts(proRatedProfileCosts); + + // Append an indication that this edge uses a partial profile cost. + nameWithNodeIds = String.format("%s r%4.3f l%4.3f", nameWithNodeIds, ratio, length); + // System.out.printf("Way (partial): %s size %d%n", nameWithNodeIds, proRatedProfileCosts.size()); + System.out.printf( + "%s %f%n", + nameWithNodeIds, + proRatedProfileCosts.get(MobilityProfile.WCHAIRE) + ); + } - Map proRatedProfileCosts = MobilityProfileRouting.getProRatedProfileCosts( - edgeMobilityCostMap.costs(), - ratio - ); - seb.withProfileCosts(proRatedProfileCosts); - - // Append an indication that this edge uses a partial profile cost. - nameWithNodeIds = String.format("%s r%4.3f l%4.3f", nameWithNodeIds, ratio, length); - // System.out.printf("Way (partial): %s size %d%n", nameWithNodeIds, proRatedProfileCosts.size()); - System.out.printf( - "%s %f%n", - nameWithNodeIds, - proRatedProfileCosts.get(MobilityProfile.WCHAIRE) - ); + seb.withName(nameWithNodeIds); + // LOG.info("Applied mobility profile costs between nodes {}-{}", startShortId, endShortId); + // Keep tab of node pairs for which mobility profile costs have been mapped. + mappedMobilityProfileEntries.add(key); } - - seb.withName(nameWithNodeIds); - // LOG.info("Applied mobility profile costs between nodes {}-{}", startShortId, endShortId); - // Keep tab of node pairs for which mobility profile costs have been mapped. - mappedMobilityProfileEntries.add(wayId); + } catch (NumberFormatException nfe) { + // Don't do anything related to mobility profiles if node ids are non-numerical. + LOG.info("Not applying mobility costs for link {}:{}→{}", way.getId(), startEndpoint.getLabel(), endEndpoint.getLabel()); } } From 00c04025cac595f42ac3435c2d0b78e7b9a9ab6b Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 11 Apr 2024 10:10:26 -0400 Subject: [PATCH 054/111] style(OsmModule): Apply prettier --- .../graph_builder/module/osm/OsmModule.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 88a39d7c36f..40c46680e0b 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -664,7 +664,12 @@ private StreetEdge getEdgeForStreet( } } catch (NumberFormatException nfe) { // Don't do anything related to mobility profiles if node ids are non-numerical. - LOG.info("Not applying mobility costs for link {}:{}→{}", way.getId(), startEndpoint.getLabel(), endEndpoint.getLabel()); + LOG.info( + "Not applying mobility costs for link {}:{}→{}", + way.getId(), + startEndpoint.getLabel(), + endEndpoint.getLabel() + ); } } From 5cc3a4b31b2ff746aa292167c9bf88002d27fbb5 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 11 Apr 2024 11:17:56 -0400 Subject: [PATCH 055/111] refactor(MobilityProfileParser): Use EnumMap --- .../ext/mobilityprofile/MobilityProfileRoutingTest.java | 4 ++-- .../ext/mobilityprofile/MobilityProfileParser.java | 5 ++--- .../ext/mobilityprofile/MobilityProfileRouting.java | 3 +-- .../routing/graph/kryosupport/KryoBuilder.java | 4 ++++ .../opentripplanner/street/model/edge/StreetEdgeBuilder.java | 2 +- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java b/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java index 62ba94ca3af..d72e2ecd66b 100644 --- a/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java @@ -4,7 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.util.HashMap; +import java.util.EnumMap; import java.util.Map; import org.junit.jupiter.api.Test; import org.opentripplanner.openstreetmap.model.OSMWay; @@ -67,7 +67,7 @@ void canPreserveWalkPermissionOnFootway() { @Test void canProRateProfileCosts() { - Map profileCost = new HashMap<>(); + Map profileCost = new EnumMap<>(MobilityProfile.class); profileCost.put(MobilityProfile.NONE, 10.0f); profileCost.put(MobilityProfile.DEVICE, 100.0f); diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java index 4b2f88f4315..f0359e99172 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.util.EnumMap; import java.util.HashMap; import java.util.Map; import org.slf4j.Logger; @@ -67,9 +68,7 @@ private static void parseRow( String key = getKey(id, fromNode, toNode); float lengthMeters = ONE_MILE_IN_METERS * Float.parseFloat(reader.get("Link Length")); - // The weight map has to be a HashMap instead of an EnumMap so that it is correctly - // persisted in the graph. - var weightMap = new HashMap(); + var weightMap = new EnumMap(MobilityProfile.class); for (var profile : MobilityProfile.values()) { currentColumnHeader = profile.getText(); try { diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java index db37a85d4ac..5257221e1aa 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java @@ -3,7 +3,6 @@ import static java.util.Map.entry; import java.util.EnumMap; -import java.util.HashMap; import java.util.Map; import org.opentripplanner.openstreetmap.model.OSMWay; import org.opentripplanner.street.model.StreetTraversalPermission; @@ -85,7 +84,7 @@ public static Map getProRatedProfileCosts( float ratio ) { // Has to be a HashMap for graph serialization - Map result = new HashMap<>(); + Map result = new EnumMap<>(MobilityProfile.class); cost.forEach((k, v) -> result.put(k, v * ratio)); return result; } diff --git a/src/main/java/org/opentripplanner/routing/graph/kryosupport/KryoBuilder.java b/src/main/java/org/opentripplanner/routing/graph/kryosupport/KryoBuilder.java index d094f8c945f..3836b8aa6e0 100644 --- a/src/main/java/org/opentripplanner/routing/graph/kryosupport/KryoBuilder.java +++ b/src/main/java/org/opentripplanner/routing/graph/kryosupport/KryoBuilder.java @@ -3,6 +3,7 @@ import com.conveyal.kryo.TIntArrayListSerializer; import com.conveyal.kryo.TIntIntHashMapSerializer; import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.serializers.EnumMapSerializer; import com.esotericsoftware.kryo.serializers.ExternalizableSerializer; import com.esotericsoftware.kryo.util.DefaultInstantiatorStrategy; import com.google.common.collect.ArrayListMultimap; @@ -12,11 +13,13 @@ import gnu.trove.impl.hash.TPrimitiveHash; import gnu.trove.list.array.TIntArrayList; import gnu.trove.map.hash.TIntIntHashMap; +import java.util.EnumMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import org.objenesis.strategy.SerializingInstantiatorStrategy; +import org.opentripplanner.ext.mobilityprofile.MobilityProfile; import org.opentripplanner.kryo.BuildConfigSerializer; import org.opentripplanner.kryo.RouterConfigSerializer; import org.opentripplanner.kryo.UnmodifiableCollectionsSerializer; @@ -65,6 +68,7 @@ public static Kryo create() { kryo.register(RouterConfig.class, new RouterConfigSerializer()); kryo.register(BuildConfig.class, new BuildConfigSerializer()); kryo.register(AtomicInteger.class, new AtomicIntegerSerializer()); + kryo.register(EnumMap.class, new EnumMapSerializer(MobilityProfile.class)); UnmodifiableCollectionsSerializer.registerSerializers(kryo); // Instantiation strategy: how should Kryo make new instances of objects when they are deserialized? diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java index c7d20dd3c70..f49a37a5d6b 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java @@ -42,7 +42,7 @@ public class StreetEdgeBuilder> { private float bicycleSafetyFactor; private short flags; private StreetElevationExtension streetElevationExtension; - private Map profileCosts = new HashMap<>(); // new EnumMap<>(MobilityProfile.class); + private Map profileCosts = new EnumMap<>(MobilityProfile.class); public StreetEdgeBuilder() { this.defaultLength = true; From 160c1401145cccbcc3b1f9939b9037c9ce34209f Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 16 Apr 2024 15:36:04 -0400 Subject: [PATCH 056/111] refactor(MobilityProfileRouting): Fix temporal unit in comment. --- .../ext/mobilityprofile/MobilityProfileRouting.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java index 5257221e1aa..faef9e8828b 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java @@ -44,7 +44,7 @@ private MobilityProfileRouting() { // Np public constructor. } - /** Computes the travel time, in minutes, for the given distance and mobility profile. */ + /** Computes the travel time, in hours, for the given distance and mobility profile. */ public static float computeTravelHours(double meters, MobilityProfile mobilityProfile) { return (float) ( meters / @@ -65,6 +65,7 @@ public static StreetTraversalPermission adjustPedestrianPermissions( OSMWay way, StreetTraversalPermission permissions ) { + //System.out.println("Way %s%n", way.); return isHighwayFootway(way) ? permissions : permissions.remove(StreetTraversalPermission.PEDESTRIAN); From ccfea073603a41a846e09bc911185e32efcd3ce3 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 16 Apr 2024 17:50:47 -0400 Subject: [PATCH 057/111] fix(StreetEdge): Convert mobility weights to seconds. Update comments. --- .../street/model/edge/StreetEdge.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index 40ef7675da3..bb3b3bd7930 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -1325,27 +1325,27 @@ private TraversalCosts walkingTraversalCosts( ); } - // G-MAP-specific: Tabulated travel times are provided through profileCost + // G-MAP-specific: Tabulated weights for known paths are provided through profileCost // (assuming a pre-determined travel speed for each profile) - // and are used to overwrite the time calculated above (convert from hours to seconds). - // If no tabulated times are available for the edge, compute them using the - // travel speeds for the given mobility profile. + // and the travel speed for that profile is used to overwrite the time calculated above. if (mobilityProfile != null) { - var defaultTravelHours = MobilityProfileRouting.computeTravelHours( - getEffectiveWalkDistance(), - mobilityProfile - ); - if (profileCost != null) { var travelTimeHours = profileCost.getOrDefault(mobilityProfile, (float) DEFAULT_LARGE_COST); + var defaultTravelHours = MobilityProfileRouting.computeTravelHours( + getEffectiveWalkDistance(), + mobilityProfile + ); time = defaultTravelHours * 3600; - weight = travelTimeHours; + // Convert the impedance of the path to seconds to match with other OTP weights, + // to make impedances compatible with street/transit transitions. + var travelImpedanceHours = profileCost.getOrDefault(mobilityProfile, (float)(weight / 3600)); + weight = travelImpedanceHours * 3600; } else { - // Assign a high travel time and weight to non-tabulated ways. - time = 360000; + // For non-tabulated ways, use the calculated travel time above but assign a high weight. weight = DEFAULT_LARGE_COST * 10.0; } } + return new TraversalCosts(time, weight); } From 808c8505e88896ec5e8b8aaec33e28b93b39bcf6 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 17 Apr 2024 16:21:11 -0400 Subject: [PATCH 058/111] style(StreetEdge): Remove unused statement, apply prettier --- .../org/opentripplanner/street/model/edge/StreetEdge.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index bb3b3bd7930..cd8e7516b50 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -1330,7 +1330,6 @@ private TraversalCosts walkingTraversalCosts( // and the travel speed for that profile is used to overwrite the time calculated above. if (mobilityProfile != null) { if (profileCost != null) { - var travelTimeHours = profileCost.getOrDefault(mobilityProfile, (float) DEFAULT_LARGE_COST); var defaultTravelHours = MobilityProfileRouting.computeTravelHours( getEffectiveWalkDistance(), mobilityProfile @@ -1338,7 +1337,10 @@ private TraversalCosts walkingTraversalCosts( time = defaultTravelHours * 3600; // Convert the impedance of the path to seconds to match with other OTP weights, // to make impedances compatible with street/transit transitions. - var travelImpedanceHours = profileCost.getOrDefault(mobilityProfile, (float)(weight / 3600)); + var travelImpedanceHours = profileCost.getOrDefault( + mobilityProfile, + (float) (weight / 3600) + ); weight = travelImpedanceHours * 3600; } else { // For non-tabulated ways, use the calculated travel time above but assign a high weight. From bdec742afd277946ed732c05fc85c54367dbc25d Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 19 Apr 2024 11:23:10 -0400 Subject: [PATCH 059/111] refactor(OSMWay): Move isFootway method to OSMWay. --- .../mobilityprofile/MobilityProfileRoutingTest.java | 4 ++-- .../ext/mobilityprofile/MobilityProfileRouting.java | 11 +---------- .../opentripplanner/openstreetmap/model/OSMWay.java | 4 ++++ 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java b/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java index d72e2ecd66b..c21811ce049 100644 --- a/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java @@ -29,8 +29,8 @@ void canComputeTravelTime() { @Test void canDetectHighwayFootwayTag() { - assertTrue(MobilityProfileRouting.isHighwayFootway(createFootway())); - assertFalse(MobilityProfileRouting.isHighwayFootway(createServiceWay())); + assertTrue(createFootway().isFootway()); + assertFalse(createServiceWay().isFootway()); } private static OSMWay createServiceWay() { diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java index faef9e8828b..7ba5d26bff4 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java @@ -11,10 +11,6 @@ public class MobilityProfileRouting { - public static final String HIGHWAY_TAG = "highway"; - - public static final String FOOTWAY_TAG_VALUE = "footway"; - private static final Map TRAVEL_SPEED_MPH_BY_PROFILE = new EnumMap<>( Map.ofEntries( entry(MobilityProfile.NONE, 2.5f), @@ -57,16 +53,11 @@ public static float computeTravelHours(double meters, MobilityProfile mobilityPr ); } - public static boolean isHighwayFootway(OSMWay way) { - return way.hasTag(HIGHWAY_TAG) && FOOTWAY_TAG_VALUE.equals(way.getTag(HIGHWAY_TAG)); - } - public static StreetTraversalPermission adjustPedestrianPermissions( OSMWay way, StreetTraversalPermission permissions ) { - //System.out.println("Way %s%n", way.); - return isHighwayFootway(way) + return way.isFootway() ? permissions : permissions.remove(StreetTraversalPermission.PEDESTRIAN); } diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java index 1ce6c698f22..a3260c71195 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java @@ -151,6 +151,10 @@ public boolean isArea() { return isTag("area", "yes"); } + public boolean isFootway() { + return "footway".equals(getTag("highway")); + } + /** * Given a set of {@code permissions} check if it can really be applied to both directions * of the way and return the permissions for both cases. From 1f407cd728d5d156513fc681103b4b9ec9982483 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 19 Apr 2024 14:04:21 -0400 Subject: [PATCH 060/111] improvement(OsmModule): Tag crosswalks with cross-street names. --- .../graph_builder/module/osm/OsmModule.java | 61 ++++++++++++++++--- .../openstreetmap/model/OSMWay.java | 5 ++ 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 40c46680e0b..348299e55b4 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -4,12 +4,14 @@ import gnu.trove.iterator.TLongIterator; import gnu.trove.list.TLongList; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import javax.annotation.Nonnull; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; @@ -65,6 +67,7 @@ public class OsmModule implements GraphBuilderModule { private final OsmDatabase osmdb; private Map mobilityProfileData; private HashSet mappedMobilityProfileEntries; + private List osmStreets; private final StreetLimitationParameters streetLimitationParameters; OsmModule( @@ -560,7 +563,8 @@ private StreetEdge getEdgeForStreet( LineString geometry, boolean back ) { - String label = "way " + way.getId() + " from " + index; + long wayId = way.getId(); + String label = "way " + wayId + " from " + index; label = label.intern(); I18NString name = params.edgeNamer().getNameForWay(way, label); float carSpeed = way.getOsmProvider().getOsmTagMapper().getCarSpeedForWay(way, back); @@ -584,6 +588,25 @@ private StreetEdge getEdgeForStreet( .withStairs(way.isSteps()) .withWheelchairAccessible(way.isWheelchairAccessible()); + boolean hasBogusName = !way.hasTag("name") && !way.hasTag("ref"); + + // If this is a street crossing (denoted with the tag "footway:crossing"), + // add a crossing indication in the edge name. + String editedName = name.toString(); + if (way.isMarkedCrossing()) { + editedName = "crosswalk"; + + // Scan the nodes of this way to find the intersecting street, + // i.e. a named way that is not a "highway: footway". + var otherWay = getIntersectingStreet(way); + if (otherWay.isPresent()) { + editedName = String.format("crosswalk over %s", otherWay.get().getTag("name")); + } + + seb.withName(editedName); + hasBogusName = false; + } + // Lookup costs by mobility profile, if any were defined. // Note that edges are bidirectional, so we check that mobility data exist in both directions. if (mobilityProfileData != null) { @@ -597,14 +620,15 @@ private StreetEdge getEdgeForStreet( // For testing, indicate the OSM node ids (remove prefixes). String nameWithNodeIds = String.format( "%s (%s, %s→%s)", - name, - way.getId(), + editedName, + wayId, startShortId, endShortId ); + seb.withName(nameWithNodeIds); - String wayId = Long.toString(way.getId(), 10); + String wayIdStr = Long.toString(wayId, 10); TLongList nodeRefs = way.getNodeRefs(); int startIndex = nodeRefs.indexOf(startShortId); int endIndex = nodeRefs.indexOf(endShortId); @@ -614,8 +638,8 @@ private StreetEdge getEdgeForStreet( long wayFromId = nodeRefs.get(0); long wayToId = nodeRefs.get(nodeRefs.size() - 1); String key = isReverse - ? MobilityProfileParser.getKey(wayId, wayToId, wayFromId) - : MobilityProfileParser.getKey(wayId, wayFromId, wayToId); + ? MobilityProfileParser.getKey(wayIdStr, wayToId, wayFromId) + : MobilityProfileParser.getKey(wayIdStr, wayFromId, wayToId); var edgeMobilityCostMap = mobilityProfileData.get(key); if (edgeMobilityCostMap != null) { @@ -666,16 +690,14 @@ private StreetEdge getEdgeForStreet( // Don't do anything related to mobility profiles if node ids are non-numerical. LOG.info( "Not applying mobility costs for link {}:{}→{}", - way.getId(), + wayId, startEndpoint.getLabel(), endEndpoint.getLabel() ); } } - if (!way.hasTag("name") && !way.hasTag("ref")) { - seb.withBogusName(true); - } + seb.withBogusName(hasBogusName); StreetEdge street = seb.buildAndConnect(); params.edgeNamer().recordEdge(way, street); @@ -683,6 +705,25 @@ private StreetEdge getEdgeForStreet( return street; } + private Optional getIntersectingStreet(OSMWay way) { + if (osmStreets == null) { + osmStreets = osmdb + .getWays() + .stream() + .filter(w -> !w.isFootway()) + .filter(w -> w.hasTag("name")) + .toList(); + } + + long[] wayNodeRefs = way.getNodeRefs().toArray(); + long wayId = way.getId(); + return osmStreets + .stream() + .filter(w -> w.getId() != wayId) + .filter(w -> Arrays.stream(wayNodeRefs).anyMatch(nid -> w.getNodeRefs().contains(nid))) + .findFirst(); + } + private float getMaxCarSpeed() { float maxSpeed = 0f; for (OsmProvider provider : providers) { diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java index a3260c71195..5f42e14b5f6 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java @@ -155,6 +155,11 @@ public boolean isFootway() { return "footway".equals(getTag("highway")); } + public boolean isMarkedCrossing() { + return "crossing".equals(getTag("footway")) && + "yes".equals(getTag("crossing:markings")); + } + /** * Given a set of {@code permissions} check if it can really be applied to both directions * of the way and return the permissions for both cases. From 30968efde256bf1cb833816df30215e424c583dc Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 19 Apr 2024 14:06:16 -0400 Subject: [PATCH 061/111] refactor(StreetEdgeBuilder): Remove unused import --- .../org/opentripplanner/street/model/edge/StreetEdgeBuilder.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java index f49a37a5d6b..221aca661f8 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java @@ -12,7 +12,6 @@ import static org.opentripplanner.street.model.edge.StreetEdge.WHEELCHAIR_ACCESSIBLE_FLAG_INDEX; import java.util.EnumMap; -import java.util.HashMap; import java.util.Map; import org.locationtech.jts.geom.LineString; import org.opentripplanner.ext.mobilityprofile.MobilityProfile; From 48937a4d717353935d17a0efc67fb359b7eb745b Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 19 Apr 2024 16:51:22 -0400 Subject: [PATCH 062/111] improvement(OsmModule): Move sidewalk id outside of parenthesis --- .../graph_builder/module/osm/OsmModule.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 348299e55b4..94a7813028c 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -594,17 +594,17 @@ private StreetEdge getEdgeForStreet( // add a crossing indication in the edge name. String editedName = name.toString(); if (way.isMarkedCrossing()) { - editedName = "crosswalk"; - // Scan the nodes of this way to find the intersecting street, // i.e. a named way that is not a "highway: footway". var otherWay = getIntersectingStreet(way); - if (otherWay.isPresent()) { - editedName = String.format("crosswalk over %s", otherWay.get().getTag("name")); - } + editedName = otherWay.isPresent() + ? String.format("crosswalk over %s", otherWay.get().getTag("name")) + : String.format("crosswalk %s", wayId); seb.withName(editedName); hasBogusName = false; + } else if ("sidewalk".equals(editedName) || "path".equals(editedName)) { + editedName = String.format("%s %s", editedName, wayId); } // Lookup costs by mobility profile, if any were defined. From 06c85fc5153d5a66fdbaaf180e5222c5a12715d4 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 22 Apr 2024 10:58:41 -0400 Subject: [PATCH 063/111] improvement(OSMWay): Add criteria for footway, service road, marked crossing --- .../openstreetmap/model/OSMWay.java | 6 +- .../openstreetmap/model/OSMWayTest.java | 56 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java index 5f42e14b5f6..3692899bc94 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java @@ -157,7 +157,11 @@ public boolean isFootway() { public boolean isMarkedCrossing() { return "crossing".equals(getTag("footway")) && - "yes".equals(getTag("crossing:markings")); + "yes".equals(getTag("crossing:markings")) || "marked".equals(getTag("crossing")); + } + + public boolean isServiceRoad() { + return "service".equals(getTag("highway")); } /** diff --git a/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java b/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java index c316793ad8c..fba66271f78 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java +++ b/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java @@ -1,9 +1,14 @@ package org.opentripplanner.openstreetmap.model; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.opentripplanner.openstreetmap.wayproperty.specifier.WayTestData; public class OSMWayTest { @@ -140,4 +145,55 @@ void escalator() { escalator.addTag("conveying", "whoknows?"); assertFalse(escalator.isEscalator()); } + + private static OSMWay createGenericHighway() { + var osm = new OSMWay(); + osm.addTag("highway", "primary"); + return osm; + } + + private static OSMWay createGenericFootway() { + var osm = new OSMWay(); + osm.addTag("highway", "footway"); + return osm; + } + + private static OSMWay createFootway(String footwayValue, String crossingTag, String crossingValue) { + var osm = createGenericFootway(); + osm.addTag("footway", footwayValue); + osm.addTag(crossingTag, crossingValue); + return osm; + } + + @Test + void footway() { + assertFalse(createGenericHighway().isFootway()); + assertTrue(createGenericFootway().isFootway()); + } + + @Test + void serviceRoad() { + assertFalse(createGenericHighway().isServiceRoad()); + + var osm2 = new OSMWay(); + osm2.addTag("highway", "service"); + assertTrue(osm2.isServiceRoad()); + } + + @ParameterizedTest + @MethodSource("createCrossingCases") + void markedCrossing(OSMWay way, boolean result) { + assertEquals(result, way.isMarkedCrossing()); + } + + static Stream createCrossingCases() { + return Stream.of( + Arguments.of(createGenericFootway(), false), + Arguments.of(createFootway("whatever", "unused", "unused"), false), + Arguments.of(createFootway("crossing", "crossing", "marked"), true), + Arguments.of(createFootway("crossing", "crossing", "other"), false), + Arguments.of(createFootway("crossing", "crossing:markings", "yes"), true), + Arguments.of(createFootway("crossing", "crossing:markings", "no"), false) + ); + } } From e6a6dbe543a386a62e995272134229fbac5c1d58 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 22 Apr 2024 17:02:49 -0400 Subject: [PATCH 064/111] refactor(StreetEdge): Assume mobility profile impedances in seconds --- .../opentripplanner/street/model/edge/StreetEdge.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index cd8e7516b50..376cca7a473 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -1335,13 +1335,12 @@ private TraversalCosts walkingTraversalCosts( mobilityProfile ); time = defaultTravelHours * 3600; - // Convert the impedance of the path to seconds to match with other OTP weights, - // to make impedances compatible with street/transit transitions. - var travelImpedanceHours = profileCost.getOrDefault( + // Impedance of the path is in seconds, so it already matches other OTP weights + // for compatibility with street/transit transitions. + weight = profileCost.getOrDefault( mobilityProfile, - (float) (weight / 3600) + (float) weight ); - weight = travelImpedanceHours * 3600; } else { // For non-tabulated ways, use the calculated travel time above but assign a high weight. weight = DEFAULT_LARGE_COST * 10.0; From a86b1cffeba255dac6b1f99f6f42703f8dcc3522 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 23 Apr 2024 10:30:56 -0400 Subject: [PATCH 065/111] improvement(StatesToWalkStepsMapper): Collapse short 'continue' steps into next. --- .../mapping/StatesToWalkStepsMapper.java | 143 +++++++++++------- .../street/model/edge/StreetEdge.java | 5 + 2 files changed, 93 insertions(+), 55 deletions(-) diff --git a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java index 94905bb840a..e2c65c1b8d0 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java @@ -1,5 +1,6 @@ package org.opentripplanner.routing.algorithm.mapping; +import static org.opentripplanner.model.plan.RelativeDirection.CONTINUE; import static org.opentripplanner.model.plan.RelativeDirection.ENTER_STATION; import static org.opentripplanner.model.plan.RelativeDirection.EXIT_STATION; import static org.opentripplanner.model.plan.RelativeDirection.FOLLOW_SIGNS; @@ -189,69 +190,85 @@ private void processState(State backState, State forwardState) { if (current == null) { createFirstStep(backState, forwardState); createdNewStep = true; - } else if ( - modeTransition || - !continueOnSameStreet(edge, streetNameNoParens) || - // went on to or off of a roundabout - edge.isRoundabout() != - (roundaboutExit > 0) || - isLink(edge) && - !isLink(backState.getBackEdge()) - ) { - // Street name has changed, or we've gone on to or off of a roundabout. - - // if we were just on a roundabout, make note of which exit was taken in the existing step - if (roundaboutExit > 0) { - // ordinal numbers from - current.withExit(Integer.toString(roundaboutExit)); - if (streetNameNoParens.equals(roundaboutPreviousStreet)) { - current.withStayOn(true); - } - roundaboutExit = 0; - } - - // start a new step - current = createWalkStep(forwardState, backState); - createdNewStep = true; - steps.add(current); - - // indicate that we are now on a roundabout and use one-based exit numbering - if (edge.isRoundabout()) { - roundaboutExit = 1; - roundaboutPreviousStreet = getNormalizedName(backState.getBackEdge().getName().toString()); - } - - double thisAngle = DirectionUtils.getFirstAngle(geom); - current.withDirections(lastAngle, thisAngle, edge.isRoundabout()); - // new step, set distance to length of first edge - distance = edge.getDistanceMeters(); } else { - // street name has not changed double thisAngle = DirectionUtils.getFirstAngle(geom); RelativeDirection direction = RelativeDirection.calculate( lastAngle, thisAngle, edge.isRoundabout() ); - if (edge.isRoundabout()) { - // we are on a roundabout, and have already traversed at least one edge of it. - if (multipleTurnOptionsInPreviousState(backState)) { - // increment exit count if we passed one. - roundaboutExit += 1; + + // HACK: If the instruction is "continue", the current street name is bogus and its length is very short (< 10 meters) + // but not the next edge one, use the next street name and don't start a new step. + if (shouldOverwriteCurrentDirectionText(edge, direction)) { + current.withDirectionText(I18NString.of(streetNameNoParens)); + current.withBogusName(false); + } + // HACK: Similar hack if the next edge name is bogus and its length is very short (< 10 meters) + // but not the current step. In this case, continue using the current street name and don't start a new step. + if (edge instanceof StreetEdge streetEdge && shouldOverwriteEdgeDirectionText(edge, direction)) { + streetNameNoParens = current.directionTextNoParens(); + streetEdge.setName(current.directionText()); + streetEdge.setBogusName(false); + } + + if ( + modeTransition || + !continueOnSameStreet(edge, streetNameNoParens) || + // went on to or off of a roundabout + edge.isRoundabout() != + (roundaboutExit > 0) || + isLink(edge) && + !isLink(backState.getBackEdge()) + ) { + // Street name has changed, or we've gone on to or off of a roundabout. + + // if we were just on a roundabout, make note of which exit was taken in the existing step + if (roundaboutExit > 0) { + // ordinal numbers from + current.withExit(Integer.toString(roundaboutExit)); + if (streetNameNoParens.equals(roundaboutPreviousStreet)) { + current.withStayOn(true); + } + roundaboutExit = 0; + } + + // start a new step + current = createWalkStep(forwardState, backState); + createdNewStep = true; + steps.add(current); + + // indicate that we are now on a roundabout and use one-based exit numbering + if (edge.isRoundabout()) { + roundaboutExit = 1; + roundaboutPreviousStreet = getNormalizedName(backState.getBackEdge().getName().toString()); } - } else if (direction != RelativeDirection.CONTINUE) { - // we are not on a roundabout, and not continuing straight through. - // figure out if there were other plausible turn options at the last intersection - // to see if we should generate a "left to continue" instruction. - if (isPossibleToTurnToOtherStreet(backState, edge, streetName, thisAngle)) { - // turn to stay on same-named street - current = createWalkStep(forwardState, backState); - createdNewStep = true; - current.withDirections(lastAngle, thisAngle, false); - current.withStayOn(true); - steps.add(current); - // new step, set distance to length of first edge - distance = edge.getDistanceMeters(); + + current.withDirections(lastAngle, thisAngle, edge.isRoundabout()); + // new step, set distance to length of first edge + distance = edge.getDistanceMeters(); + } else { + // street name has not changed + if (edge.isRoundabout()) { + // we are on a roundabout, and have already traversed at least one edge of it. + if (multipleTurnOptionsInPreviousState(backState)) { + // increment exit count if we passed one. + roundaboutExit += 1; + } + } else if (direction != RelativeDirection.CONTINUE) { + // we are not on a roundabout, and not continuing straight through. + // figure out if there were other plausible turn options at the last intersection + // to see if we should generate a "left to continue" instruction. + if (isPossibleToTurnToOtherStreet(backState, edge, streetName, thisAngle)) { + // turn to stay on same-named street + current = createWalkStep(forwardState, backState); + createdNewStep = true; + current.withDirections(lastAngle, thisAngle, false); + current.withStayOn(true); + steps.add(current); + // new step, set distance to length of first edge + distance = edge.getDistanceMeters(); + } } } } @@ -428,6 +445,22 @@ private boolean isTurnToOtherStreet(String streetName, double angleDiff, Edge al return angleDiff > Math.PI / 4 || altAngleDiff - angleDiff < Math.PI / 16; } + private boolean shouldOverwriteCurrentDirectionText(Edge edge, RelativeDirection direction) { + return + direction == CONTINUE && + distance < 10 && + current.bogusName() + && !edge.hasBogusName(); + } + + private boolean shouldOverwriteEdgeDirectionText(Edge edge, RelativeDirection direction) { + return + direction == CONTINUE && + edge.getDistanceMeters() < 10 && + !current.bogusName() + && edge.hasBogusName(); + } + private boolean continueOnSameStreet(Edge edge, String streetNameNoParens) { return !( current.directionText().toString() != null && diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index 376cca7a473..d1284a19577 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -460,10 +460,15 @@ public void setName(I18NString name) { this.name = name; } + @Override public boolean hasBogusName() { return BitSetUtils.get(flags, HASBOGUSNAME_FLAG_INDEX); } + public void setBogusName(boolean bogusName) { + flags = BitSetUtils.set(flags, HASBOGUSNAME_FLAG_INDEX, bogusName); + } + public LineString getGeometry() { return CompactLineStringUtils.uncompactLineString( fromv.getLon(), From 13ff3d73f02555266e898457bd924a890e02dd93 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 23 Apr 2024 12:13:32 -0400 Subject: [PATCH 066/111] refactor(StatesToWalkStepsMapper): Move comments regarding overwriting street names --- .../algorithm/mapping/StatesToWalkStepsMapper.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java index e2c65c1b8d0..ee5f938866e 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java @@ -198,15 +198,15 @@ private void processState(State backState, State forwardState) { edge.isRoundabout() ); - // HACK: If the instruction is "continue", the current street name is bogus and its length is very short (< 10 meters) - // but not the next edge one, use the next street name and don't start a new step. if (shouldOverwriteCurrentDirectionText(edge, direction)) { + // HACK: If the instruction is "continue", the current street name is bogus and its length is very short (< 10 meters) + // but not the next edge one, use the next street name and don't start a new step. current.withDirectionText(I18NString.of(streetNameNoParens)); current.withBogusName(false); } - // HACK: Similar hack if the next edge name is bogus and its length is very short (< 10 meters) - // but not the current step. In this case, continue using the current street name and don't start a new step. if (edge instanceof StreetEdge streetEdge && shouldOverwriteEdgeDirectionText(edge, direction)) { + // HACK: Similar hack if the next edge name is bogus and its length is very short (< 10 meters) + // but not the current step. In this case, continue using the current street name and don't start a new step. streetNameNoParens = current.directionTextNoParens(); streetEdge.setName(current.directionText()); streetEdge.setBogusName(false); From 1288768461d9bf10cda22f882bb670100ede5102 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 23 Apr 2024 15:40:16 -0400 Subject: [PATCH 067/111] improvement(StatesToWalkStepsMapper): Avoid reusing crossing names in zags. --- .../mapping/StatesToWalkStepsMapper.java | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java index ee5f938866e..ca9144b7bd7 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java @@ -204,7 +204,9 @@ private void processState(State backState, State forwardState) { current.withDirectionText(I18NString.of(streetNameNoParens)); current.withBogusName(false); } - if (edge instanceof StreetEdge streetEdge && shouldOverwriteEdgeDirectionText(edge, direction)) { + if ( + edge instanceof StreetEdge streetEdge && shouldOverwriteEdgeDirectionText(edge, direction) + ) { // HACK: Similar hack if the next edge name is bogus and its length is very short (< 10 meters) // but not the current step. In this case, continue using the current street name and don't start a new step. streetNameNoParens = current.directionTextNoParens(); @@ -214,12 +216,12 @@ private void processState(State backState, State forwardState) { if ( modeTransition || - !continueOnSameStreet(edge, streetNameNoParens) || - // went on to or off of a roundabout - edge.isRoundabout() != - (roundaboutExit > 0) || - isLink(edge) && - !isLink(backState.getBackEdge()) + !continueOnSameStreet(edge, streetNameNoParens) || + // went on to or off of a roundabout + edge.isRoundabout() != + (roundaboutExit > 0) || + isLink(edge) && + !isLink(backState.getBackEdge()) ) { // Street name has changed, or we've gone on to or off of a roundabout. @@ -241,7 +243,8 @@ private void processState(State backState, State forwardState) { // indicate that we are now on a roundabout and use one-based exit numbering if (edge.isRoundabout()) { roundaboutExit = 1; - roundaboutPreviousStreet = getNormalizedName(backState.getBackEdge().getName().toString()); + roundaboutPreviousStreet = + getNormalizedName(backState.getBackEdge().getName().toString()); } current.withDirections(lastAngle, thisAngle, edge.isRoundabout()); @@ -282,9 +285,9 @@ private void processState(State backState, State forwardState) { WalkStepBuilder threeBack = steps.get(lastIndex - 2); WalkStepBuilder twoBack = steps.get(lastIndex - 1); WalkStepBuilder lastStep = steps.get(lastIndex); - boolean isOnSameStreet = lastStep - .directionTextNoParens() - .equals(threeBack.directionTextNoParens()); + boolean isOnSameStreet = + !lastStep.directionTextNoParens().startsWith("crosswalk") && + lastStep.directionTextNoParens().equals(threeBack.directionTextNoParens()); if (twoBack.distance() < MAX_ZAG_DISTANCE && isOnSameStreet) { if (isUTurn(twoBack, lastStep)) { steps.remove(lastIndex - 1); @@ -446,19 +449,16 @@ private boolean isTurnToOtherStreet(String streetName, double angleDiff, Edge al } private boolean shouldOverwriteCurrentDirectionText(Edge edge, RelativeDirection direction) { - return - direction == CONTINUE && - distance < 10 && - current.bogusName() - && !edge.hasBogusName(); + return direction == CONTINUE && distance < 10 && current.bogusName() && !edge.hasBogusName(); } private boolean shouldOverwriteEdgeDirectionText(Edge edge, RelativeDirection direction) { - return + return ( direction == CONTINUE && - edge.getDistanceMeters() < 10 && - !current.bogusName() - && edge.hasBogusName(); + edge.getDistanceMeters() < 10 && + !current.bogusName() && + edge.hasBogusName() + ); } private boolean continueOnSameStreet(Edge edge, String streetNameNoParens) { From d80e2be94c6824e59f85efb7113f4dab1717fea2 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 23 Apr 2024 15:41:54 -0400 Subject: [PATCH 068/111] improvement(OsmModule): Expand crosswalk support to slip lanes, service roads. --- .../graph_builder/module/osm/OsmModule.java | 53 ++++++++++++------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 94a7813028c..84eb3b976bc 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -594,12 +594,20 @@ private StreetEdge getEdgeForStreet( // add a crossing indication in the edge name. String editedName = name.toString(); if (way.isMarkedCrossing()) { - // Scan the nodes of this way to find the intersecting street, - // i.e. a named way that is not a "highway: footway". - var otherWay = getIntersectingStreet(way); - editedName = otherWay.isPresent() - ? String.format("crosswalk over %s", otherWay.get().getTag("name")) - : String.format("crosswalk %s", wayId); + // Scan the nodes of this way to find the intersecting street. + var otherWayOpt = getIntersectingStreet(way); + if (otherWayOpt.isPresent()) { + OSMWay otherWay = otherWayOpt.get(); + if (otherWay.hasTag("name")) { + editedName = String.format("crosswalk over %s", otherWay.getTag("name")); + } else if (otherWay.isServiceRoad()) { + editedName = "crosswalk over service road"; + } else if (otherWay.isOneWayForwardDriving()) { + editedName = "crosswalk over turn lane"; + } else { + editedName = String.format("crosswalk %s", wayId); + } + } seb.withName(editedName); hasBogusName = false; @@ -682,7 +690,7 @@ private StreetEdge getEdgeForStreet( } seb.withName(nameWithNodeIds); - // LOG.info("Applied mobility profile costs between nodes {}-{}", startShortId, endShortId); + // Keep tab of node pairs for which mobility profile costs have been mapped. mappedMobilityProfileEntries.add(key); } @@ -707,21 +715,26 @@ private StreetEdge getEdgeForStreet( private Optional getIntersectingStreet(OSMWay way) { if (osmStreets == null) { - osmStreets = osmdb - .getWays() - .stream() - .filter(w -> !w.isFootway()) - .filter(w -> w.hasTag("name")) - .toList(); + osmStreets = + osmdb + .getWays() + .stream() + .filter(w -> !w.isFootway()) + // Keep named streets, service roads, and slip/turn lanes. + .filter(w -> w.hasTag("name") || w.isServiceRoad() || w.isOneWayForwardDriving()) + .toList(); } - long[] wayNodeRefs = way.getNodeRefs().toArray(); - long wayId = way.getId(); - return osmStreets - .stream() - .filter(w -> w.getId() != wayId) - .filter(w -> Arrays.stream(wayNodeRefs).anyMatch(nid -> w.getNodeRefs().contains(nid))) - .findFirst(); + TLongList nodeRefs = way.getNodeRefs(); + if (nodeRefs.size() >= 3) { + // Exclude the first and last node which are on the sidewalk. + long[] nodeRefsArray = nodeRefs.toArray(1, nodeRefs.size() - 2); + return osmStreets + .stream() + .filter(w -> Arrays.stream(nodeRefsArray).anyMatch(nid -> w.getNodeRefs().contains(nid))) + .findFirst(); + } + return Optional.empty(); } private float getMaxCarSpeed() { From c051357f8003f1199d8825a887c97d94065515ab Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 23 Apr 2024 15:43:21 -0400 Subject: [PATCH 069/111] style: Apply prettier --- .../ext/mobilityprofile/MobilityProfileRouting.java | 4 +--- .../org/opentripplanner/openstreetmap/model/OSMWay.java | 7 +++++-- .../org/opentripplanner/street/model/edge/StreetEdge.java | 5 +---- .../opentripplanner/openstreetmap/model/OSMWayTest.java | 6 +++++- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java index 7ba5d26bff4..5dd17869907 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java @@ -57,9 +57,7 @@ public static StreetTraversalPermission adjustPedestrianPermissions( OSMWay way, StreetTraversalPermission permissions ) { - return way.isFootway() - ? permissions - : permissions.remove(StreetTraversalPermission.PEDESTRIAN); + return way.isFootway() ? permissions : permissions.remove(StreetTraversalPermission.PEDESTRIAN); } /** Multiplies profile costs by the distance ratio between the given edge and its parent. */ diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java index 3692899bc94..9bb60d68b6e 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java @@ -156,8 +156,11 @@ public boolean isFootway() { } public boolean isMarkedCrossing() { - return "crossing".equals(getTag("footway")) && - "yes".equals(getTag("crossing:markings")) || "marked".equals(getTag("crossing")); + return ( + "crossing".equals(getTag("footway")) && + "yes".equals(getTag("crossing:markings")) || + "marked".equals(getTag("crossing")) + ); } public boolean isServiceRoad() { diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index d1284a19577..89e90c487ef 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -1342,10 +1342,7 @@ private TraversalCosts walkingTraversalCosts( time = defaultTravelHours * 3600; // Impedance of the path is in seconds, so it already matches other OTP weights // for compatibility with street/transit transitions. - weight = profileCost.getOrDefault( - mobilityProfile, - (float) weight - ); + weight = profileCost.getOrDefault(mobilityProfile, (float) weight); } else { // For non-tabulated ways, use the calculated travel time above but assign a high weight. weight = DEFAULT_LARGE_COST * 10.0; diff --git a/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java b/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java index fba66271f78..38e6f1421e4 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java +++ b/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java @@ -158,7 +158,11 @@ private static OSMWay createGenericFootway() { return osm; } - private static OSMWay createFootway(String footwayValue, String crossingTag, String crossingValue) { + private static OSMWay createFootway( + String footwayValue, + String crossingTag, + String crossingValue + ) { var osm = createGenericFootway(); osm.addTag("footway", footwayValue); osm.addTag(crossingTag, crossingValue); From c23fc28bc5c58b6c0597366db92f6ef5b19b12f1 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 23 Apr 2024 18:17:25 -0400 Subject: [PATCH 070/111] refactor(StatesToWalkStepsMapper): Don't recombine streets without mobility profile. --- .../mapping/StatesToWalkStepsMapper.java | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java index ca9144b7bd7..dee709ec4f3 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java @@ -198,20 +198,22 @@ private void processState(State backState, State forwardState) { edge.isRoundabout() ); - if (shouldOverwriteCurrentDirectionText(edge, direction)) { - // HACK: If the instruction is "continue", the current street name is bogus and its length is very short (< 10 meters) - // but not the next edge one, use the next street name and don't start a new step. - current.withDirectionText(I18NString.of(streetNameNoParens)); - current.withBogusName(false); - } - if ( - edge instanceof StreetEdge streetEdge && shouldOverwriteEdgeDirectionText(edge, direction) - ) { - // HACK: Similar hack if the next edge name is bogus and its length is very short (< 10 meters) - // but not the current step. In this case, continue using the current street name and don't start a new step. - streetNameNoParens = current.directionTextNoParens(); - streetEdge.setName(current.directionText()); - streetEdge.setBogusName(false); + // G-MAP-specific: Overwrite the name on short street edges so they are merged with longer street + // sections to clean up turn-by-turn instructions. + if (edge instanceof StreetEdge streetEdge && !streetEdge.profileCost.isEmpty()) { + if (shouldOverwriteCurrentDirectionText(edge, direction)) { + // HACK: If the instruction is "continue", the current street name is bogus and its length is very short (< 10 meters) + // but not the next edge one, use the next street name and don't start a new step. + current.withDirectionText(I18NString.of(streetNameNoParens)); + current.withBogusName(false); + } + if (shouldOverwriteEdgeDirectionText(edge, direction)) { + // HACK: Similar hack if the next edge name is bogus and its length is very short (< 10 meters) + // but not the current step. In this case, continue using the current street name and don't start a new step. + streetNameNoParens = current.directionTextNoParens(); + streetEdge.setName(current.directionText()); + streetEdge.setBogusName(false); + } } if ( From 957a724fee4f3e5da7e15d42250678b420f84beb Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 25 Apr 2024 11:47:20 -0400 Subject: [PATCH 071/111] refactor(OsmModule): Rename "crosswalk" to "crossing". --- .../graph_builder/module/osm/OsmModule.java | 8 ++++---- .../algorithm/mapping/StatesToWalkStepsMapper.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 84eb3b976bc..84793d7aa01 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -599,13 +599,13 @@ private StreetEdge getEdgeForStreet( if (otherWayOpt.isPresent()) { OSMWay otherWay = otherWayOpt.get(); if (otherWay.hasTag("name")) { - editedName = String.format("crosswalk over %s", otherWay.getTag("name")); + editedName = String.format("crossing over %s", otherWay.getTag("name")); } else if (otherWay.isServiceRoad()) { - editedName = "crosswalk over service road"; + editedName = "crossing over service road"; } else if (otherWay.isOneWayForwardDriving()) { - editedName = "crosswalk over turn lane"; + editedName = "crossing over turn lane"; } else { - editedName = String.format("crosswalk %s", wayId); + editedName = String.format("crossing %s", wayId); } } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java index dee709ec4f3..7e8fdcc57d7 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java @@ -288,7 +288,7 @@ private void processState(State backState, State forwardState) { WalkStepBuilder twoBack = steps.get(lastIndex - 1); WalkStepBuilder lastStep = steps.get(lastIndex); boolean isOnSameStreet = - !lastStep.directionTextNoParens().startsWith("crosswalk") && + !lastStep.directionTextNoParens().startsWith("crossing") && lastStep.directionTextNoParens().equals(threeBack.directionTextNoParens()); if (twoBack.distance() < MAX_ZAG_DISTANCE && isOnSameStreet) { if (isUTurn(twoBack, lastStep)) { From da79421438dcc4514141d7d5fd37398636bf97d3 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 24 May 2024 11:29:03 -0400 Subject: [PATCH 072/111] refactor(OsmModule): Extract logic for finding intersecting street, add tests. --- .../graph_builder/module/osm/OsmModule.java | 12 ++++++++++-- .../module/osm/OsmModuleTest.java | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index cd33cfaa881..bd41f31fa84 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -591,6 +591,7 @@ private StreetEdge getEdgeForStreet( // If this is a street crossing (denoted with the tag "footway:crossing"), // add a crossing indication in the edge name. + // TODO: i18n. String editedName = name.toString(); if (way.isMarkedCrossing()) { // Scan the nodes of this way to find the intersecting street. @@ -604,6 +605,7 @@ private StreetEdge getEdgeForStreet( } else if (otherWay.isOneWayForwardDriving()) { editedName = "crossing over turn lane"; } else { + // Default on using the OSM way ID, which should not happen. editedName = String.format("crossing %s", wayId); } } @@ -719,11 +721,17 @@ private Optional getIntersectingStreet(OSMWay way) { .toList(); } + return getIntersectingStreet(way, osmStreets); + } + + public static Optional getIntersectingStreet(OSMWay way, List streets) { TLongList nodeRefs = way.getNodeRefs(); if (nodeRefs.size() >= 3) { - // Exclude the first and last node which are on the sidewalk. + // There needs to be at least three nodes: 2 extremities that are on the sidewalk, + // and one somewhere in the middle that joins the crossing with the street. + // We exclude the first and last node which are on the sidewalk. long[] nodeRefsArray = nodeRefs.toArray(1, nodeRefs.size() - 2); - return osmStreets + return streets .stream() .filter(w -> Arrays.stream(nodeRefsArray).anyMatch(nid -> w.getNodeRefs().contains(nid))) .findFirst(); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java index 091652a2dbf..638cec5f8aa 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java @@ -381,5 +381,24 @@ private void testBuildingAreas(boolean skipVisibility) { } } + @Test + void testGetIntersectingStreet() { + OSMWay way = new OSMWay(); + way.getNodeRefs().add(new long[] { 10001, 10002, 10003, 10004 }); + OSMWay street = new OSMWay(); + street.setId(50001); + street.getNodeRefs().add(new long[] { 20001, 20002, 20003, 10002, 20004, 20005 }); + OSMWay otherStreet = new OSMWay(); + otherStreet.setId(50002); + otherStreet.getNodeRefs().add(new long[] { 30001, 30002, 30003, 30004, 30005 }); + + var intersectingStreet = OsmModule.getIntersectingStreet(way, List.of(street, otherStreet)); + assertTrue(intersectingStreet.isPresent()); + assertEquals(50001, intersectingStreet.get().getId()); + + var intersectingStreet2 = OsmModule.getIntersectingStreet(way, List.of(otherStreet)); + assertFalse(intersectingStreet2.isPresent()); + } + private record VertexPair(Vertex v0, Vertex v1) {} } From 353a534c263cb398747d37e790312fb72d97abdf Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 24 May 2024 11:57:52 -0400 Subject: [PATCH 073/111] refactor(OsmModule): Extract logic for filtering streets, add tests. --- .../graph_builder/module/osm/OsmModule.java | 19 ++++++++++--------- .../module/osm/OsmModuleTest.java | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index bd41f31fa84..2e00e1a3387 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -709,18 +709,19 @@ private StreetEdge getEdgeForStreet( return seb.buildAndConnect(); } + public static List getStreets(Collection ways) { + return ways + .stream() + .filter(w -> !w.isFootway()) + // Keep named streets, service roads, and slip/turn lanes. + .filter(w -> w.hasTag("name") || w.isServiceRoad() || w.isOneWayForwardDriving()) + .toList(); + } + private Optional getIntersectingStreet(OSMWay way) { if (osmStreets == null) { - osmStreets = - osmdb - .getWays() - .stream() - .filter(w -> !w.isFootway()) - // Keep named streets, service roads, and slip/turn lanes. - .filter(w -> w.hasTag("name") || w.isServiceRoad() || w.isOneWayForwardDriving()) - .toList(); + osmStreets = getStreets(osmdb.getWays()); } - return getIntersectingStreet(way, osmStreets); } diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java index 638cec5f8aa..8aed4262f43 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java @@ -400,5 +400,24 @@ void testGetIntersectingStreet() { assertFalse(intersectingStreet2.isPresent()); } + @Test + void testGetStreets() { + OSMWay footway = new OSMWay(); + footway.addTag("highway", "footway"); + OSMWay street = new OSMWay(); + street.addTag("highway", "primary"); + street.addTag("name", "3rd Street"); + OSMWay serviceRoad = new OSMWay(); + serviceRoad.addTag("highway", "service"); + OSMWay otherStreet = new OSMWay(); + otherStreet.addTag("highway", "trunk"); + otherStreet.addTag("oneway", "true"); + OSMWay blankPath = new OSMWay(); + + List streets = OsmModule.getStreets(List.of(street, footway, serviceRoad, otherStreet, blankPath)); + assertEquals(3, streets.size()); + assertTrue(streets.containsAll(List.of(serviceRoad, street, otherStreet))); + } + private record VertexPair(Vertex v0, Vertex v1) {} } From c6efe6c63e633397df152f3cfddc766d3801e44d Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 24 May 2024 12:01:14 -0400 Subject: [PATCH 074/111] refactor(OsmModule): Add JavaDoc to extracted methods. --- .../opentripplanner/graph_builder/module/osm/OsmModule.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 2e00e1a3387..7480dbe5a79 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -709,6 +709,7 @@ private StreetEdge getEdgeForStreet( return seb.buildAndConnect(); } + /** Gets the streets from a collection of OSM ways. */ public static List getStreets(Collection ways) { return ways .stream() @@ -718,6 +719,7 @@ public static List getStreets(Collection ways) { .toList(); } + /** Gets the intersecting street, if any, for the given way using ways in osmdb. */ private Optional getIntersectingStreet(OSMWay way) { if (osmStreets == null) { osmStreets = getStreets(osmdb.getWays()); @@ -725,6 +727,7 @@ private Optional getIntersectingStreet(OSMWay way) { return getIntersectingStreet(way, osmStreets); } + /** Gets the intersecting street, if any, for the given way and candidate streets. */ public static Optional getIntersectingStreet(OSMWay way, List streets) { TLongList nodeRefs = way.getNodeRefs(); if (nodeRefs.size() >= 3) { From 1db49ee8261165556962d813654f82d488d7d2f3 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 24 May 2024 13:51:11 -0400 Subject: [PATCH 075/111] refactor(OsmModule): Add logic for finding continued ways. --- .../graph_builder/module/osm/OsmModule.java | 16 +++++++++++ .../openstreetmap/model/OSMWay.java | 14 ++++++++++ .../module/osm/OsmModuleTest.java | 28 +++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 7480dbe5a79..252f926f952 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -743,6 +743,22 @@ public static Optional getIntersectingStreet(OSMWay way, List st return Optional.empty(); } + /** Determines whether a way is a continuation (i.e. connects through end nodes) of marked crossing. */ + public static boolean isContinuationOfMarkedCrossing(OSMWay way, Collection ways) { + int adjacentWayCount = 0; + boolean markedCrossingFound = false; + + for (OSMWay w : ways) { + if (way.isAdjacentTo(w)) { + adjacentWayCount++; + if (!markedCrossingFound && w.isMarkedCrossing()) { + markedCrossingFound = true; + } + } + } + return markedCrossingFound && adjacentWayCount == 1; + } + private float getMaxCarSpeed() { float maxSpeed = 0f; for (OsmProvider provider : providers) { diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java index f3babcba6b2..8c483e56f77 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java @@ -157,6 +157,20 @@ public boolean isServiceRoad() { return "service".equals(getTag("highway")); } + /** Whether this way is connected to the given way through their extremities. */ + public boolean isAdjacentTo(OSMWay way) { + long wayFirstNode = way.nodes.get(0); + long wayLastNode = way.nodes.get(way.nodes.size() - 1); + + long firstNode = nodes.get(0); + long lastNode = nodes.get(nodes.size() - 1); + + return firstNode == wayFirstNode && lastNode != wayLastNode || + firstNode == wayLastNode && lastNode != wayFirstNode || + lastNode == wayFirstNode && firstNode != wayLastNode || + lastNode == wayLastNode && firstNode != wayFirstNode; + } + /** * Given a set of {@code permissions} check if it can really be applied to both directions * of the way and return the permissions for both cases. diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java index 8aed4262f43..6b852aa6ac0 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java @@ -419,5 +419,33 @@ void testGetStreets() { assertTrue(streets.containsAll(List.of(serviceRoad, street, otherStreet))); } + @Test + void testIsContinuationOfMarkedCrossing() { + OSMWay footway = new OSMWay(); + footway.addTag("highway", "footway"); + footway.getNodeRefs().add(new long[] { 10001, 10000, 10002 }); + + OSMWay crossing = new OSMWay(); + crossing.getNodeRefs().add(new long[] { 10002, 10003, 10004 }); + crossing.addTag("highway", "footway"); + crossing.addTag("footway", "crossing"); + crossing.addTag("crossing", "marked"); + + OSMWay otherCrossing = new OSMWay(); + otherCrossing.getNodeRefs().add(new long[] { 10003, 10001, 10004 }); + otherCrossing.addTag("highway", "footway"); + otherCrossing.addTag("footway", "crossing"); + otherCrossing.addTag("crossing", "unmarked"); + + // If more than one footway are adjacent to the crossing, there is no continuation. + OSMWay otherFootway = new OSMWay(); + otherFootway.addTag("highway", "footway"); + otherFootway.getNodeRefs().add(new long[] { 10002, 10006 }); + + assertTrue(OsmModule.isContinuationOfMarkedCrossing(footway, List.of(footway, crossing, otherCrossing))); + assertFalse(OsmModule.isContinuationOfMarkedCrossing(footway, List.of(footway, otherCrossing))); + assertFalse(OsmModule.isContinuationOfMarkedCrossing(footway, List.of(footway, crossing, otherFootway))); + } + private record VertexPair(Vertex v0, Vertex v1) {} } From 22618bd4a97707f6162a8f3c64258afd69c58b41 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 30 May 2024 11:12:30 -0400 Subject: [PATCH 076/111] fix(OsmWay): Adjust marked crossing logic and tests. --- .../java/org/opentripplanner/openstreetmap/model/OSMWay.java | 5 +++-- .../org/opentripplanner/openstreetmap/model/OSMWayTest.java | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java index 8c483e56f77..ad735855482 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java @@ -146,10 +146,11 @@ public boolean isFootway() { } public boolean isMarkedCrossing() { + String crossingMarkingsTag = getTag("crossing:markings"); return ( "crossing".equals(getTag("footway")) && - "yes".equals(getTag("crossing:markings")) || - "marked".equals(getTag("crossing")) + (crossingMarkingsTag != null && !"no".equals(crossingMarkingsTag) || + "marked".equals(getTag("crossing"))) ); } diff --git a/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java b/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java index 38ae396a321..ae98d43f8a5 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java +++ b/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java @@ -11,7 +11,7 @@ import org.junit.jupiter.params.provider.MethodSource; import org.opentripplanner.openstreetmap.wayproperty.specifier.WayTestData; -public class OSMWayTest { +class OSMWayTest { @Test void testIsBicycleDismountForced() { @@ -182,6 +182,8 @@ static Stream createCrossingCases() { Arguments.of(createFootway("crossing", "crossing", "marked"), true), Arguments.of(createFootway("crossing", "crossing", "other"), false), Arguments.of(createFootway("crossing", "crossing:markings", "yes"), true), + Arguments.of(createFootway("crossing", "crossing:markings", "marking-details"), true), + Arguments.of(createFootway("crossing", "crossing:markings", null), false), Arguments.of(createFootway("crossing", "crossing:markings", "no"), false) ); } From b9bcf36606e8d29cd4aae13130fea5958920e828 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 30 May 2024 11:36:14 -0400 Subject: [PATCH 077/111] perf(OsmModule): Cache last intersection for prev OSMWay. --- .../graph_builder/module/osm/OsmModule.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 252f926f952..8132024c78f 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -68,6 +68,9 @@ public class OsmModule implements GraphBuilderModule { private Map mobilityProfileData; private HashSet mappedMobilityProfileEntries; private List osmStreets; + private List osmFootways; + private OSMWay lastQueriedCrossing; + private OSMWay lastIntersectingStreetFound; private final StreetLimitationParameters streetLimitationParameters; OsmModule( @@ -724,7 +727,16 @@ private Optional getIntersectingStreet(OSMWay way) { if (osmStreets == null) { osmStreets = getStreets(osmdb.getWays()); } - return getIntersectingStreet(way, osmStreets); + + // Perf: If the same way is queried again, return the previously found intersecting street. + if (way == lastQueriedCrossing) { + return Optional.ofNullable(lastIntersectingStreetFound); + } + + lastQueriedCrossing = way; + Optional intersectingStreetOptional = getIntersectingStreet(way, osmStreets); + lastIntersectingStreetFound = intersectingStreetOptional.orElse(null); + return intersectingStreetOptional; } /** Gets the intersecting street, if any, for the given way and candidate streets. */ From 23e79fbbf0ecd0c440f04a9786a28a628a4d280c Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:53:56 -0400 Subject: [PATCH 078/111] refactor(OsmModule): Extract methods for crossing name and nearest crossing. --- .../graph_builder/module/osm/OsmModule.java | 75 ++++++++++++++----- .../module/osm/OsmModuleTest.java | 7 +- 2 files changed, 61 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 8132024c78f..03ffdac74f5 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -556,6 +556,25 @@ private StreetEdgePair getEdgesForStreet( return new StreetEdgePair(street, backStreet); } + private String getCrossingName(OSMWay way, String defaultName) { + // Scan the nodes of this way to find the intersecting street. + var otherWayOpt = getIntersectingStreet(way); + if (otherWayOpt.isPresent()) { + OSMWay otherWay = otherWayOpt.get(); + if (otherWay.hasTag("name")) { + return String.format("crossing over %s", otherWay.getTag("name")); + } else if (otherWay.isServiceRoad()) { + return "crossing over service road"; + } else if (otherWay.isOneWayForwardDriving()) { + return "crossing over turn lane"; + } else { + // Default on using the OSM way ID, which should not happen. + return String.format("crossing %s", way.getId()); + } + } + return defaultName; + } + private StreetEdge getEdgeForStreet( IntersectionVertex startEndpoint, IntersectionVertex endEndpoint, @@ -597,26 +616,19 @@ private StreetEdge getEdgeForStreet( // TODO: i18n. String editedName = name.toString(); if (way.isMarkedCrossing()) { - // Scan the nodes of this way to find the intersecting street. - var otherWayOpt = getIntersectingStreet(way); - if (otherWayOpt.isPresent()) { - OSMWay otherWay = otherWayOpt.get(); - if (otherWay.hasTag("name")) { - editedName = String.format("crossing over %s", otherWay.getTag("name")); - } else if (otherWay.isServiceRoad()) { - editedName = "crossing over service road"; - } else if (otherWay.isOneWayForwardDriving()) { - editedName = "crossing over turn lane"; - } else { - // Default on using the OSM way ID, which should not happen. - editedName = String.format("crossing %s", wayId); - } - } - + editedName = getCrossingName(way, editedName); seb.withName(editedName); seb.withBogusName(false); - } else if ("sidewalk".equals(editedName) || "path".equals(editedName)) { - editedName = String.format("%s %s", editedName, wayId); + } else { + OSMWay continuedCrossing = getContinuedMarkedCrossing(way); + if (continuedCrossing != null) { + // Change the name of this segment to the name of the crossing. + editedName = getCrossingName(continuedCrossing, editedName); + seb.withName(editedName); + seb.withBogusName(false); + } else if ("sidewalk".equals(editedName) || "path".equals(editedName)) { + editedName = String.format("%s %s", editedName, wayId); + } } // Lookup costs by mobility profile, if any were defined. @@ -755,6 +767,33 @@ public static Optional getIntersectingStreet(OSMWay way, List st return Optional.empty(); } + /** Gets the footways from a collection of OSM ways. */ + public static List getFootways(Collection ways) { + return ways + .stream() + .filter(OSMWay::isFootway) + .toList(); + } + + /** + * Determines whether a way is a continuation (connects through end nodes) of a marked crossing, + * using the footways from osmdb. + */ + private OSMWay getContinuedMarkedCrossing(OSMWay way) { + // Perf: If the same way is queried again, return the previously found intersecting street. + if (way == lastQueriedCrossingExtension) { + return lastAdjacentCrossingFound; + } + + if (osmFootways == null) { + osmFootways = getFootways(osmdb.getWays()); + } + + lastQueriedCrossingExtension = way; + lastAdjacentCrossingFound = getContinuedMarkedCrossing(way, osmFootways); + return lastAdjacentCrossingFound; + } + /** Determines whether a way is a continuation (i.e. connects through end nodes) of marked crossing. */ public static boolean isContinuationOfMarkedCrossing(OSMWay way, Collection ways) { int adjacentWayCount = 0; diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java index 6b852aa6ac0..24ebb0f479f 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.opentripplanner.openstreetmap.wayproperty.WayPropertiesBuilder.withModes; @@ -442,9 +443,9 @@ void testIsContinuationOfMarkedCrossing() { otherFootway.addTag("highway", "footway"); otherFootway.getNodeRefs().add(new long[] { 10002, 10006 }); - assertTrue(OsmModule.isContinuationOfMarkedCrossing(footway, List.of(footway, crossing, otherCrossing))); - assertFalse(OsmModule.isContinuationOfMarkedCrossing(footway, List.of(footway, otherCrossing))); - assertFalse(OsmModule.isContinuationOfMarkedCrossing(footway, List.of(footway, crossing, otherFootway))); + assertEquals(crossing, OsmModule.getContinuedMarkedCrossing(footway, List.of(footway, crossing, otherCrossing))); + assertNull(OsmModule.getContinuedMarkedCrossing(footway, List.of(footway, otherCrossing))); + assertNull(OsmModule.getContinuedMarkedCrossing(footway, List.of(footway, crossing, otherFootway))); } private record VertexPair(Vertex v0, Vertex v1) {} From 618fcb49b901b5fe3724099da030cfeed87737d2 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 3 Jun 2024 11:03:51 -0400 Subject: [PATCH 079/111] refactor(OsmModule): Add other missing pieces. --- .../graph_builder/module/osm/OsmModule.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 03ffdac74f5..e4758c5d2dd 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -71,6 +71,8 @@ public class OsmModule implements GraphBuilderModule { private List osmFootways; private OSMWay lastQueriedCrossing; private OSMWay lastIntersectingStreetFound; + private OSMWay lastQueriedCrossingExtension; + private OSMWay lastAdjacentCrossingFound; private final StreetLimitationParameters streetLimitationParameters; OsmModule( @@ -736,15 +738,15 @@ public static List getStreets(Collection ways) { /** Gets the intersecting street, if any, for the given way using ways in osmdb. */ private Optional getIntersectingStreet(OSMWay way) { - if (osmStreets == null) { - osmStreets = getStreets(osmdb.getWays()); - } - // Perf: If the same way is queried again, return the previously found intersecting street. if (way == lastQueriedCrossing) { return Optional.ofNullable(lastIntersectingStreetFound); } + if (osmStreets == null) { + osmStreets = getStreets(osmdb.getWays()); + } + lastQueriedCrossing = way; Optional intersectingStreetOptional = getIntersectingStreet(way, osmStreets); lastIntersectingStreetFound = intersectingStreetOptional.orElse(null); @@ -795,19 +797,19 @@ private OSMWay getContinuedMarkedCrossing(OSMWay way) { } /** Determines whether a way is a continuation (i.e. connects through end nodes) of marked crossing. */ - public static boolean isContinuationOfMarkedCrossing(OSMWay way, Collection ways) { + public static OSMWay getContinuedMarkedCrossing(OSMWay way, Collection ways) { int adjacentWayCount = 0; - boolean markedCrossingFound = false; + OSMWay markedCrossing = null; for (OSMWay w : ways) { if (way.isAdjacentTo(w)) { adjacentWayCount++; - if (!markedCrossingFound && w.isMarkedCrossing()) { - markedCrossingFound = true; + if (markedCrossing == null && w.isMarkedCrossing()) { + markedCrossing = w; } } } - return markedCrossingFound && adjacentWayCount == 1; + return adjacentWayCount == 1 ? markedCrossing : null; } private float getMaxCarSpeed() { From 49ba592485b374f7eb80717758c899ce07fe2473 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 3 Jun 2024 15:59:20 -0400 Subject: [PATCH 080/111] fix(StatesToWalkStepsMapper): Keep instr when xing and cont on other side of same street --- .../model/plan/WalkStepBuilder.java | 6 ++-- .../mapping/StatesToWalkStepsMapper.java | 16 +++++++-- .../mapping/StatesToWalkStepsMapperTest.java | 33 +++++++++++++++++++ 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java b/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java index 25c6ee25b6b..4f27214ec28 100644 --- a/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java +++ b/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java @@ -120,10 +120,10 @@ public WalkStepBuilder addEdge(Edge edge) { @Nullable public String directionTextNoParens() { - var str = directionText.toString(); - if (str == null) { - return null; //Avoid null reference exceptions with pathways which don't have names + if (directionText == null) { + return null; // Avoid null reference exceptions with pathways which don't have names } + var str = directionText.toString(); int idx = str.indexOf('('); if (idx > 0) { return str.substring(0, idx - 1); diff --git a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java index 7e8fdcc57d7..fa27986e307 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java @@ -288,8 +288,7 @@ private void processState(State backState, State forwardState) { WalkStepBuilder twoBack = steps.get(lastIndex - 1); WalkStepBuilder lastStep = steps.get(lastIndex); boolean isOnSameStreet = - !lastStep.directionTextNoParens().startsWith("crossing") && - lastStep.directionTextNoParens().equals(threeBack.directionTextNoParens()); + isOnSameStreet(lastStep, twoBack, threeBack); if (twoBack.distance() < MAX_ZAG_DISTANCE && isOnSameStreet) { if (isUTurn(twoBack, lastStep)) { steps.remove(lastIndex - 1); @@ -318,6 +317,19 @@ private void processState(State backState, State forwardState) { current.addEdge(edge); } + public static boolean isOnSameStreet(WalkStepBuilder lastStep, WalkStepBuilder twoBack, WalkStepBuilder threeBack) { + String lastStepName = lastStep.directionTextNoParens(); + String twoBackStepName = twoBack.directionTextNoParens(); + String threeBackStepName = threeBack.directionTextNoParens(); + if (lastStepName == null || twoBackStepName == null || threeBackStepName == null) return false; + + // Keep an explicit instruction when crossing to the other side of the same street. + // (An instruction can be given to cross at a particular location because others may not be accessible, practical, etc.) + return !lastStepName.startsWith("crossing") && + !twoBackStepName.startsWith("crossing") && + lastStepName.equals(threeBackStepName); + } + @Nonnull private static RelativeDirection relativeDirectionForTransitLink(StreetTransitEntranceLink link) { if (link.isExit()) { diff --git a/src/test/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapperTest.java b/src/test/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapperTest.java index 6e41f6ddf2b..75c3929ca5e 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapperTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapperTest.java @@ -5,12 +5,20 @@ import static org.opentripplanner.model.plan.RelativeDirection.ENTER_STATION; import static org.opentripplanner.model.plan.RelativeDirection.EXIT_STATION; import static org.opentripplanner.model.plan.RelativeDirection.FOLLOW_SIGNS; +import static org.opentripplanner.routing.algorithm.mapping.StatesToWalkStepsMapper.isOnSameStreet; +import com.beust.jcommander.internal.Lists; import java.util.List; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.opentripplanner.astar.model.GraphPath; +import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.model.plan.RelativeDirection; import org.opentripplanner.model.plan.WalkStep; +import org.opentripplanner.model.plan.WalkStepBuilder; import org.opentripplanner.routing.services.notes.StreetNotesService; import org.opentripplanner.street.search.state.TestStateBuilder; @@ -74,4 +82,29 @@ private static List buildWalkSteps(TestStateBuilder builder) { var mapper = new StatesToWalkStepsMapper(path.states, null, new StreetNotesService(), 0); return mapper.generateWalkSteps(); } + + @ParameterizedTest + @MethodSource("createIsOnSameStreetCases") + void testIsOnSameStreet(List streets, boolean expected, String message) { + List steps = streets.stream().map( + s -> s != null ? WalkStep.builder().withDirectionText(I18NString.of(s)) : WalkStep.builder() + ).toList(); + + int lastIndex = steps.size() - 1; + WalkStepBuilder threeBack = steps.get(lastIndex - 2); + WalkStepBuilder twoBack = steps.get(lastIndex - 1); + WalkStepBuilder lastStep = steps.get(lastIndex); + + assertEquals(expected, isOnSameStreet(lastStep, twoBack, threeBack), message); + } + + static Stream createIsOnSameStreetCases() { + return Stream.of( + Arguments.of(List.of("Street1", "Street2", "Street3"), false, "Is not a zig-zag"), + Arguments.of(List.of("Street1", "Street2", "Street1"), true, "Is a zig-zag"), + Arguments.of(List.of("Street1", "crossing over Street2", "Street1"), false, "Is a crossing"), + Arguments.of(List.of("crossing over turn lane", "Street1", "crossing over turn lane"), false, "Is many crossings"), + Arguments.of(Lists.newArrayList(null, null, null), false, "Is not a zig-zag") + ); + } } From 9be135b705dfeaae33d099d5cbb60e7d6ee83295 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 3 Jun 2024 17:02:08 -0400 Subject: [PATCH 081/111] style: Apply prettier --- .../graph_builder/module/osm/OsmModule.java | 5 +---- .../openstreetmap/model/OSMWay.java | 21 +++++++++++++------ .../mapping/StatesToWalkStepsMapper.java | 15 ++++++++----- .../module/osm/OsmModuleTest.java | 13 +++++++++--- .../mapping/StatesToWalkStepsMapperTest.java | 15 +++++++++---- 5 files changed, 47 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index e4758c5d2dd..151d1de744b 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -771,10 +771,7 @@ public static Optional getIntersectingStreet(OSMWay way, List st /** Gets the footways from a collection of OSM ways. */ public static List getFootways(Collection ways) { - return ways - .stream() - .filter(OSMWay::isFootway) - .toList(); + return ways.stream().filter(OSMWay::isFootway).toList(); } /** diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java index ad735855482..ed6b1173cd5 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java @@ -149,8 +149,11 @@ public boolean isMarkedCrossing() { String crossingMarkingsTag = getTag("crossing:markings"); return ( "crossing".equals(getTag("footway")) && - (crossingMarkingsTag != null && !"no".equals(crossingMarkingsTag) || - "marked".equals(getTag("crossing"))) + ( + crossingMarkingsTag != null && + !"no".equals(crossingMarkingsTag) || + "marked".equals(getTag("crossing")) + ) ); } @@ -166,10 +169,16 @@ public boolean isAdjacentTo(OSMWay way) { long firstNode = nodes.get(0); long lastNode = nodes.get(nodes.size() - 1); - return firstNode == wayFirstNode && lastNode != wayLastNode || - firstNode == wayLastNode && lastNode != wayFirstNode || - lastNode == wayFirstNode && firstNode != wayLastNode || - lastNode == wayLastNode && firstNode != wayFirstNode; + return ( + firstNode == wayFirstNode && + lastNode != wayLastNode || + firstNode == wayLastNode && + lastNode != wayFirstNode || + lastNode == wayFirstNode && + firstNode != wayLastNode || + lastNode == wayLastNode && + firstNode != wayFirstNode + ); } /** diff --git a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java index fa27986e307..6a1f5b5c3c6 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java @@ -287,8 +287,7 @@ private void processState(State backState, State forwardState) { WalkStepBuilder threeBack = steps.get(lastIndex - 2); WalkStepBuilder twoBack = steps.get(lastIndex - 1); WalkStepBuilder lastStep = steps.get(lastIndex); - boolean isOnSameStreet = - isOnSameStreet(lastStep, twoBack, threeBack); + boolean isOnSameStreet = isOnSameStreet(lastStep, twoBack, threeBack); if (twoBack.distance() < MAX_ZAG_DISTANCE && isOnSameStreet) { if (isUTurn(twoBack, lastStep)) { steps.remove(lastIndex - 1); @@ -317,7 +316,11 @@ private void processState(State backState, State forwardState) { current.addEdge(edge); } - public static boolean isOnSameStreet(WalkStepBuilder lastStep, WalkStepBuilder twoBack, WalkStepBuilder threeBack) { + public static boolean isOnSameStreet( + WalkStepBuilder lastStep, + WalkStepBuilder twoBack, + WalkStepBuilder threeBack + ) { String lastStepName = lastStep.directionTextNoParens(); String twoBackStepName = twoBack.directionTextNoParens(); String threeBackStepName = threeBack.directionTextNoParens(); @@ -325,9 +328,11 @@ public static boolean isOnSameStreet(WalkStepBuilder lastStep, WalkStepBuilder t // Keep an explicit instruction when crossing to the other side of the same street. // (An instruction can be given to cross at a particular location because others may not be accessible, practical, etc.) - return !lastStepName.startsWith("crossing") && + return ( + !lastStepName.startsWith("crossing") && !twoBackStepName.startsWith("crossing") && - lastStepName.equals(threeBackStepName); + lastStepName.equals(threeBackStepName) + ); } @Nonnull diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java index 24ebb0f479f..6c22143417c 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java @@ -415,7 +415,9 @@ void testGetStreets() { otherStreet.addTag("oneway", "true"); OSMWay blankPath = new OSMWay(); - List streets = OsmModule.getStreets(List.of(street, footway, serviceRoad, otherStreet, blankPath)); + List streets = OsmModule.getStreets( + List.of(street, footway, serviceRoad, otherStreet, blankPath) + ); assertEquals(3, streets.size()); assertTrue(streets.containsAll(List.of(serviceRoad, street, otherStreet))); } @@ -443,9 +445,14 @@ void testIsContinuationOfMarkedCrossing() { otherFootway.addTag("highway", "footway"); otherFootway.getNodeRefs().add(new long[] { 10002, 10006 }); - assertEquals(crossing, OsmModule.getContinuedMarkedCrossing(footway, List.of(footway, crossing, otherCrossing))); + assertEquals( + crossing, + OsmModule.getContinuedMarkedCrossing(footway, List.of(footway, crossing, otherCrossing)) + ); assertNull(OsmModule.getContinuedMarkedCrossing(footway, List.of(footway, otherCrossing))); - assertNull(OsmModule.getContinuedMarkedCrossing(footway, List.of(footway, crossing, otherFootway))); + assertNull( + OsmModule.getContinuedMarkedCrossing(footway, List.of(footway, crossing, otherFootway)) + ); } private record VertexPair(Vertex v0, Vertex v1) {} diff --git a/src/test/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapperTest.java b/src/test/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapperTest.java index 75c3929ca5e..3f95a6f0bf1 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapperTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapperTest.java @@ -86,9 +86,12 @@ private static List buildWalkSteps(TestStateBuilder builder) { @ParameterizedTest @MethodSource("createIsOnSameStreetCases") void testIsOnSameStreet(List streets, boolean expected, String message) { - List steps = streets.stream().map( - s -> s != null ? WalkStep.builder().withDirectionText(I18NString.of(s)) : WalkStep.builder() - ).toList(); + List steps = streets + .stream() + .map(s -> + s != null ? WalkStep.builder().withDirectionText(I18NString.of(s)) : WalkStep.builder() + ) + .toList(); int lastIndex = steps.size() - 1; WalkStepBuilder threeBack = steps.get(lastIndex - 2); @@ -103,7 +106,11 @@ static Stream createIsOnSameStreetCases() { Arguments.of(List.of("Street1", "Street2", "Street3"), false, "Is not a zig-zag"), Arguments.of(List.of("Street1", "Street2", "Street1"), true, "Is a zig-zag"), Arguments.of(List.of("Street1", "crossing over Street2", "Street1"), false, "Is a crossing"), - Arguments.of(List.of("crossing over turn lane", "Street1", "crossing over turn lane"), false, "Is many crossings"), + Arguments.of( + List.of("crossing over turn lane", "Street1", "crossing over turn lane"), + false, + "Is many crossings" + ), Arguments.of(Lists.newArrayList(null, null, null), false, "Is not a zig-zag") ); } From faaa7cade4072cea0df434af3f2c3f7ecae8dd15 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 17 Jul 2024 14:11:17 -0400 Subject: [PATCH 082/111] fix(StreetEdge): Check non-empty profileCode map. --- .../java/org/opentripplanner/street/model/edge/StreetEdge.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index 89e90c487ef..37d959e529b 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -1334,7 +1334,7 @@ private TraversalCosts walkingTraversalCosts( // (assuming a pre-determined travel speed for each profile) // and the travel speed for that profile is used to overwrite the time calculated above. if (mobilityProfile != null) { - if (profileCost != null) { + if (profileCost != null && !profileCost.isEmpty()) { var defaultTravelHours = MobilityProfileRouting.computeTravelHours( getEffectiveWalkDistance(), mobilityProfile From a4143a9e109049dbd5a6d367d8493fa015c5b40c Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 31 Jul 2024 15:21:56 -0400 Subject: [PATCH 083/111] fix(MobilityProfileRouting): Allow pedestrian routing on transit platforms. --- .../MobilityProfileRouting.java | 2 +- .../openstreetmap/model/OSMWay.java | 4 ++++ .../openstreetmap/model/OSMWayTest.java | 21 +++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java index 5dd17869907..df5375321f8 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java @@ -57,7 +57,7 @@ public static StreetTraversalPermission adjustPedestrianPermissions( OSMWay way, StreetTraversalPermission permissions ) { - return way.isFootway() ? permissions : permissions.remove(StreetTraversalPermission.PEDESTRIAN); + return way.isFootway() || way.isTransitPlatform() ? permissions : permissions.remove(StreetTraversalPermission.PEDESTRIAN); } /** Multiplies profile costs by the distance ratio between the given edge and its parent. */ diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java index ed6b1173cd5..f14bd20f1de 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java @@ -224,4 +224,8 @@ public StreetTraversalPermissionPair splitPermissions(StreetTraversalPermission public String url() { return String.format("https://www.openstreetmap.org/way/%d", getId()); } + + public boolean isTransitPlatform() { + return "platform".equals(getTag("railway")) || "platform".equals(getTag("public_transport")); + } } diff --git a/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java b/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java index ae98d43f8a5..e09bb6b1620 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java +++ b/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java @@ -187,4 +187,25 @@ static Stream createCrossingCases() { Arguments.of(createFootway("crossing", "crossing:markings", "no"), false) ); } + + private static OSMWay createPlatform(String kind) { + var osm = new OSMWay(); + osm.addTag(kind, "platform"); + return osm; + } + + @ParameterizedTest + @MethodSource("createTransitPlatformCases") + void transitPlatform(OSMWay way, boolean result) { + assertEquals(result, way.isTransitPlatform()); + } + + static Stream createTransitPlatformCases() { + return Stream.of( + Arguments.of(createGenericHighway(), false), + Arguments.of(createGenericFootway(), false), + Arguments.of(createPlatform("railway"), true), + Arguments.of(createPlatform("public_transport"), true) + ); + } } From f78e1922b0c6609ade26f7f3e05a76b3b8da7154 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 31 Jul 2024 15:52:43 -0400 Subject: [PATCH 084/111] style(MobilityProfileRouting): Apply prettier. --- .../ext/mobilityprofile/MobilityProfileRouting.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java index df5375321f8..3e34ff4205d 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java @@ -57,7 +57,9 @@ public static StreetTraversalPermission adjustPedestrianPermissions( OSMWay way, StreetTraversalPermission permissions ) { - return way.isFootway() || way.isTransitPlatform() ? permissions : permissions.remove(StreetTraversalPermission.PEDESTRIAN); + return way.isFootway() || way.isTransitPlatform() + ? permissions + : permissions.remove(StreetTraversalPermission.PEDESTRIAN); } /** Multiplies profile costs by the distance ratio between the given edge and its parent. */ From f77c33fa059fb1584f9ec9c78cf162a5c29f1352 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 6 Aug 2024 11:25:20 -0400 Subject: [PATCH 085/111] feat(OsmImpedanceUpdater): Add basic diff logic between two impedance sets. --- .../impedance/ImpedanceUpdateHandler.java | 14 ++ .../impedance/OsmImpedanceUpdater.java | 120 ++++++++++++++++++ .../OsmImpedanceUpdaterParameters.java | 13 ++ .../impedance/OsmImpedanceUpdaterTest.java | 62 +++++++++ 4 files changed, 209 insertions(+) create mode 100644 src/main/java/org/opentripplanner/updater/impedance/ImpedanceUpdateHandler.java create mode 100644 src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java create mode 100644 src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdaterParameters.java create mode 100644 src/test/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdaterTest.java diff --git a/src/main/java/org/opentripplanner/updater/impedance/ImpedanceUpdateHandler.java b/src/main/java/org/opentripplanner/updater/impedance/ImpedanceUpdateHandler.java new file mode 100644 index 00000000000..7e8fbe4e836 --- /dev/null +++ b/src/main/java/org/opentripplanner/updater/impedance/ImpedanceUpdateHandler.java @@ -0,0 +1,14 @@ +package org.opentripplanner.updater.impedance; + +import java.util.Map; +import org.opentripplanner.ext.mobilityprofile.MobilityProfileData; +import org.opentripplanner.routing.graph.Graph; + +/** + * Processes updated impedances to the graph. + */ +public class ImpedanceUpdateHandler { + public void update(Graph graph, Map impedances) { + + } +} diff --git a/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java b/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java new file mode 100644 index 00000000000..25b5d5ef1ef --- /dev/null +++ b/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java @@ -0,0 +1,120 @@ +package org.opentripplanner.updater.impedance; + +import java.net.URI; +import java.util.HashMap; +import java.util.Map; +import javax.annotation.Nonnull; +import org.opentripplanner.ext.mobilityprofile.MobilityProfileData; +import org.opentripplanner.ext.mobilityprofile.MobilityProfileParser; +import org.opentripplanner.framework.io.OtpHttpClient; +import org.opentripplanner.framework.io.OtpHttpClientFactory; +import org.opentripplanner.framework.tostring.ToStringBuilder; +import org.opentripplanner.updater.spi.HttpHeaders; +import org.opentripplanner.updater.spi.PollingGraphUpdater; +import org.opentripplanner.updater.spi.WriteToGraphCallback; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * G-MAP OSM impedance updater + */ +public class OsmImpedanceUpdater extends PollingGraphUpdater { + + private static final Logger LOG = LoggerFactory.getLogger(OsmImpedanceUpdater.class); + + private final String url; + private final ImpedanceUpdateHandler updateHandler; + private final HttpHeaders headers; + private final OtpHttpClient otpHttpClient; + private WriteToGraphCallback saveResultOnGraph; + private Map previousImpedances; + + public OsmImpedanceUpdater( + OsmImpedanceUpdaterParameters config + ) { + super(config); + this.url = config.url(); + this.headers = HttpHeaders.of().add(config.headers()).build(); + + this.updateHandler = new ImpedanceUpdateHandler(); + this.otpHttpClient = new OtpHttpClientFactory().create(LOG); + LOG.info("Creating real-time alert updater running every {}: {}", pollingPeriod(), url); + } + + @Override + public void setGraphUpdaterManager(WriteToGraphCallback saveResultOnGraph) { + this.saveResultOnGraph = saveResultOnGraph; + } + + @Override + public String toString() { + return ToStringBuilder.of(this.getClass()).addStr("url", url).toString(); + } + + @Override + protected void runPolling() { + try { + final Map impedances = otpHttpClient.getAndMap( + URI.create(url), + this.headers.asMap(), + MobilityProfileParser::parseData + ); + + // Filter out which rows have been updated since previous poll. + Map changedImpedances = getChangedImpedances(impedances, previousImpedances); + previousImpedances = impedances; + + // Handle update in graph writer runnable + saveResultOnGraph.execute((graph, transitModel) -> updateHandler.update(graph, changedImpedances)); + } catch (Exception e) { + LOG.error("Error parsing impedance data from {}", url, e); + } + } + + /** + * Indicates whether two profile data have the same impedances. + */ + public static boolean areSameImpedances( + @Nonnull MobilityProfileData profileData1, + @Nonnull MobilityProfileData profileData2 + ) { + return profileData1.costs().equals(profileData2.costs()); + } + + /** + * Performs a diff with existing entries, to avoid updating unchanged portions of the graph. + */ + public static Map getChangedImpedances( + @Nonnull Map newImpedances, + @Nonnull Map existingImpedances + ) { + Map result = new HashMap<>(); + + for (var entry : newImpedances.entrySet()) { + String key = entry.getKey(); + + // Include entries that exist in both sets and that were modified in newImpedances. + // Include entries introduced in newImpedances not in existingImpedances. + if (!existingImpedances.containsKey(key) || !areSameImpedances(entry.getValue(), existingImpedances.get(key))) { + result.put(key, entry.getValue()); + } + } + + for (var entry : existingImpedances.entrySet()) { + String key = entry.getKey(); + + // Include entries that were removed in newImpedances, but mark them as empty map. + if (!newImpedances.containsKey(key)) { + MobilityProfileData removedData = entry.getValue(); + result.put(key, new MobilityProfileData( + removedData.lengthInMeters(), + removedData.fromNode(), + removedData.toNode(), + Map.of()) + ); + } + } + + return result; + } +} diff --git a/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdaterParameters.java b/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdaterParameters.java new file mode 100644 index 00000000000..e5efb4b9910 --- /dev/null +++ b/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdaterParameters.java @@ -0,0 +1,13 @@ +package org.opentripplanner.updater.impedance; + +import java.time.Duration; +import org.opentripplanner.updater.spi.HttpHeaders; +import org.opentripplanner.updater.spi.PollingGraphUpdaterParameters; + +public record OsmImpedanceUpdaterParameters( + String configRef, + String url, + Duration frequency, + HttpHeaders headers +) + implements PollingGraphUpdaterParameters {} diff --git a/src/test/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdaterTest.java b/src/test/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdaterTest.java new file mode 100644 index 00000000000..8ebf575b915 --- /dev/null +++ b/src/test/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdaterTest.java @@ -0,0 +1,62 @@ +package org.opentripplanner.updater.impedance; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.opentripplanner.ext.mobilityprofile.MobilityProfile; +import org.opentripplanner.ext.mobilityprofile.MobilityProfileData; + +class OsmImpedanceUpdaterTest { + private MobilityProfileData profileData(float impNone, float impWChairE, float impBlind) { + return new MobilityProfileData(10, 123456, 654321, Map.of( + MobilityProfile.NONE, impNone, + MobilityProfile.WCHAIRE, impWChairE, + MobilityProfile.BLIND, impBlind + )); + } + + @Test + void areSameImpedances() { + MobilityProfileData profileData1 = profileData(1.2F, 5.0F, 3.4F); + MobilityProfileData profileData2 = profileData(1.6F, 5.0F, 3.4F); + MobilityProfileData profileData3 = profileData(1.2F, 5.0F, 3.4F); + + assertTrue(OsmImpedanceUpdater.areSameImpedances(profileData1, profileData1)); + assertFalse(OsmImpedanceUpdater.areSameImpedances(profileData1, profileData2)); + assertTrue(OsmImpedanceUpdater.areSameImpedances(profileData1, profileData3)); + } + + /** + * Contains a simple case for filtering impedances. + * Impedance data are simplified to three profiles: "None", "WChairE", and "Blind". + * OSM attributes such as way ids and corresponding geometry are assumed identical. + */ + @Test + void getChangedImpedances() { + Map newImpedances = Map.of( + "Street1", profileData(1.2F, 5.0F, 3.4F), + "Street2", profileData(2.1F, 7.0F, 4.4F), + "Street3", profileData(0.6F, 1.1F, 1.5F), + "Street4", profileData(1.4F, 1.6F, 1.8F), + "Street6", profileData(3.1F, 2.6F, 4.0F) + ); + Map oldImpedances = Map.of( + "Street1", profileData(1.2F, 5.0F, 3.4F), + "Street2", profileData(2.1F, 3.0F, 4.4F), + "Street3", profileData(0.6F, 1.1F, 1.5F), + "Street4", profileData(1.4F, 1.6F, 1.8F), + "Street5", profileData(2.3F, 2.5F, 3.0F) + ); + + Map changedImpedances = Map.of( + "Street6", profileData(3.1F, 2.6F, 4.0F), + "Street5", new MobilityProfileData(10, 123456, 654321, Map.of()), + "Street2", profileData(2.1F, 7.0F, 4.4F) + ); + + assertEquals(changedImpedances, OsmImpedanceUpdater.getChangedImpedances(newImpedances, oldImpedances)); + } +} From aeb0362ac1f7b6aafca1e588215615c48ba0232e Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 6 Aug 2024 14:00:33 -0400 Subject: [PATCH 086/111] feat(OsmImpedanceUpdater): Implement interval polling and diffing of impdedance data. --- .../config/routerconfig/UpdatersConfig.java | 9 ++++++++ .../updaters/OsmImpedanceUpdaterConfig.java | 22 +++++++++++++++++++ .../updater/UpdatersParameters.java | 3 +++ .../configure/UpdaterConfigurator.java | 5 +++++ .../impedance/OsmImpedanceUpdater.java | 2 +- 5 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/OsmImpedanceUpdaterConfig.java diff --git a/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java b/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java index 08371660b8a..0d3e6cba971 100644 --- a/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java @@ -3,6 +3,7 @@ import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V1_5; import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_2; import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.BIKE_RENTAL; +import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.OSM_IMPEDANCE_UPDATER; import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.MQTT_GTFS_RT_UPDATER; import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.REAL_TIME_ALERTS; import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.SIRI_AZURE_ET_UPDATER; @@ -31,6 +32,7 @@ import org.opentripplanner.standalone.config.framework.json.NodeAdapter; import org.opentripplanner.standalone.config.routerconfig.updaters.GtfsRealtimeAlertsUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.MqttGtfsRealtimeUpdaterConfig; +import org.opentripplanner.standalone.config.routerconfig.updaters.OsmImpedanceUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.PollingTripUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.SiriETGooglePubsubUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.SiriETUpdaterConfig; @@ -44,6 +46,7 @@ import org.opentripplanner.updater.TimetableSnapshotSourceParameters; import org.opentripplanner.updater.UpdatersParameters; import org.opentripplanner.updater.alert.GtfsRealtimeAlertsUpdaterParameters; +import org.opentripplanner.updater.impedance.OsmImpedanceUpdaterParameters; import org.opentripplanner.updater.trip.MqttGtfsRealtimeUpdaterParameters; import org.opentripplanner.updater.trip.PollingTripUpdaterParameters; import org.opentripplanner.updater.vehicle_parking.VehicleParkingUpdaterParameters; @@ -202,6 +205,11 @@ public List getSiriAzureSXUpdaterParameters() { return getParameters(SIRI_AZURE_SX_UPDATER); } + @Override + public List getOsmImpedanceUpdaterParameters() { + return getParameters(OSM_IMPEDANCE_UPDATER); + } + private List getParameters(Type key) { return (List) configList.get(key); } @@ -217,6 +225,7 @@ public enum Type { MQTT_GTFS_RT_UPDATER(MqttGtfsRealtimeUpdaterConfig::create), REAL_TIME_ALERTS(GtfsRealtimeAlertsUpdaterConfig::create), VEHICLE_POSITIONS(VehiclePositionsUpdaterConfig::create), + OSM_IMPEDANCE_UPDATER(OsmImpedanceUpdaterConfig::create), SIRI_ET_UPDATER(SiriETUpdaterConfig::create), SIRI_ET_GOOGLE_PUBSUB_UPDATER(SiriETGooglePubsubUpdaterConfig::create), SIRI_SX_UPDATER(SiriSXUpdaterConfig::create), diff --git a/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/OsmImpedanceUpdaterConfig.java b/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/OsmImpedanceUpdaterConfig.java new file mode 100644 index 00000000000..2b274c79371 --- /dev/null +++ b/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/OsmImpedanceUpdaterConfig.java @@ -0,0 +1,22 @@ +package org.opentripplanner.standalone.config.routerconfig.updaters; + +import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_5; + +import java.time.Duration; +import org.opentripplanner.standalone.config.framework.json.NodeAdapter; +import org.opentripplanner.updater.impedance.OsmImpedanceUpdaterParameters; + +public class OsmImpedanceUpdaterConfig { + public static OsmImpedanceUpdaterParameters create(String configRef, NodeAdapter c) { + return new OsmImpedanceUpdaterParameters( + configRef, + c.of("url").since(V2_5).summary("URL to fetch the GTFS-RT feed from.").asString(), + c + .of("frequency") + .since(V2_5) + .summary("How often the URL should be fetched.") + .asDuration(Duration.ofSeconds(30)), + HttpHeadersConfig.headers(c, V2_5) + ); + } +} diff --git a/src/main/java/org/opentripplanner/updater/UpdatersParameters.java b/src/main/java/org/opentripplanner/updater/UpdatersParameters.java index a955e757100..985d28faf03 100644 --- a/src/main/java/org/opentripplanner/updater/UpdatersParameters.java +++ b/src/main/java/org/opentripplanner/updater/UpdatersParameters.java @@ -8,6 +8,7 @@ import org.opentripplanner.ext.siri.updater.azure.SiriAzureSXUpdaterParameters; import org.opentripplanner.ext.vehiclerentalservicedirectory.api.VehicleRentalServiceDirectoryFetcherParameters; import org.opentripplanner.updater.alert.GtfsRealtimeAlertsUpdaterParameters; +import org.opentripplanner.updater.impedance.OsmImpedanceUpdaterParameters; import org.opentripplanner.updater.trip.MqttGtfsRealtimeUpdaterParameters; import org.opentripplanner.updater.trip.PollingTripUpdaterParameters; import org.opentripplanner.updater.vehicle_parking.VehicleParkingUpdaterParameters; @@ -40,4 +41,6 @@ public interface UpdatersParameters { List getSiriAzureETUpdaterParameters(); List getSiriAzureSXUpdaterParameters(); + + List getOsmImpedanceUpdaterParameters(); } diff --git a/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java b/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java index 3b6f7f52279..94ed4c86ea6 100644 --- a/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java +++ b/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java @@ -19,6 +19,7 @@ import org.opentripplanner.updater.GraphUpdaterManager; import org.opentripplanner.updater.UpdatersParameters; import org.opentripplanner.updater.alert.GtfsRealtimeAlertsUpdater; +import org.opentripplanner.updater.impedance.OsmImpedanceUpdater; import org.opentripplanner.updater.spi.GraphUpdater; import org.opentripplanner.updater.trip.MqttGtfsRealtimeUpdater; import org.opentripplanner.updater.trip.PollingTripUpdater; @@ -201,6 +202,10 @@ private List createUpdatersFromConfig() { updaters.add(new SiriAzureSXUpdater(configItem, transitModel)); } + for (var configItem : updatersParameters.getOsmImpedanceUpdaterParameters()) { + updaters.add(new OsmImpedanceUpdater(configItem)); + } + return updaters; } diff --git a/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java b/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java index 25b5d5ef1ef..a2817cb589c 100644 --- a/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java +++ b/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java @@ -27,7 +27,7 @@ public class OsmImpedanceUpdater extends PollingGraphUpdater { private final HttpHeaders headers; private final OtpHttpClient otpHttpClient; private WriteToGraphCallback saveResultOnGraph; - private Map previousImpedances; + private Map previousImpedances = Map.of(); public OsmImpedanceUpdater( OsmImpedanceUpdaterParameters config From 8f6343dcbe281083fa2a1ed867658417c741c4ae Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 7 Aug 2024 10:19:01 -0400 Subject: [PATCH 087/111] refactor(OsmImpedanceUpdater): Include from/to nodes in change comparison. --- .../impedance/OsmImpedanceUpdater.java | 5 ++- .../impedance/OsmImpedanceUpdaterTest.java | 38 ++++++++++--------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java b/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java index a2817cb589c..4ab106d6d90 100644 --- a/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java +++ b/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java @@ -78,7 +78,7 @@ public static boolean areSameImpedances( @Nonnull MobilityProfileData profileData1, @Nonnull MobilityProfileData profileData2 ) { - return profileData1.costs().equals(profileData2.costs()); + return profileData1.equals(profileData2); } /** @@ -95,7 +95,8 @@ public static Map getChangedImpedances( // Include entries that exist in both sets and that were modified in newImpedances. // Include entries introduced in newImpedances not in existingImpedances. - if (!existingImpedances.containsKey(key) || !areSameImpedances(entry.getValue(), existingImpedances.get(key))) { + var existingImpedance = existingImpedances.get(key); + if (existingImpedance == null || !areSameImpedances(entry.getValue(), existingImpedance)) { result.put(key, entry.getValue()); } } diff --git a/src/test/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdaterTest.java b/src/test/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdaterTest.java index 8ebf575b915..09f5376ee39 100644 --- a/src/test/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdaterTest.java +++ b/src/test/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdaterTest.java @@ -10,8 +10,8 @@ import org.opentripplanner.ext.mobilityprofile.MobilityProfileData; class OsmImpedanceUpdaterTest { - private MobilityProfileData profileData(float impNone, float impWChairE, float impBlind) { - return new MobilityProfileData(10, 123456, 654321, Map.of( + private MobilityProfileData profileData(long from, long to, float impNone, float impWChairE, float impBlind) { + return new MobilityProfileData(10, from, to, Map.of( MobilityProfile.NONE, impNone, MobilityProfile.WCHAIRE, impWChairE, MobilityProfile.BLIND, impBlind @@ -20,13 +20,15 @@ private MobilityProfileData profileData(float impNone, float impWChairE, float i @Test void areSameImpedances() { - MobilityProfileData profileData1 = profileData(1.2F, 5.0F, 3.4F); - MobilityProfileData profileData2 = profileData(1.6F, 5.0F, 3.4F); - MobilityProfileData profileData3 = profileData(1.2F, 5.0F, 3.4F); + MobilityProfileData profileData1 = profileData(1001, 1002,1.2F, 5.0F, 3.4F); + MobilityProfileData profileData2 = profileData(1002, 1003, 1.6F, 5.0F, 3.4F); + MobilityProfileData profileData3 = profileData(1001, 1002, 1.2F, 5.0F, 3.4F); + MobilityProfileData profileData4 = profileData(1003, 1004, 1.2F, 5.0F, 3.4F); assertTrue(OsmImpedanceUpdater.areSameImpedances(profileData1, profileData1)); assertFalse(OsmImpedanceUpdater.areSameImpedances(profileData1, profileData2)); assertTrue(OsmImpedanceUpdater.areSameImpedances(profileData1, profileData3)); + assertFalse(OsmImpedanceUpdater.areSameImpedances(profileData1, profileData4)); } /** @@ -37,24 +39,24 @@ void areSameImpedances() { @Test void getChangedImpedances() { Map newImpedances = Map.of( - "Street1", profileData(1.2F, 5.0F, 3.4F), - "Street2", profileData(2.1F, 7.0F, 4.4F), - "Street3", profileData(0.6F, 1.1F, 1.5F), - "Street4", profileData(1.4F, 1.6F, 1.8F), - "Street6", profileData(3.1F, 2.6F, 4.0F) + "Street1", profileData(1001, 1002, 1.2F, 5.0F, 3.4F), + "Street2", profileData(1002, 1003, 2.1F, 7.0F, 4.4F), + "Street3", profileData(1003, 1004, 0.6F, 1.1F, 1.5F), + "Street4", profileData(1004, 1005, 1.4F, 1.6F, 1.8F), + "Street6", profileData(1006, 1007, 1.2F, 5.0F, 3.4F) ); Map oldImpedances = Map.of( - "Street1", profileData(1.2F, 5.0F, 3.4F), - "Street2", profileData(2.1F, 3.0F, 4.4F), - "Street3", profileData(0.6F, 1.1F, 1.5F), - "Street4", profileData(1.4F, 1.6F, 1.8F), - "Street5", profileData(2.3F, 2.5F, 3.0F) + "Street1", profileData(1001, 1002, 1.2F, 5.0F, 3.4F), + "Street2", profileData(1002, 1003, 2.1F, 3.0F, 4.4F), + "Street3", profileData(1003, 1004, 0.6F, 1.1F, 1.5F), + "Street4", profileData(1004, 1005, 1.4F, 1.6F, 1.8F), + "Street5", profileData(1005, 1006, 2.3F, 2.5F, 3.0F) ); Map changedImpedances = Map.of( - "Street6", profileData(3.1F, 2.6F, 4.0F), - "Street5", new MobilityProfileData(10, 123456, 654321, Map.of()), - "Street2", profileData(2.1F, 7.0F, 4.4F) + "Street6", profileData(1006, 1007, 1.2F, 5.0F, 3.4F), + "Street5", new MobilityProfileData(10, 1005, 1006, Map.of()), + "Street2", profileData(1002, 1003, 2.1F, 7.0F, 4.4F) ); assertEquals(changedImpedances, OsmImpedanceUpdater.getChangedImpedances(newImpedances, oldImpedances)); From 85f20cb7d39cee72b5222670116e62ecc90b4c60 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 7 Aug 2024 15:05:00 -0400 Subject: [PATCH 088/111] refactor(GraphBuilder): Remove impedance data source. --- docs/BuildConfiguration.md | 1 - .../graph_builder/GraphBuilder.java | 18 +----------------- .../graph_builder/GraphBuilderDataSources.java | 13 ------------- .../standalone/config/BuildConfig.java | 10 ---------- 4 files changed, 1 insertion(+), 41 deletions(-) diff --git a/docs/BuildConfiguration.md b/docs/BuildConfiguration.md index 12c5c42152b..7d2d90ea41b 100644 --- a/docs/BuildConfiguration.md +++ b/docs/BuildConfiguration.md @@ -33,7 +33,6 @@ Sections follow that describe particular settings in more depth. | maxElevationPropagationMeters | `integer` | The maximum distance to propagate elevation to vertices which have no elevation. | *Optional* | `2000` | 1.5 | | [maxStopToShapeSnapDistance](#maxStopToShapeSnapDistance) | `double` | Maximum distance between route shapes and their stops. | *Optional* | `150.0` | 2.1 | | maxTransferDuration | `duration` | Transfers up to this duration with the default walk speed value will be pre-calculated and included in the Graph. | *Optional* | `"PT30M"` | 2.1 | -| mobilityProfileFile | `string` | Name of the CSV-formatted file in the build directory which contains the routing costs by mobility profile. | *Optional* | | 2.5 | | [multiThreadElevationCalculations](#multiThreadElevationCalculations) | `boolean` | Configuring multi-threading during elevation calculations. | *Optional* | `false` | 2.0 | | [osmCacheDataInMem](#osmCacheDataInMem) | `boolean` | If OSM data should be cached in memory during processing. | *Optional* | `false` | 2.0 | | [osmNaming](#osmNaming) | `enum` | A custom OSM namer to use. | *Optional* | `"default"` | 1.5 | diff --git a/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java b/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java index 7181bbbe9fe..c036c113e26 100644 --- a/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java +++ b/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java @@ -5,16 +5,12 @@ import static org.opentripplanner.datastore.api.FileType.OSM; import jakarta.inject.Inject; -import java.io.IOException; import java.time.Duration; import java.util.ArrayList; import java.util.List; -import java.util.Optional; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import org.opentripplanner.datastore.api.DataSource; import org.opentripplanner.ext.emissions.EmissionsDataModel; -import org.opentripplanner.ext.mobilityprofile.MobilityProfileParser; import org.opentripplanner.ext.stopconsolidation.StopConsolidationRepository; import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.framework.application.OtpAppException; @@ -24,7 +20,6 @@ import org.opentripplanner.graph_builder.issue.api.DataImportIssueSummary; import org.opentripplanner.graph_builder.model.GraphBuilderModule; import org.opentripplanner.graph_builder.module.configure.DaggerGraphBuilderFactory; -import org.opentripplanner.graph_builder.module.osm.OsmModule; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.service.worldenvelope.WorldEnvelopeRepository; import org.opentripplanner.standalone.config.BuildConfig; @@ -104,18 +99,7 @@ public static GraphBuilder create( graphBuilder.hasTransitData = hasTransitData; if (hasOsm) { - // TODO: Use the OTP-built-in data source mechanism (see #5677). - Optional mobilityDataSource = dataSources.mobilityProfileDataSource(); - OsmModule osmModule = factory.osmModule(); - if (mobilityDataSource.isPresent()) { - // Parse stuff from the mobility profile CSV - try (var inputStream = mobilityDataSource.get().asInputStream()) { - osmModule.setMobilityProfileData(MobilityProfileParser.parseData(inputStream)); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - graphBuilder.addModule(osmModule); + graphBuilder.addModule(factory.osmModule()); } if (hasGtfs) { diff --git a/src/main/java/org/opentripplanner/graph_builder/GraphBuilderDataSources.java b/src/main/java/org/opentripplanner/graph_builder/GraphBuilderDataSources.java index 46031f9f749..3ed675c0da8 100644 --- a/src/main/java/org/opentripplanner/graph_builder/GraphBuilderDataSources.java +++ b/src/main/java/org/opentripplanner/graph_builder/GraphBuilderDataSources.java @@ -19,7 +19,6 @@ import org.opentripplanner.datastore.api.DataSource; import org.opentripplanner.datastore.api.FileType; import org.opentripplanner.datastore.api.OtpBaseDirectory; -import org.opentripplanner.datastore.file.FileDataSource; import org.opentripplanner.framework.application.OtpAppException; import org.opentripplanner.graph_builder.module.ned.parameter.DemExtractParameters; import org.opentripplanner.graph_builder.module.ned.parameter.DemExtractParametersBuilder; @@ -190,18 +189,6 @@ public Optional stopConsolidation() { return store.stopConsolidation(); } - /** - * Returns the optional data source for the mobility profile routing costs. - */ - public Optional mobilityProfileDataSource() { - return Optional - .ofNullable(buildConfig.mobilityProfileFile) - .map(fileName -> { - var f = baseDirectory.toPath().resolve(fileName).toFile(); - return new FileDataSource(f, FileType.CONFIG); - }); - } - /** * Match the URI provided in the configuration with the URI of a datasource, * either by comparing directly the two URIs or by first prepending the OTP base directory diff --git a/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java b/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java index a221d239e9b..a3c6f313799 100644 --- a/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java @@ -184,7 +184,6 @@ public class BuildConfig implements OtpDataStoreConfig { public final ZoneId transitModelTimeZone; public final URI stopConsolidation; - public final String mobilityProfileFile; /** * Set all parameters from the given Jackson JSON tree, applying defaults. Supplying @@ -613,15 +612,6 @@ that we support remote input files (cloud storage or arbitrary URLs) not all dat ) .asUri(null); - mobilityProfileFile = - root - .of("mobilityProfileFile") - .since(V2_5) - .summary( - "Name of the CSV-formatted file in the build directory which contains the routing costs by mobility profile." - ) - .asString(null); - osmDefaults = OsmConfig.mapOsmDefaults(root, "osmDefaults"); osm = OsmConfig.mapOsmConfig(root, "osm", osmDefaults); demDefaults = DemConfig.mapDemDefaultsConfig(root, "demDefaults"); From a7e555c7d501f9cb8f03ba105dd52478495793eb Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 7 Aug 2024 15:25:18 -0400 Subject: [PATCH 089/111] refactor(StreetEdge): Store original OSM way ID. --- .../graph_builder/module/osm/OsmModule.java | 1 + .../opentripplanner/street/model/edge/StreetEdge.java | 5 +++++ .../street/model/edge/StreetEdgeBuilder.java | 11 +++++++++++ .../street/model/edge/TemporaryPartialStreetEdge.java | 1 + 4 files changed, 18 insertions(+) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 151d1de744b..ccd525aa81f 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -611,6 +611,7 @@ private StreetEdge getEdgeForStreet( .withSlopeOverride(way.getOsmProvider().getWayPropertySet().getSlopeOverride(way)) .withStairs(way.isSteps()) .withWheelchairAccessible(way.isWheelchairAccessible()) + .withOsmWayId(wayId) .withBogusName(way.hasNoName()); // If this is a street crossing (denoted with the tag "footway:crossing"), diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index 37d959e529b..a3fba4009d0 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -90,6 +90,10 @@ public class StreetEdge */ private float bicycleSafetyFactor; + /** The OSM Way ID that this object is created or derived from. */ + public long osmWayId; + + /** A map of cost based on a mobility profile. Implemented as HashMap for serialization. */ public Map profileCost = new HashMap<>(); /** @@ -154,6 +158,7 @@ protected StreetEdge(StreetEdgeBuilder builder) { outAngle = lineStringInOutAngles.outAngle(); elevationExtension = builder.streetElevationExtension(); profileCost = builder.profileCosts(); + osmWayId = builder.osmWayId(); } public StreetEdgeBuilder toBuilder() { diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java index 221aca661f8..52146f57f73 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java @@ -42,6 +42,7 @@ public class StreetEdgeBuilder> { private short flags; private StreetElevationExtension streetElevationExtension; private Map profileCosts = new EnumMap<>(MobilityProfile.class); + private long osmWayId; public StreetEdgeBuilder() { this.defaultLength = true; @@ -64,6 +65,7 @@ public StreetEdgeBuilder(StreetEdge original) { this.bicycleSafetyFactor = original.getBicycleSafetyFactor(); this.flags = original.getFlags(); this.profileCosts = original.profileCost; + this.osmWayId = original.osmWayId; } public StreetEdge buildAndConnect() { @@ -262,6 +264,15 @@ public Map profileCosts() { return profileCosts; } + public B withOsmWayId(long wayId) { + this.osmWayId = wayId; + return instance(); + } + + public long osmWayId() { + return osmWayId; + } + @SuppressWarnings("unchecked") final B instance() { return (B) this; diff --git a/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java index f84edf43115..13942519541 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java @@ -32,6 +32,7 @@ public final class TemporaryPartialStreetEdge extends StreetEdge implements Temp this.parentEdge = builder.parentEdge(); this.geometry = super.getGeometry(); this.profileCost = MobilityProfileRouting.getProRatedProfileCosts(this); + this.osmWayId = builder.osmWayId(); if (!this.profileCost.isEmpty()) { float ratio = (float) (getDistanceMeters() / getParentEdge().getDistanceMeters()); this.setName( From 0778883d0994559da5d043c0ac5f8b0b744271d1 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 8 Aug 2024 13:18:12 -0400 Subject: [PATCH 090/111] refactor(StreetEdge): store profileKey for fast retrieval. --- .../MobilityProfileParser.java | 12 +-- .../graph_builder/module/osm/OsmModule.java | 81 +++++++++++-------- .../street/model/edge/StreetEdge.java | 4 +- .../street/model/edge/StreetEdgeBuilder.java | 12 +-- .../edge/TemporaryPartialStreetEdge.java | 2 +- .../impedance/ImpedanceUpdateHandler.java | 51 ++++++++++++ 6 files changed, 113 insertions(+), 49 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java index f0359e99172..19d74687304 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileParser.java @@ -46,13 +46,8 @@ public static Map parseData(InputStream is) { } /** Helper to build a key of the form "id:from=>to" for an OSM way. */ - public static String getKey(String id, String from, String to) { - return String.format("%s:%s=>%s", id, from, to); - } - - /** Helper to build a key of the form "id:from=>to" for an OSM way. */ - public static String getKey(String id, long from, long to) { - return String.format("%s:%d=>%d", id, from, to); + public static String getKey(long id, long from, long to) { + return String.format("%d:%d=>%d", id, from, to); } private static void parseRow( @@ -65,7 +60,8 @@ private static void parseRow( long fromNode = Long.parseLong(reader.get("Upstream Node"), 10); long toNode = Long.parseLong(reader.get("Downstream Node"), 10); String id = reader.get("Way Id"); - String key = getKey(id, fromNode, toNode); + long osmWayId = Long.parseLong(id, 10); + String key = getKey(osmWayId, fromNode, toNode); float lengthMeters = ONE_MILE_IN_METERS * Float.parseFloat(reader.get("Link Length")); var weightMap = new EnumMap(MobilityProfile.class); diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index ccd525aa81f..2e3feed8f23 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -129,10 +129,6 @@ public Map elevationDataOutput() { return elevationData; } - public void setMobilityProfileData(Map mobilityProfileData) { - this.mobilityProfileData = mobilityProfileData; - } - private void build() { var parkingProcessor = new ParkingProcessor( graph, @@ -197,29 +193,6 @@ private void build() { params.edgeNamer().postprocess(); normalizer.applySafetyFactors(); - - listUnusedMobilityCosts(); - } - - /** - * Lists unused entries from the mobility profile data. - */ - private void listUnusedMobilityCosts() { - if (mobilityProfileData != null) { - List unusedEntries = mobilityProfileData - .keySet() - .stream() - .filter(key -> !mappedMobilityProfileEntries.contains(key)) - .toList(); - - if (!unusedEntries.isEmpty()) { - StringBuilder sb = new StringBuilder(); - for (var entry : unusedEntries) { - sb.append(String.format("%n- %s", entry)); - } - LOG.warn("{} mobility profile entries were not used:{}", unusedEntries.size(), sb); - } - } } /** @@ -587,15 +560,25 @@ private StreetEdge getEdgeForStreet( LineString geometry, boolean back ) { + final boolean DEBUG_STREET_NAMES = true; + long wayId = way.getId(); String label = "way " + wayId + " from " + index; label = label.intern(); I18NString name = params.edgeNamer().getNameForWay(way, label); float carSpeed = way.getOsmProvider().getOsmTagMapper().getCarSpeedForWay(way, back); - StreetTraversalPermission perms = mobilityProfileData != null - ? MobilityProfileRouting.adjustPedestrianPermissions(way, permissions) - : permissions; + StreetTraversalPermission perms = MobilityProfileRouting.adjustPedestrianPermissions(way, permissions); + String startId = startEndpoint.getLabel().toString(); + String endId = endEndpoint.getLabel().toString(); + String profileKey = ""; + try { + long startShortId = Long.parseLong(startId.replace("osm:node:", ""), 10); + long endShortId = Long.parseLong(endId.replace("osm:node:", ""), 10); + profileKey = getProfileKey(way, startShortId, endShortId); + } catch (NumberFormatException nfe) { + LOG.warn("Unable to extract OSM nodes for way {}, {}=>{}", wayId, startId, endId); + } StreetEdgeBuilder seb = new StreetEdgeBuilder<>() .withFromVertex(startEndpoint) @@ -611,7 +594,7 @@ private StreetEdge getEdgeForStreet( .withSlopeOverride(way.getOsmProvider().getWayPropertySet().getSlopeOverride(way)) .withStairs(way.isSteps()) .withWheelchairAccessible(way.isWheelchairAccessible()) - .withOsmWayId(wayId) + .withProfileKey(profileKey) .withBogusName(way.hasNoName()); // If this is a street crossing (denoted with the tag "footway:crossing"), @@ -634,6 +617,17 @@ private StreetEdge getEdgeForStreet( } } + // For testing, indicate the OSM node ids (remove prefixes). + String nameWithNodeIds = String.format( + "%s (%s)", + editedName, + profileKey + ); + if (DEBUG_STREET_NAMES) { + seb.withName(nameWithNodeIds); + } + +/* // Lookup costs by mobility profile, if any were defined. // Note that edges are bidirectional, so we check that mobility data exist in both directions. if (mobilityProfileData != null) { @@ -711,7 +705,7 @@ private StreetEdge getEdgeForStreet( seb.withName(nameWithNodeIds); // Keep tab of node pairs for which mobility profile costs have been mapped. - mappedMobilityProfileEntries.add(key); + mappedMobilityProfileEntries.add(profileKey); } } catch (NumberFormatException nfe) { // Don't do anything related to mobility profiles if node ids are non-numerical. @@ -724,9 +718,32 @@ private StreetEdge getEdgeForStreet( } } + + */ return seb.buildAndConnect(); } + /** * + * Obtains the correct key for this OSM way. + */ + private static String getProfileKey(OSMWay way, long startShortId, long endShortId) { + long wayId = way.getId(); + TLongList nodeRefs = way.getNodeRefs(); + //List nodes = Arrays.stream(nodeRefs.toArray()).mapToObj(Long::toString).toList() ; + //System.out.printf("%s: %s%n", wayId, String.join(",", nodes)); + int startIndex = nodeRefs.indexOf(startShortId); + int endIndex = nodeRefs.indexOf(endShortId); + boolean isReverse = endIndex < startIndex; + + // Use the start and end nodes of the OSM way per the OSM data to lookup the mobility costs. + long wayFromId = nodeRefs.get(0); + long wayToId = nodeRefs.get(nodeRefs.size() - 1); + String key = isReverse + ? MobilityProfileParser.getKey(wayId, wayToId, wayFromId) + : MobilityProfileParser.getKey(wayId, wayFromId, wayToId); + return key; + } + /** Gets the streets from a collection of OSM ways. */ public static List getStreets(Collection ways) { return ways diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index a3fba4009d0..4c06b76c5ee 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -91,7 +91,7 @@ public class StreetEdge private float bicycleSafetyFactor; /** The OSM Way ID that this object is created or derived from. */ - public long osmWayId; + public String profileKey; /** A map of cost based on a mobility profile. Implemented as HashMap for serialization. */ public Map profileCost = new HashMap<>(); @@ -158,7 +158,7 @@ protected StreetEdge(StreetEdgeBuilder builder) { outAngle = lineStringInOutAngles.outAngle(); elevationExtension = builder.streetElevationExtension(); profileCost = builder.profileCosts(); - osmWayId = builder.osmWayId(); + profileKey = builder.profileKey(); } public StreetEdgeBuilder toBuilder() { diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java index 52146f57f73..4fc57bba870 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java @@ -42,7 +42,7 @@ public class StreetEdgeBuilder> { private short flags; private StreetElevationExtension streetElevationExtension; private Map profileCosts = new EnumMap<>(MobilityProfile.class); - private long osmWayId; + private String profileKey; public StreetEdgeBuilder() { this.defaultLength = true; @@ -65,7 +65,7 @@ public StreetEdgeBuilder(StreetEdge original) { this.bicycleSafetyFactor = original.getBicycleSafetyFactor(); this.flags = original.getFlags(); this.profileCosts = original.profileCost; - this.osmWayId = original.osmWayId; + this.profileKey = original.profileKey; } public StreetEdge buildAndConnect() { @@ -264,13 +264,13 @@ public Map profileCosts() { return profileCosts; } - public B withOsmWayId(long wayId) { - this.osmWayId = wayId; + public B withProfileKey(String key) { + this.profileKey = key; return instance(); } - public long osmWayId() { - return osmWayId; + public String profileKey() { + return profileKey; } @SuppressWarnings("unchecked") diff --git a/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java index 13942519541..2e5dea51cfe 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java @@ -32,7 +32,7 @@ public final class TemporaryPartialStreetEdge extends StreetEdge implements Temp this.parentEdge = builder.parentEdge(); this.geometry = super.getGeometry(); this.profileCost = MobilityProfileRouting.getProRatedProfileCosts(this); - this.osmWayId = builder.osmWayId(); + this.profileKey = builder.profileKey(); if (!this.profileCost.isEmpty()) { float ratio = (float) (getDistanceMeters() / getParentEdge().getDistanceMeters()); this.setName( diff --git a/src/main/java/org/opentripplanner/updater/impedance/ImpedanceUpdateHandler.java b/src/main/java/org/opentripplanner/updater/impedance/ImpedanceUpdateHandler.java index 7e8fbe4e836..e4745b96abf 100644 --- a/src/main/java/org/opentripplanner/updater/impedance/ImpedanceUpdateHandler.java +++ b/src/main/java/org/opentripplanner/updater/impedance/ImpedanceUpdateHandler.java @@ -1,14 +1,65 @@ package org.opentripplanner.updater.impedance; +import java.util.Collection; import java.util.Map; +import org.opentripplanner.ext.mobilityprofile.MobilityProfile; import org.opentripplanner.ext.mobilityprofile.MobilityProfileData; +import org.opentripplanner.ext.mobilityprofile.MobilityProfileRouting; +import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.street.model.edge.StreetEdge; +import org.opentripplanner.street.model.vertex.OsmVertex; +import org.opentripplanner.street.search.TraverseMode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Processes updated impedances to the graph. */ public class ImpedanceUpdateHandler { + + private static final Logger LOG = LoggerFactory.getLogger(ImpedanceUpdateHandler.class); + public void update(Graph graph, Map impedances) { + long start = System.currentTimeMillis(); + long count = 0; + + // Some basic G-MAP stats: + // - total edges: ~902k + // - walkable edges: ~92k + // - First-time impedance load: ~29k entries + + // This filter is fast and cuts the size of the searchable set tenfold. + Collection walkableEdges = graph + .getStreetEdges() + .stream() + .filter(se -> se.getPermission().allows(TraverseMode.WALK)) + .toList(); + + for (StreetEdge se : walkableEdges) { + if (se.getFromVertex() instanceof OsmVertex osmFrom && se.getToVertex() instanceof OsmVertex osmTo) { + var impedance = impedances.get(se.profileKey); + if (impedance != null) { + String symbol = "★"; + Map proRatedCosts = impedance.costs(); + + // Create pro-rated impedance data for split edges. + if (osmFrom.nodeId != impedance.fromNode() || osmTo.nodeId != impedance.toNode()) { + double ratio = se.getDistanceMeters() / impedance.lengthInMeters(); + proRatedCosts = MobilityProfileRouting.getProRatedProfileCosts(impedance.costs(), (float)ratio); + symbol = "☆"; + } + + // Update profile costs for this StreetEdge object if an impedance entry was found. + se.profileCost = proRatedCosts; + count++; + + // Amend the name with an indication that impedances were applied + se.setName(I18NString.of(String.format("%s%s", se.getName(), symbol))); + } + } + } + LOG.info("{} new impedance entries imported into graph in {} seconds.", count, (System.currentTimeMillis() - start) / 1000); } } From 5a3fdd7f32ae60541a5126065e3c892095bea76d Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 8 Aug 2024 13:58:20 -0400 Subject: [PATCH 091/111] refactor(OsmImpedanceUpdater): Tweak log messages. --- .../updater/impedance/OsmImpedanceUpdater.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java b/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java index 4ab106d6d90..479913579dc 100644 --- a/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java +++ b/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java @@ -38,7 +38,7 @@ public OsmImpedanceUpdater( this.updateHandler = new ImpedanceUpdateHandler(); this.otpHttpClient = new OtpHttpClientFactory().create(LOG); - LOG.info("Creating real-time alert updater running every {}: {}", pollingPeriod(), url); + LOG.info("Creating impedance updater running every {}: {}", pollingPeriod(), url); } @Override @@ -65,7 +65,11 @@ protected void runPolling() { previousImpedances = impedances; // Handle update in graph writer runnable - saveResultOnGraph.execute((graph, transitModel) -> updateHandler.update(graph, changedImpedances)); + if (!changedImpedances.isEmpty()) { + saveResultOnGraph.execute((graph, transitModel) -> updateHandler.update(graph, changedImpedances)); + } else { + LOG.error("Impedance data unchanged (not updating graph)."); + } } catch (Exception e) { LOG.error("Error parsing impedance data from {}", url, e); } From a6f4ee1f5ef56e247e301964d4a63c5c3e0f0bc6 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 8 Aug 2024 14:01:36 -0400 Subject: [PATCH 092/111] style: Apply prettier. --- .../graph_builder/module/osm/OsmModule.java | 13 ++-- .../config/routerconfig/UpdatersConfig.java | 2 +- .../updaters/OsmImpedanceUpdaterConfig.java | 1 + .../impedance/ImpedanceUpdateHandler.java | 14 +++- .../impedance/OsmImpedanceUpdater.java | 26 ++++--- .../impedance/OsmImpedanceUpdaterTest.java | 73 +++++++++++++------ 6 files changed, 87 insertions(+), 42 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 2e3feed8f23..2dae4cea608 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -568,7 +568,10 @@ private StreetEdge getEdgeForStreet( I18NString name = params.edgeNamer().getNameForWay(way, label); float carSpeed = way.getOsmProvider().getOsmTagMapper().getCarSpeedForWay(way, back); - StreetTraversalPermission perms = MobilityProfileRouting.adjustPedestrianPermissions(way, permissions); + StreetTraversalPermission perms = MobilityProfileRouting.adjustPedestrianPermissions( + way, + permissions + ); String startId = startEndpoint.getLabel().toString(); String endId = endEndpoint.getLabel().toString(); String profileKey = ""; @@ -618,16 +621,12 @@ private StreetEdge getEdgeForStreet( } // For testing, indicate the OSM node ids (remove prefixes). - String nameWithNodeIds = String.format( - "%s (%s)", - editedName, - profileKey - ); + String nameWithNodeIds = String.format("%s (%s)", editedName, profileKey); if (DEBUG_STREET_NAMES) { seb.withName(nameWithNodeIds); } -/* + /* // Lookup costs by mobility profile, if any were defined. // Note that edges are bidirectional, so we check that mobility data exist in both directions. if (mobilityProfileData != null) { diff --git a/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java b/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java index 0d3e6cba971..b570eab6def 100644 --- a/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java @@ -3,8 +3,8 @@ import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V1_5; import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_2; import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.BIKE_RENTAL; -import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.OSM_IMPEDANCE_UPDATER; import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.MQTT_GTFS_RT_UPDATER; +import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.OSM_IMPEDANCE_UPDATER; import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.REAL_TIME_ALERTS; import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.SIRI_AZURE_ET_UPDATER; import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.SIRI_AZURE_SX_UPDATER; diff --git a/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/OsmImpedanceUpdaterConfig.java b/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/OsmImpedanceUpdaterConfig.java index 2b274c79371..5bbed3aa149 100644 --- a/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/OsmImpedanceUpdaterConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/OsmImpedanceUpdaterConfig.java @@ -7,6 +7,7 @@ import org.opentripplanner.updater.impedance.OsmImpedanceUpdaterParameters; public class OsmImpedanceUpdaterConfig { + public static OsmImpedanceUpdaterParameters create(String configRef, NodeAdapter c) { return new OsmImpedanceUpdaterParameters( configRef, diff --git a/src/main/java/org/opentripplanner/updater/impedance/ImpedanceUpdateHandler.java b/src/main/java/org/opentripplanner/updater/impedance/ImpedanceUpdateHandler.java index e4745b96abf..2cd841910ca 100644 --- a/src/main/java/org/opentripplanner/updater/impedance/ImpedanceUpdateHandler.java +++ b/src/main/java/org/opentripplanner/updater/impedance/ImpedanceUpdateHandler.java @@ -37,7 +37,10 @@ public void update(Graph graph, Map impedances) { .toList(); for (StreetEdge se : walkableEdges) { - if (se.getFromVertex() instanceof OsmVertex osmFrom && se.getToVertex() instanceof OsmVertex osmTo) { + if ( + se.getFromVertex() instanceof OsmVertex osmFrom && + se.getToVertex() instanceof OsmVertex osmTo + ) { var impedance = impedances.get(se.profileKey); if (impedance != null) { String symbol = "★"; @@ -46,7 +49,8 @@ public void update(Graph graph, Map impedances) { // Create pro-rated impedance data for split edges. if (osmFrom.nodeId != impedance.fromNode() || osmTo.nodeId != impedance.toNode()) { double ratio = se.getDistanceMeters() / impedance.lengthInMeters(); - proRatedCosts = MobilityProfileRouting.getProRatedProfileCosts(impedance.costs(), (float)ratio); + proRatedCosts = + MobilityProfileRouting.getProRatedProfileCosts(impedance.costs(), (float) ratio); symbol = "☆"; } @@ -60,6 +64,10 @@ public void update(Graph graph, Map impedances) { } } - LOG.info("{} new impedance entries imported into graph in {} seconds.", count, (System.currentTimeMillis() - start) / 1000); + LOG.info( + "{} new impedance entries imported into graph in {} seconds.", + count, + (System.currentTimeMillis() - start) / 1000 + ); } } diff --git a/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java b/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java index 479913579dc..e9f8a85baaf 100644 --- a/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java +++ b/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java @@ -29,9 +29,7 @@ public class OsmImpedanceUpdater extends PollingGraphUpdater { private WriteToGraphCallback saveResultOnGraph; private Map previousImpedances = Map.of(); - public OsmImpedanceUpdater( - OsmImpedanceUpdaterParameters config - ) { + public OsmImpedanceUpdater(OsmImpedanceUpdaterParameters config) { super(config); this.url = config.url(); this.headers = HttpHeaders.of().add(config.headers()).build(); @@ -61,12 +59,17 @@ protected void runPolling() { ); // Filter out which rows have been updated since previous poll. - Map changedImpedances = getChangedImpedances(impedances, previousImpedances); + Map changedImpedances = getChangedImpedances( + impedances, + previousImpedances + ); previousImpedances = impedances; // Handle update in graph writer runnable if (!changedImpedances.isEmpty()) { - saveResultOnGraph.execute((graph, transitModel) -> updateHandler.update(graph, changedImpedances)); + saveResultOnGraph.execute((graph, transitModel) -> + updateHandler.update(graph, changedImpedances) + ); } else { LOG.error("Impedance data unchanged (not updating graph)."); } @@ -111,11 +114,14 @@ public static Map getChangedImpedances( // Include entries that were removed in newImpedances, but mark them as empty map. if (!newImpedances.containsKey(key)) { MobilityProfileData removedData = entry.getValue(); - result.put(key, new MobilityProfileData( - removedData.lengthInMeters(), - removedData.fromNode(), - removedData.toNode(), - Map.of()) + result.put( + key, + new MobilityProfileData( + removedData.lengthInMeters(), + removedData.fromNode(), + removedData.toNode(), + Map.of() + ) ); } } diff --git a/src/test/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdaterTest.java b/src/test/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdaterTest.java index 09f5376ee39..b8ec6b192ac 100644 --- a/src/test/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdaterTest.java +++ b/src/test/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdaterTest.java @@ -10,17 +10,32 @@ import org.opentripplanner.ext.mobilityprofile.MobilityProfileData; class OsmImpedanceUpdaterTest { - private MobilityProfileData profileData(long from, long to, float impNone, float impWChairE, float impBlind) { - return new MobilityProfileData(10, from, to, Map.of( - MobilityProfile.NONE, impNone, - MobilityProfile.WCHAIRE, impWChairE, - MobilityProfile.BLIND, impBlind - )); + + private MobilityProfileData profileData( + long from, + long to, + float impNone, + float impWChairE, + float impBlind + ) { + return new MobilityProfileData( + 10, + from, + to, + Map.of( + MobilityProfile.NONE, + impNone, + MobilityProfile.WCHAIRE, + impWChairE, + MobilityProfile.BLIND, + impBlind + ) + ); } @Test void areSameImpedances() { - MobilityProfileData profileData1 = profileData(1001, 1002,1.2F, 5.0F, 3.4F); + MobilityProfileData profileData1 = profileData(1001, 1002, 1.2F, 5.0F, 3.4F); MobilityProfileData profileData2 = profileData(1002, 1003, 1.6F, 5.0F, 3.4F); MobilityProfileData profileData3 = profileData(1001, 1002, 1.2F, 5.0F, 3.4F); MobilityProfileData profileData4 = profileData(1003, 1004, 1.2F, 5.0F, 3.4F); @@ -39,26 +54,42 @@ void areSameImpedances() { @Test void getChangedImpedances() { Map newImpedances = Map.of( - "Street1", profileData(1001, 1002, 1.2F, 5.0F, 3.4F), - "Street2", profileData(1002, 1003, 2.1F, 7.0F, 4.4F), - "Street3", profileData(1003, 1004, 0.6F, 1.1F, 1.5F), - "Street4", profileData(1004, 1005, 1.4F, 1.6F, 1.8F), - "Street6", profileData(1006, 1007, 1.2F, 5.0F, 3.4F) + "Street1", + profileData(1001, 1002, 1.2F, 5.0F, 3.4F), + "Street2", + profileData(1002, 1003, 2.1F, 7.0F, 4.4F), + "Street3", + profileData(1003, 1004, 0.6F, 1.1F, 1.5F), + "Street4", + profileData(1004, 1005, 1.4F, 1.6F, 1.8F), + "Street6", + profileData(1006, 1007, 1.2F, 5.0F, 3.4F) ); Map oldImpedances = Map.of( - "Street1", profileData(1001, 1002, 1.2F, 5.0F, 3.4F), - "Street2", profileData(1002, 1003, 2.1F, 3.0F, 4.4F), - "Street3", profileData(1003, 1004, 0.6F, 1.1F, 1.5F), - "Street4", profileData(1004, 1005, 1.4F, 1.6F, 1.8F), - "Street5", profileData(1005, 1006, 2.3F, 2.5F, 3.0F) + "Street1", + profileData(1001, 1002, 1.2F, 5.0F, 3.4F), + "Street2", + profileData(1002, 1003, 2.1F, 3.0F, 4.4F), + "Street3", + profileData(1003, 1004, 0.6F, 1.1F, 1.5F), + "Street4", + profileData(1004, 1005, 1.4F, 1.6F, 1.8F), + "Street5", + profileData(1005, 1006, 2.3F, 2.5F, 3.0F) ); Map changedImpedances = Map.of( - "Street6", profileData(1006, 1007, 1.2F, 5.0F, 3.4F), - "Street5", new MobilityProfileData(10, 1005, 1006, Map.of()), - "Street2", profileData(1002, 1003, 2.1F, 7.0F, 4.4F) + "Street6", + profileData(1006, 1007, 1.2F, 5.0F, 3.4F), + "Street5", + new MobilityProfileData(10, 1005, 1006, Map.of()), + "Street2", + profileData(1002, 1003, 2.1F, 7.0F, 4.4F) ); - assertEquals(changedImpedances, OsmImpedanceUpdater.getChangedImpedances(newImpedances, oldImpedances)); + assertEquals( + changedImpedances, + OsmImpedanceUpdater.getChangedImpedances(newImpedances, oldImpedances) + ); } } From b667766672b67f8435fe6ce564c247d506b863c5 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 8 Aug 2024 14:28:24 -0400 Subject: [PATCH 093/111] refactor(OsmModule): Remove debug stuff. --- .../graph_builder/module/osm/OsmModule.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 2dae4cea608..41350a31efa 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -560,8 +560,6 @@ private StreetEdge getEdgeForStreet( LineString geometry, boolean back ) { - final boolean DEBUG_STREET_NAMES = true; - long wayId = way.getId(); String label = "way " + wayId + " from " + index; label = label.intern(); @@ -620,12 +618,6 @@ private StreetEdge getEdgeForStreet( } } - // For testing, indicate the OSM node ids (remove prefixes). - String nameWithNodeIds = String.format("%s (%s)", editedName, profileKey); - if (DEBUG_STREET_NAMES) { - seb.withName(nameWithNodeIds); - } - /* // Lookup costs by mobility profile, if any were defined. // Note that edges are bidirectional, so we check that mobility data exist in both directions. From 0f3da14494d6548c08b4098b515fce940a91b569 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 8 Aug 2024 14:30:10 -0400 Subject: [PATCH 094/111] refactor(KryoBuilder): Remove mobility profile persistence. --- .../routing/graph/kryosupport/KryoBuilder.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/org/opentripplanner/routing/graph/kryosupport/KryoBuilder.java b/src/main/java/org/opentripplanner/routing/graph/kryosupport/KryoBuilder.java index 3836b8aa6e0..d094f8c945f 100644 --- a/src/main/java/org/opentripplanner/routing/graph/kryosupport/KryoBuilder.java +++ b/src/main/java/org/opentripplanner/routing/graph/kryosupport/KryoBuilder.java @@ -3,7 +3,6 @@ import com.conveyal.kryo.TIntArrayListSerializer; import com.conveyal.kryo.TIntIntHashMapSerializer; import com.esotericsoftware.kryo.Kryo; -import com.esotericsoftware.kryo.serializers.EnumMapSerializer; import com.esotericsoftware.kryo.serializers.ExternalizableSerializer; import com.esotericsoftware.kryo.util.DefaultInstantiatorStrategy; import com.google.common.collect.ArrayListMultimap; @@ -13,13 +12,11 @@ import gnu.trove.impl.hash.TPrimitiveHash; import gnu.trove.list.array.TIntArrayList; import gnu.trove.map.hash.TIntIntHashMap; -import java.util.EnumMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import org.objenesis.strategy.SerializingInstantiatorStrategy; -import org.opentripplanner.ext.mobilityprofile.MobilityProfile; import org.opentripplanner.kryo.BuildConfigSerializer; import org.opentripplanner.kryo.RouterConfigSerializer; import org.opentripplanner.kryo.UnmodifiableCollectionsSerializer; @@ -68,7 +65,6 @@ public static Kryo create() { kryo.register(RouterConfig.class, new RouterConfigSerializer()); kryo.register(BuildConfig.class, new BuildConfigSerializer()); kryo.register(AtomicInteger.class, new AtomicIntegerSerializer()); - kryo.register(EnumMap.class, new EnumMapSerializer(MobilityProfile.class)); UnmodifiableCollectionsSerializer.registerSerializers(kryo); // Instantiation strategy: how should Kryo make new instances of objects when they are deserialized? From 5b56661075d13e057a10c47e7a5f3a06e260813b Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 8 Aug 2024 17:54:59 -0400 Subject: [PATCH 095/111] refactor(OsmModule): Remove unused code. --- .../graph_builder/module/osm/OsmModule.java | 107 +----------------- 1 file changed, 3 insertions(+), 104 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 41350a31efa..8188ed9d699 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -7,7 +7,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -16,8 +15,6 @@ import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.LineString; -import org.opentripplanner.ext.mobilityprofile.MobilityProfile; -import org.opentripplanner.ext.mobilityprofile.MobilityProfileData; import org.opentripplanner.ext.mobilityprofile.MobilityProfileParser; import org.opentripplanner.ext.mobilityprofile.MobilityProfileRouting; import org.opentripplanner.framework.geometry.GeometryUtils; @@ -65,8 +62,6 @@ public class OsmModule implements GraphBuilderModule { private final SafetyValueNormalizer normalizer; private final VertexGenerator vertexGenerator; private final OsmDatabase osmdb; - private Map mobilityProfileData; - private HashSet mappedMobilityProfileEntries; private List osmStreets; private List osmFootways; private OSMWay lastQueriedCrossing; @@ -162,8 +157,6 @@ private void build() { // figure out which nodes that are actually intersections vertexGenerator.initIntersectionNodes(); - mappedMobilityProfileEntries = new HashSet<>(); - buildBasicGraph(); buildWalkableAreas(!params.areaVisibility()); validateBarriers(); @@ -615,102 +608,10 @@ private StreetEdge getEdgeForStreet( seb.withBogusName(false); } else if ("sidewalk".equals(editedName) || "path".equals(editedName)) { editedName = String.format("%s %s", editedName, wayId); + // seb.withName(editedName); } } - /* - // Lookup costs by mobility profile, if any were defined. - // Note that edges are bidirectional, so we check that mobility data exist in both directions. - if (mobilityProfileData != null) { - String startId = startEndpoint.getLabel().toString(); - String endId = endEndpoint.getLabel().toString(); - - try { - long startShortId = Long.parseLong(startId.replace("osm:node:", ""), 10); - long endShortId = Long.parseLong(endId.replace("osm:node:", ""), 10); - - // For testing, indicate the OSM node ids (remove prefixes). - String nameWithNodeIds = String.format( - "%s (%s, %s→%s)", - editedName, - wayId, - startShortId, - endShortId - ); - - seb.withName(nameWithNodeIds); - - String wayIdStr = Long.toString(wayId, 10); - TLongList nodeRefs = way.getNodeRefs(); - int startIndex = nodeRefs.indexOf(startShortId); - int endIndex = nodeRefs.indexOf(endShortId); - boolean isReverse = endIndex < startIndex; - - // Use the start and end nodes of the OSM way per the OSM data to lookup the mobility costs. - long wayFromId = nodeRefs.get(0); - long wayToId = nodeRefs.get(nodeRefs.size() - 1); - String key = isReverse - ? MobilityProfileParser.getKey(wayIdStr, wayToId, wayFromId) - : MobilityProfileParser.getKey(wayIdStr, wayFromId, wayToId); - - var edgeMobilityCostMap = mobilityProfileData.get(key); - if (edgeMobilityCostMap != null) { - // Check whether the nodes for this way match the nodes from mobility profile data. - if ( - startShortId == edgeMobilityCostMap.fromNode() && - endShortId == edgeMobilityCostMap.toNode() || - startShortId == edgeMobilityCostMap.toNode() && - endShortId == edgeMobilityCostMap.fromNode() - ) { - // If the from/to nodes match, then assign the cost directly - seb.withProfileCosts(edgeMobilityCostMap.costs()); - - // Append an indication that this edge uses a full profile cost. - nameWithNodeIds = String.format("%s ☑", nameWithNodeIds); - // System.out.printf("Way (full length): %s size %d%n", nameWithNodeIds, edgeMobilityCostMap.costs().size()); - System.out.printf( - "%s %f%n", - nameWithNodeIds, - edgeMobilityCostMap.costs().get(MobilityProfile.WCHAIRE) - ); - } else { - // Otherwise, pro-rate the cost to the length of the edge. - float ratio = (float) (length / edgeMobilityCostMap.lengthInMeters()); - - Map proRatedProfileCosts = MobilityProfileRouting.getProRatedProfileCosts( - edgeMobilityCostMap.costs(), - ratio - ); - seb.withProfileCosts(proRatedProfileCosts); - - // Append an indication that this edge uses a partial profile cost. - nameWithNodeIds = String.format("%s r%4.3f l%4.3f", nameWithNodeIds, ratio, length); - // System.out.printf("Way (partial): %s size %d%n", nameWithNodeIds, proRatedProfileCosts.size()); - System.out.printf( - "%s %f%n", - nameWithNodeIds, - proRatedProfileCosts.get(MobilityProfile.WCHAIRE) - ); - } - - seb.withName(nameWithNodeIds); - - // Keep tab of node pairs for which mobility profile costs have been mapped. - mappedMobilityProfileEntries.add(profileKey); - } - } catch (NumberFormatException nfe) { - // Don't do anything related to mobility profiles if node ids are non-numerical. - LOG.info( - "Not applying mobility costs for link {}:{}→{}", - wayId, - startEndpoint.getLabel(), - endEndpoint.getLabel() - ); - } - } - - - */ return seb.buildAndConnect(); } @@ -720,8 +621,7 @@ private StreetEdge getEdgeForStreet( private static String getProfileKey(OSMWay way, long startShortId, long endShortId) { long wayId = way.getId(); TLongList nodeRefs = way.getNodeRefs(); - //List nodes = Arrays.stream(nodeRefs.toArray()).mapToObj(Long::toString).toList() ; - //System.out.printf("%s: %s%n", wayId, String.join(",", nodes)); + int startIndex = nodeRefs.indexOf(startShortId); int endIndex = nodeRefs.indexOf(endShortId); boolean isReverse = endIndex < startIndex; @@ -729,10 +629,9 @@ private static String getProfileKey(OSMWay way, long startShortId, long endShort // Use the start and end nodes of the OSM way per the OSM data to lookup the mobility costs. long wayFromId = nodeRefs.get(0); long wayToId = nodeRefs.get(nodeRefs.size() - 1); - String key = isReverse + return isReverse ? MobilityProfileParser.getKey(wayId, wayToId, wayFromId) : MobilityProfileParser.getKey(wayId, wayFromId, wayToId); - return key; } /** Gets the streets from a collection of OSM ways. */ From 1c8458277ad58fbd7a06b6d5c84655ef2c6cd2dd Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 14 Aug 2024 09:46:34 -0400 Subject: [PATCH 096/111] refactor(StreetEdge): Update profileKey comment. --- .../java/org/opentripplanner/street/model/edge/StreetEdge.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index 4c06b76c5ee..30e16997a9b 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -90,7 +90,7 @@ public class StreetEdge */ private float bicycleSafetyFactor; - /** The OSM Way ID that this object is created or derived from. */ + /** The key to the mobility profile data for this street edge. */ public String profileKey; /** A map of cost based on a mobility profile. Implemented as HashMap for serialization. */ From 5128679202629aa0ed42f5b5be4de53d74706759 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 14 Aug 2024 14:37:29 -0400 Subject: [PATCH 097/111] refactor(OsmModule): Remove build-time permission restrictions. --- .../graph_builder/module/osm/OsmModule.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 8188ed9d699..af0cccc9255 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -16,7 +16,6 @@ import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.LineString; import org.opentripplanner.ext.mobilityprofile.MobilityProfileParser; -import org.opentripplanner.ext.mobilityprofile.MobilityProfileRouting; import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.framework.geometry.SphericalDistanceLibrary; import org.opentripplanner.framework.i18n.I18NString; @@ -559,10 +558,6 @@ private StreetEdge getEdgeForStreet( I18NString name = params.edgeNamer().getNameForWay(way, label); float carSpeed = way.getOsmProvider().getOsmTagMapper().getCarSpeedForWay(way, back); - StreetTraversalPermission perms = MobilityProfileRouting.adjustPedestrianPermissions( - way, - permissions - ); String startId = startEndpoint.getLabel().toString(); String endId = endEndpoint.getLabel().toString(); String profileKey = ""; @@ -580,7 +575,7 @@ private StreetEdge getEdgeForStreet( .withGeometry(geometry) .withName(name) .withMeterLength(length) - .withPermission(perms) + .withPermission(permissions) .withBack(back) .withCarSpeed(carSpeed) .withLink(way.isLink()) From 26a2efb554b0addcbe9fa0a954c201fdf19a54b3 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 14 Aug 2024 14:39:42 -0400 Subject: [PATCH 098/111] refactor(StreetEdge): Check for non-null profileCosts. --- .../ext/mobilityprofile/MobilityProfileRouting.java | 8 ++++++-- .../algorithm/mapping/StatesToWalkStepsMapper.java | 2 +- .../org/opentripplanner/street/model/edge/StreetEdge.java | 4 ++-- .../street/model/edge/TemporaryPartialStreetEdge.java | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java index 3e34ff4205d..d066d056fa9 100644 --- a/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java +++ b/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java @@ -3,6 +3,7 @@ import static java.util.Map.entry; import java.util.EnumMap; +import java.util.HashMap; import java.util.Map; import org.opentripplanner.openstreetmap.model.OSMWay; import org.opentripplanner.street.model.StreetTraversalPermission; @@ -67,8 +68,11 @@ public static Map getProRatedProfileCosts( TemporaryPartialStreetEdge tmpEdge ) { StreetEdge parentEdge = tmpEdge.getParentEdge(); - float ratio = (float) (tmpEdge.getDistanceMeters() / parentEdge.getDistanceMeters()); - return getProRatedProfileCosts(parentEdge.profileCost, ratio); + if (parentEdge.profileCost != null) { + float ratio = (float) (tmpEdge.getDistanceMeters() / parentEdge.getDistanceMeters()); + return getProRatedProfileCosts(parentEdge.profileCost, ratio); + } + return new HashMap<>(); } public static Map getProRatedProfileCosts( diff --git a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java index 6a1f5b5c3c6..e145db333c5 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java @@ -200,7 +200,7 @@ private void processState(State backState, State forwardState) { // G-MAP-specific: Overwrite the name on short street edges so they are merged with longer street // sections to clean up turn-by-turn instructions. - if (edge instanceof StreetEdge streetEdge && !streetEdge.profileCost.isEmpty()) { + if (edge instanceof StreetEdge streetEdge && streetEdge.profileCost != null && !streetEdge.profileCost.isEmpty()) { if (shouldOverwriteCurrentDirectionText(edge, direction)) { // HACK: If the instruction is "continue", the current street name is bogus and its length is very short (< 10 meters) // but not the next edge one, use the next street name and don't start a new step. diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index 30e16997a9b..4fba9840088 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -94,7 +94,7 @@ public class StreetEdge public String profileKey; /** A map of cost based on a mobility profile. Implemented as HashMap for serialization. */ - public Map profileCost = new HashMap<>(); + public transient Map profileCost = new HashMap<>(); /** * walkSafetyFactor = length * walkSafetyFactor. For example, a 100m street with a safety @@ -707,7 +707,7 @@ public SplitStreetEdge splitDestructively(SplitterVertex v) { seb1.withMilliMeterLength(l1); seb2.withMilliMeterLength(l2); - if (!profileCost.isEmpty()) { + if (profileCost != null && !profileCost.isEmpty()) { float ratio1 = (float) l1 / length_mm; float ratio2 = (float) l2 / length_mm; seb1.withProfileCosts(MobilityProfileRouting.getProRatedProfileCosts(profileCost, ratio1)); diff --git a/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java index 2e5dea51cfe..327d77bfa71 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java @@ -33,7 +33,7 @@ public final class TemporaryPartialStreetEdge extends StreetEdge implements Temp this.geometry = super.getGeometry(); this.profileCost = MobilityProfileRouting.getProRatedProfileCosts(this); this.profileKey = builder.profileKey(); - if (!this.profileCost.isEmpty()) { + if (this.profileCost != null && !this.profileCost.isEmpty()) { float ratio = (float) (getDistanceMeters() / getParentEdge().getDistanceMeters()); this.setName( new LocalizedString( From 7e02c2270ad68642e7650987a7032dc70ab36222 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 14 Aug 2024 14:50:57 -0400 Subject: [PATCH 099/111] style(StatesToWalkStepsMapper): Apply prettier. --- .../routing/algorithm/mapping/StatesToWalkStepsMapper.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java index e145db333c5..df4c54d0302 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java @@ -200,7 +200,10 @@ private void processState(State backState, State forwardState) { // G-MAP-specific: Overwrite the name on short street edges so they are merged with longer street // sections to clean up turn-by-turn instructions. - if (edge instanceof StreetEdge streetEdge && streetEdge.profileCost != null && !streetEdge.profileCost.isEmpty()) { + if ( + edge instanceof StreetEdge streetEdge && + streetEdge.profileCost != null && !streetEdge.profileCost.isEmpty() + ) { if (shouldOverwriteCurrentDirectionText(edge, direction)) { // HACK: If the instruction is "continue", the current street name is bogus and its length is very short (< 10 meters) // but not the next edge one, use the next street name and don't start a new step. From 92a0c048fd69330b9811655722ae606fcc4832bf Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 14 Aug 2024 16:38:23 -0400 Subject: [PATCH 100/111] refactor(StreetEdge): Extract method to check non-null/empty profile costs. --- .../org/opentripplanner/street/model/edge/StreetEdge.java | 8 ++++++-- .../street/model/edge/TemporaryPartialStreetEdge.java | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index 4fba9840088..5878bc76bcd 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -707,7 +707,7 @@ public SplitStreetEdge splitDestructively(SplitterVertex v) { seb1.withMilliMeterLength(l1); seb2.withMilliMeterLength(l2); - if (profileCost != null && !profileCost.isEmpty()) { + if (hasProfileCost()) { float ratio1 = (float) l1 / length_mm; float ratio2 = (float) l2 / length_mm; seb1.withProfileCosts(MobilityProfileRouting.getProRatedProfileCosts(profileCost, ratio1)); @@ -731,6 +731,10 @@ public SplitStreetEdge splitDestructively(SplitterVertex v) { return splitEdges; } + public boolean hasProfileCost() { + return profileCost != null && !profileCost.isEmpty(); + } + /** Split this street edge and return the resulting street edges. The original edge is kept. */ public SplitStreetEdge splitNonDestructively( SplitterVertex v, @@ -1339,7 +1343,7 @@ private TraversalCosts walkingTraversalCosts( // (assuming a pre-determined travel speed for each profile) // and the travel speed for that profile is used to overwrite the time calculated above. if (mobilityProfile != null) { - if (profileCost != null && !profileCost.isEmpty()) { + if (hasProfileCost()) { var defaultTravelHours = MobilityProfileRouting.computeTravelHours( getEffectiveWalkDistance(), mobilityProfile diff --git a/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java index 327d77bfa71..12c89d29b7c 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java @@ -33,7 +33,7 @@ public final class TemporaryPartialStreetEdge extends StreetEdge implements Temp this.geometry = super.getGeometry(); this.profileCost = MobilityProfileRouting.getProRatedProfileCosts(this); this.profileKey = builder.profileKey(); - if (this.profileCost != null && !this.profileCost.isEmpty()) { + if (this.hasProfileCost()) { float ratio = (float) (getDistanceMeters() / getParentEdge().getDistanceMeters()); this.setName( new LocalizedString( From 5dc06356ab4ff16e71d3cd4e896fba920ae80af4 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 15 Aug 2024 13:38:32 -0400 Subject: [PATCH 101/111] refactor(OsmImpedanceUpdater): Improve logs and comments. --- .../opentripplanner/updater/impedance/OsmImpedanceUpdater.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java b/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java index e9f8a85baaf..f896c7f51e1 100644 --- a/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java +++ b/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java @@ -51,12 +51,14 @@ public String toString() { @Override protected void runPolling() { + LOG.info("Fetching mobility impedances..."); try { final Map impedances = otpHttpClient.getAndMap( URI.create(url), this.headers.asMap(), MobilityProfileParser::parseData ); + LOG.info("Fetched mobility impedances."); // Filter out which rows have been updated since previous poll. Map changedImpedances = getChangedImpedances( @@ -74,6 +76,7 @@ protected void runPolling() { LOG.error("Impedance data unchanged (not updating graph)."); } } catch (Exception e) { + // Download errors, including timeouts, will be caught here. LOG.error("Error parsing impedance data from {}", url, e); } } From 5ad796d174604bca1bbed89e84d4255f2e61104d Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 15 Aug 2024 13:39:28 -0400 Subject: [PATCH 102/111] refactor(OsmModule): Improve logs. --- .../org/opentripplanner/graph_builder/module/osm/OsmModule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index af0cccc9255..6fe3caf468e 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -566,7 +566,7 @@ private StreetEdge getEdgeForStreet( long endShortId = Long.parseLong(endId.replace("osm:node:", ""), 10); profileKey = getProfileKey(way, startShortId, endShortId); } catch (NumberFormatException nfe) { - LOG.warn("Unable to extract OSM nodes for way {}, {}=>{}", wayId, startId, endId); + LOG.warn("Unable to extract OSM nodes for way {} {}, {}=>{}", name, wayId, startId, endId); } StreetEdgeBuilder seb = new StreetEdgeBuilder<>() From d6ce9c5f4f4ff0a9ed5af4fa3cdad4eeeec2d611 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 15 Aug 2024 13:40:19 -0400 Subject: [PATCH 103/111] refactor(StatesToWalkStepMapper): Reuse hasProfileCost check. --- .../routing/algorithm/mapping/StatesToWalkStepsMapper.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java index df4c54d0302..d5b8b31efce 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java @@ -200,10 +200,7 @@ private void processState(State backState, State forwardState) { // G-MAP-specific: Overwrite the name on short street edges so they are merged with longer street // sections to clean up turn-by-turn instructions. - if ( - edge instanceof StreetEdge streetEdge && - streetEdge.profileCost != null && !streetEdge.profileCost.isEmpty() - ) { + if (edge instanceof StreetEdge streetEdge && streetEdge.hasProfileCost()) { if (shouldOverwriteCurrentDirectionText(edge, direction)) { // HACK: If the instruction is "continue", the current street name is bogus and its length is very short (< 10 meters) // but not the next edge one, use the next street name and don't start a new step. From 1c47d275046cc26ec867d2eebc12d479ecc5f1b7 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 15 Aug 2024 15:07:02 -0400 Subject: [PATCH 104/111] refactor(StreetEdge): Propagate profile info when splitting edges. --- .../street/model/edge/StreetEdge.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index 5878bc76bcd..af00685e55b 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -648,19 +648,23 @@ public void addRentalRestriction(RentalRestrictionExtension ext) { public SplitStreetEdge splitDestructively(SplitterVertex v) { SplitLineString geoms = GeometryUtils.splitGeometryAtPoint(getGeometry(), v.getCoordinate()); + // Instance where props are copied StreetEdgeBuilder seb1 = new StreetEdgeBuilder<>() .withFromVertex((StreetVertex) fromv) .withToVertex(v) .withGeometry(geoms.beginning()) .withName(name) + .withProfileKey(profileKey) .withPermission(permission) .withBack(isBack()); + // Instance where props are copied StreetEdgeBuilder seb2 = new StreetEdgeBuilder<>() .withFromVertex(v) .withToVertex((StreetVertex) tov) .withGeometry(geoms.ending()) .withName(name) + .withProfileKey(profileKey) .withPermission(permission) .withBack(isBack()); @@ -746,27 +750,58 @@ public SplitStreetEdge splitNonDestructively( StreetEdge e1 = null; StreetEdge e2 = null; + // TODO: refactor + // we have this code implemented in both directions, because splits are fudged half a millimeter + // when the length of this is odd. We want to make sure the lengths of the split streets end up + // exactly the same as their backStreets so that if they are split again the error does not accumulate + // and so that the order in which they are split does not matter. + int l1 = defaultMillimeterLength(geoms.beginning()); + int l2 = defaultMillimeterLength(geoms.ending()); + if (!isBack()) { + // cast before the divide so that the sum is promoted + double frac = (double) l1 / (l1 + l2); + l1 = (int) (length_mm * frac); + l2 = length_mm - l1; + } else { + // cast before the divide so that the sum is promoted + double frac = (double) l2 / (l1 + l2); + l2 = (int) (length_mm * frac); + l1 = length_mm - l2; + } + if (direction == LinkingDirection.OUTGOING || direction == LinkingDirection.BOTH_WAYS) { + // Instance where props are copied var seb1 = new TemporaryPartialStreetEdgeBuilder() .withParentEdge(this) .withFromVertex((StreetVertex) fromv) .withToVertex(v) .withGeometry(geoms.beginning()) .withName(name) + .withProfileKey(profileKey) .withBack(isBack()); + if (hasProfileCost()) { + float ratio = (float) l1 / length_mm; + seb1.withProfileCosts(MobilityProfileRouting.getProRatedProfileCosts(profileCost, ratio)); + } copyPropertiesToSplitEdge(seb1, 0, defaultMillimeterLength(geoms.beginning()) / 1000.0); e1 = seb1.buildAndConnect(); copyRentalRestrictionsToSplitEdge(e1); tempEdges.addEdge(e1); } if (direction == LinkingDirection.INCOMING || direction == LinkingDirection.BOTH_WAYS) { + // Instance where props are copied var seb2 = new TemporaryPartialStreetEdgeBuilder() .withParentEdge(this) .withFromVertex(v) .withToVertex((StreetVertex) tov) .withGeometry(geoms.ending()) .withName(name) + .withProfileKey(profileKey) .withBack(isBack()); + if (hasProfileCost()) { + float ratio = (float) l2 / length_mm; + seb2.withProfileCosts(MobilityProfileRouting.getProRatedProfileCosts(profileCost, ratio)); + } copyPropertiesToSplitEdge( seb2, getDistanceMeters() - defaultMillimeterLength(geoms.ending()) / 1000.0, @@ -807,12 +842,14 @@ public Optional createPartialEdge(StreetVertex from, StreetVertex to) { double lengthRatio = partial.getLength() / parent.getLength(); double length = getDistanceMeters() * lengthRatio; + // Instance where props are copied var tpseb = new TemporaryPartialStreetEdgeBuilder() .withParentEdge(this) .withFromVertex(from) .withToVertex(to) .withGeometry(partial) .withName(getName()) + .withProfileKey(profileKey) .withMeterLength(length); copyPropertiesToSplitEdge(tpseb, start, start + length); TemporaryPartialStreetEdge se = tpseb.buildAndConnect(); From d1496bed3b751cf5e255a4e620f864fde9e79b3c Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 15 Aug 2024 15:07:49 -0400 Subject: [PATCH 105/111] refactor(ImpedanceUpdateHandler): Apply impedances to edges with SplitterVertex. --- .../impedance/ImpedanceUpdateHandler.java | 51 ++++++++----------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/src/main/java/org/opentripplanner/updater/impedance/ImpedanceUpdateHandler.java b/src/main/java/org/opentripplanner/updater/impedance/ImpedanceUpdateHandler.java index 2cd841910ca..5167368c1f2 100644 --- a/src/main/java/org/opentripplanner/updater/impedance/ImpedanceUpdateHandler.java +++ b/src/main/java/org/opentripplanner/updater/impedance/ImpedanceUpdateHandler.java @@ -1,6 +1,5 @@ package org.opentripplanner.updater.impedance; -import java.util.Collection; import java.util.Map; import org.opentripplanner.ext.mobilityprofile.MobilityProfile; import org.opentripplanner.ext.mobilityprofile.MobilityProfileData; @@ -9,7 +8,6 @@ import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.street.model.edge.StreetEdge; import org.opentripplanner.street.model.vertex.OsmVertex; -import org.opentripplanner.street.search.TraverseMode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,38 +27,31 @@ public void update(Graph graph, Map impedances) { // - walkable edges: ~92k // - First-time impedance load: ~29k entries - // This filter is fast and cuts the size of the searchable set tenfold. - Collection walkableEdges = graph - .getStreetEdges() - .stream() - .filter(se -> se.getPermission().allows(TraverseMode.WALK)) - .toList(); + for (StreetEdge se : graph.getStreetEdges()) { + var impedance = impedances.get(se.profileKey); + if (impedance != null) { + String symbol = "★"; + Map proRatedCosts = impedance.costs(); - for (StreetEdge se : walkableEdges) { - if ( - se.getFromVertex() instanceof OsmVertex osmFrom && - se.getToVertex() instanceof OsmVertex osmTo - ) { - var impedance = impedances.get(se.profileKey); - if (impedance != null) { - String symbol = "★"; - Map proRatedCosts = impedance.costs(); + // Create pro-rated impedances for split edges with intermediate OsmVertex or SplitterVertex. + long fromNodeId = 0; + if (se.getFromVertex() instanceof OsmVertex osmFrom) fromNodeId = osmFrom.nodeId; + long toNodeId = 0; + if (se.getToVertex() instanceof OsmVertex osmTo) toNodeId = osmTo.nodeId; - // Create pro-rated impedance data for split edges. - if (osmFrom.nodeId != impedance.fromNode() || osmTo.nodeId != impedance.toNode()) { - double ratio = se.getDistanceMeters() / impedance.lengthInMeters(); - proRatedCosts = - MobilityProfileRouting.getProRatedProfileCosts(impedance.costs(), (float) ratio); - symbol = "☆"; - } + if (fromNodeId != impedance.fromNode() || toNodeId != impedance.toNode()) { + double ratio = se.getDistanceMeters() / impedance.lengthInMeters(); + proRatedCosts = + MobilityProfileRouting.getProRatedProfileCosts(impedance.costs(), (float) ratio); + symbol = "☆"; + } - // Update profile costs for this StreetEdge object if an impedance entry was found. - se.profileCost = proRatedCosts; - count++; + // Update profile costs for this StreetEdge object if an impedance entry was found. + se.profileCost = proRatedCosts; + count++; - // Amend the name with an indication that impedances were applied - se.setName(I18NString.of(String.format("%s%s", se.getName(), symbol))); - } + // Amend the name with an indication that impedances were applied + se.setName(I18NString.of(String.format("%s%s", se.getName(), symbol))); } } From 371ee42bca87baf90c8ce75ec36f8da7f6e319e8 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 16 Aug 2024 10:38:36 -0400 Subject: [PATCH 106/111] feat(OsmModule): Add build option to prevent ped routing on roads. --- docs/BuildConfiguration.md | 10 ++++++++++ .../module/configure/GraphBuilderModules.java | 1 + .../graph_builder/module/osm/OsmModule.java | 7 ++++++- .../graph_builder/module/osm/OsmModuleBuilder.java | 9 ++++++++- .../module/osm/parameters/OsmProcessingParameters.java | 3 ++- .../opentripplanner/standalone/config/BuildConfig.java | 10 ++++++++++ 6 files changed, 37 insertions(+), 3 deletions(-) diff --git a/docs/BuildConfiguration.md b/docs/BuildConfiguration.md index 7d2d90ea41b..6a38b5466ef 100644 --- a/docs/BuildConfiguration.md +++ b/docs/BuildConfiguration.md @@ -37,6 +37,7 @@ Sections follow that describe particular settings in more depth. | [osmCacheDataInMem](#osmCacheDataInMem) | `boolean` | If OSM data should be cached in memory during processing. | *Optional* | `false` | 2.0 | | [osmNaming](#osmNaming) | `enum` | A custom OSM namer to use. | *Optional* | `"default"` | 1.5 | | platformEntriesLinking | `boolean` | Link unconnected entries to public transport platforms. | *Optional* | `false` | 2.0 | +| [preventWalkingOnRoads](#preventWalkingOnRoads) | `boolean` | Determines whether to prevent pedestrian routing on roads or not. | *Optional* | `false` | 2.5 | | [readCachedElevations](#readCachedElevations) | `boolean` | Whether to read cached elevation data. | *Optional* | `true` | 2.0 | | staticBikeParkAndRide | `boolean` | Whether we should create bike P+R stations from OSM data. | *Optional* | `false` | 1.5 | | staticParkAndRide | `boolean` | Whether we should create car P+R stations from OSM data. | *Optional* | `true` | 1.5 | @@ -528,6 +529,15 @@ data, and to `false` to read the stream from the source each time. A custom OSM namer to use. +

preventWalkingOnRoads

+ +**Since version:** `2.5` ∙ **Type:** `boolean` ∙ **Cardinality:** `Optional` ∙ **Default value:** `false` +**Path:** / + +Determines whether to prevent pedestrian routing on roads or not. + +True to prevent pedestrian routing on roads. +

readCachedElevations

**Since version:** `2.0` ∙ **Type:** `boolean` ∙ **Cardinality:** `Optional` ∙ **Default value:** `true` diff --git a/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java b/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java index 10d3a997579..e0c1bd26914 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java @@ -85,6 +85,7 @@ static OsmModule provideOpenStreetMapModule( .withBoardingAreaRefTags(config.boardingLocationTags) .withIssueStore(issueStore) .withStreetLimitationParameters(streetLimitationParameters) + .withPreventWalkingOnRoads(config.preventWalkingOnRoads) .build(); } diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 6fe3caf468e..65c12d5aa20 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -16,6 +16,7 @@ import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.LineString; import org.opentripplanner.ext.mobilityprofile.MobilityProfileParser; +import org.opentripplanner.ext.mobilityprofile.MobilityProfileRouting; import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.framework.geometry.SphericalDistanceLibrary; import org.opentripplanner.framework.i18n.I18NString; @@ -569,13 +570,17 @@ private StreetEdge getEdgeForStreet( LOG.warn("Unable to extract OSM nodes for way {} {}, {}=>{}", name, wayId, startId, endId); } + StreetTraversalPermission perms = params.preventWalkingOnRoads() + ? MobilityProfileRouting.adjustPedestrianPermissions(way, permissions) + : permissions; + StreetEdgeBuilder seb = new StreetEdgeBuilder<>() .withFromVertex(startEndpoint) .withToVertex(endEndpoint) .withGeometry(geometry) .withName(name) .withMeterLength(length) - .withPermission(permissions) + .withPermission(perms) .withBack(back) .withCarSpeed(carSpeed) .withLink(way.isLink()) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModuleBuilder.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModuleBuilder.java index f0a40fa678f..eb19e491430 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModuleBuilder.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModuleBuilder.java @@ -26,6 +26,7 @@ public class OsmModuleBuilder { private boolean staticBikeParkAndRide = false; private int maxAreaNodes; private StreetLimitationParameters streetLimitationParameters = new StreetLimitationParameters(); + private boolean preventWalkingOnRoads; OsmModuleBuilder(Collection providers, Graph graph) { this.providers = providers; @@ -77,6 +78,11 @@ public OsmModuleBuilder withStreetLimitationParameters(StreetLimitationParameter return this; } + public OsmModuleBuilder withPreventWalkingOnRoads(boolean value) { + this.preventWalkingOnRoads = value; + return this; + } + public OsmModule build() { return new OsmModule( providers, @@ -90,7 +96,8 @@ public OsmModule build() { areaVisibility, platformEntriesLinking, staticParkAndRide, - staticBikeParkAndRide + staticBikeParkAndRide, + preventWalkingOnRoads ) ); } diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmProcessingParameters.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmProcessingParameters.java index 52bf8d65314..38229fc79ff 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmProcessingParameters.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmProcessingParameters.java @@ -21,7 +21,8 @@ public record OsmProcessingParameters( boolean areaVisibility, boolean platformEntriesLinking, boolean staticParkAndRide, - boolean staticBikeParkAndRide + boolean staticBikeParkAndRide, + boolean preventWalkingOnRoads ) { public OsmProcessingParameters { boardingAreaRefTags = Set.copyOf(Objects.requireNonNull(boardingAreaRefTags)); diff --git a/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java b/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java index a3c6f313799..a7e7afc3b9b 100644 --- a/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java @@ -185,6 +185,8 @@ public class BuildConfig implements OtpDataStoreConfig { public final URI stopConsolidation; + public final boolean preventWalkingOnRoads; + /** * Set all parameters from the given Jackson JSON tree, applying defaults. Supplying * MissingNode.getInstance() will cause all the defaults to be applied. This could be done @@ -612,6 +614,14 @@ that we support remote input files (cloud storage or arbitrary URLs) not all dat ) .asUri(null); + preventWalkingOnRoads = + root + .of("preventWalkingOnRoads") + .since(V2_5) + .summary("Determines whether to prevent pedestrian routing on roads or not.") + .description("True to prevent pedestrian routing on roads.") + .asBoolean(false); + osmDefaults = OsmConfig.mapOsmDefaults(root, "osmDefaults"); osm = OsmConfig.mapOsmConfig(root, "osm", osmDefaults); demDefaults = DemConfig.mapDemDefaultsConfig(root, "demDefaults"); From a8aae89ef0ada0f4a4dfad748bee405537b400a5 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 19 Nov 2024 15:47:51 -0500 Subject: [PATCH 107/111] refactor: migrate types and imports. --- .../MobilityProfileRouting.java | 4 +- .../graph_builder/module/osm/OsmModule.java | 40 +++++++++---------- .../org/opentripplanner/osm/model/OsmWay.java | 2 +- .../routing/api/request/RouteRequest.java | 3 -- .../impedance/OsmImpedanceUpdater.java | 14 +++---- .../opentripplanner/osm/model/OsmWayTest.java | 23 +++++------ 6 files changed, 41 insertions(+), 45 deletions(-) diff --git a/application/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java b/application/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java index d066d056fa9..2b2e4da01b0 100644 --- a/application/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java +++ b/application/src/ext/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRouting.java @@ -5,7 +5,7 @@ import java.util.EnumMap; import java.util.HashMap; import java.util.Map; -import org.opentripplanner.openstreetmap.model.OSMWay; +import org.opentripplanner.osm.model.OsmWay; import org.opentripplanner.street.model.StreetTraversalPermission; import org.opentripplanner.street.model.edge.StreetEdge; import org.opentripplanner.street.model.edge.TemporaryPartialStreetEdge; @@ -55,7 +55,7 @@ public static float computeTravelHours(double meters, MobilityProfile mobilityPr } public static StreetTraversalPermission adjustPedestrianPermissions( - OSMWay way, + OsmWay way, StreetTraversalPermission permissions ) { return way.isFootway() || way.isTransitPlatform() diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index dd4682bb1d8..893d0d0c745 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -61,12 +61,12 @@ public class OsmModule implements GraphBuilderModule { private final SafetyValueNormalizer normalizer; private final VertexGenerator vertexGenerator; private final OsmDatabase osmdb; - private List osmStreets; - private List osmFootways; - private OSMWay lastQueriedCrossing; - private OSMWay lastIntersectingStreetFound; - private OSMWay lastQueriedCrossingExtension; - private OSMWay lastAdjacentCrossingFound; + private List osmStreets; + private List osmFootways; + private OsmWay lastQueriedCrossing; + private OsmWay lastIntersectingStreetFound; + private OsmWay lastQueriedCrossingExtension; + private OsmWay lastAdjacentCrossingFound; private final StreetLimitationParameters streetLimitationParameters; OsmModule( @@ -523,11 +523,11 @@ private StreetEdgePair getEdgesForStreet( return new StreetEdgePair(street, backStreet); } - private String getCrossingName(OSMWay way, String defaultName) { + private String getCrossingName(OsmWay way, String defaultName) { // Scan the nodes of this way to find the intersecting street. var otherWayOpt = getIntersectingStreet(way); if (otherWayOpt.isPresent()) { - OSMWay otherWay = otherWayOpt.get(); + OsmWay otherWay = otherWayOpt.get(); if (otherWay.hasTag("name")) { return String.format("crossing over %s", otherWay.getTag("name")); } else if (otherWay.isServiceRoad()) { @@ -599,7 +599,7 @@ private StreetEdge getEdgeForStreet( seb.withName(editedName); seb.withBogusName(false); } else { - OSMWay continuedCrossing = getContinuedMarkedCrossing(way); + OsmWay continuedCrossing = getContinuedMarkedCrossing(way); if (continuedCrossing != null) { // Change the name of this segment to the name of the crossing. editedName = getCrossingName(continuedCrossing, editedName); @@ -617,7 +617,7 @@ private StreetEdge getEdgeForStreet( /** * * Obtains the correct key for this OSM way. */ - private static String getProfileKey(OSMWay way, long startShortId, long endShortId) { + private static String getProfileKey(OsmWay way, long startShortId, long endShortId) { long wayId = way.getId(); TLongList nodeRefs = way.getNodeRefs(); @@ -634,7 +634,7 @@ private static String getProfileKey(OSMWay way, long startShortId, long endShort } /** Gets the streets from a collection of OSM ways. */ - public static List getStreets(Collection ways) { + public static List getStreets(Collection ways) { return ways .stream() .filter(w -> !w.isFootway()) @@ -644,7 +644,7 @@ public static List getStreets(Collection ways) { } /** Gets the intersecting street, if any, for the given way using ways in osmdb. */ - private Optional getIntersectingStreet(OSMWay way) { + private Optional getIntersectingStreet(OsmWay way) { // Perf: If the same way is queried again, return the previously found intersecting street. if (way == lastQueriedCrossing) { return Optional.ofNullable(lastIntersectingStreetFound); @@ -655,13 +655,13 @@ private Optional getIntersectingStreet(OSMWay way) { } lastQueriedCrossing = way; - Optional intersectingStreetOptional = getIntersectingStreet(way, osmStreets); + Optional intersectingStreetOptional = getIntersectingStreet(way, osmStreets); lastIntersectingStreetFound = intersectingStreetOptional.orElse(null); return intersectingStreetOptional; } /** Gets the intersecting street, if any, for the given way and candidate streets. */ - public static Optional getIntersectingStreet(OSMWay way, List streets) { + public static Optional getIntersectingStreet(OsmWay way, List streets) { TLongList nodeRefs = way.getNodeRefs(); if (nodeRefs.size() >= 3) { // There needs to be at least three nodes: 2 extremities that are on the sidewalk, @@ -677,15 +677,15 @@ public static Optional getIntersectingStreet(OSMWay way, List st } /** Gets the footways from a collection of OSM ways. */ - public static List getFootways(Collection ways) { - return ways.stream().filter(OSMWay::isFootway).toList(); + public static List getFootways(Collection ways) { + return ways.stream().filter(OsmWay::isFootway).toList(); } /** * Determines whether a way is a continuation (connects through end nodes) of a marked crossing, * using the footways from osmdb. */ - private OSMWay getContinuedMarkedCrossing(OSMWay way) { + private OsmWay getContinuedMarkedCrossing(OsmWay way) { // Perf: If the same way is queried again, return the previously found intersecting street. if (way == lastQueriedCrossingExtension) { return lastAdjacentCrossingFound; @@ -701,11 +701,11 @@ private OSMWay getContinuedMarkedCrossing(OSMWay way) { } /** Determines whether a way is a continuation (i.e. connects through end nodes) of marked crossing. */ - public static OSMWay getContinuedMarkedCrossing(OSMWay way, Collection ways) { + public static OsmWay getContinuedMarkedCrossing(OsmWay way, Collection ways) { int adjacentWayCount = 0; - OSMWay markedCrossing = null; + OsmWay markedCrossing = null; - for (OSMWay w : ways) { + for (OsmWay w : ways) { if (way.isAdjacentTo(w)) { adjacentWayCount++; if (markedCrossing == null && w.isMarkedCrossing()) { diff --git a/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java b/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java index 2d08f7b893e..8853a53b360 100644 --- a/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java +++ b/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java @@ -178,7 +178,7 @@ public boolean isServiceRoad() { } /** Whether this way is connected to the given way through their extremities. */ - public boolean isAdjacentTo(OSMWay way) { + public boolean isAdjacentTo(OsmWay way) { long wayFirstNode = way.nodes.get(0); long wayLastNode = way.nodes.get(way.nodes.size() - 1); diff --git a/application/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java b/application/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java index 8e46055f70b..049715a35e1 100644 --- a/application/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java +++ b/application/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java @@ -15,9 +15,6 @@ import java.util.function.Consumer; import javax.annotation.Nullable; import org.opentripplanner.ext.mobilityprofile.MobilityProfile; -import org.opentripplanner.framework.collection.ListSection; -import org.opentripplanner.framework.time.DateUtils; -import org.opentripplanner.framework.tostring.ToStringBuilder; import org.opentripplanner.model.GenericLocation; import org.opentripplanner.model.plan.SortOrder; import org.opentripplanner.model.plan.paging.cursor.PageCursor; diff --git a/application/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java b/application/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java index f896c7f51e1..fb3c7d2eefe 100644 --- a/application/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java @@ -8,10 +8,10 @@ import org.opentripplanner.ext.mobilityprofile.MobilityProfileParser; import org.opentripplanner.framework.io.OtpHttpClient; import org.opentripplanner.framework.io.OtpHttpClientFactory; -import org.opentripplanner.framework.tostring.ToStringBuilder; import org.opentripplanner.updater.spi.HttpHeaders; import org.opentripplanner.updater.spi.PollingGraphUpdater; import org.opentripplanner.updater.spi.WriteToGraphCallback; +import org.opentripplanner.utils.tostring.ToStringBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,13 +40,13 @@ public OsmImpedanceUpdater(OsmImpedanceUpdaterParameters config) { } @Override - public void setGraphUpdaterManager(WriteToGraphCallback saveResultOnGraph) { - this.saveResultOnGraph = saveResultOnGraph; + public String toString() { + return ToStringBuilder.of(this.getClass()).addStr("url", url).toString(); } @Override - public String toString() { - return ToStringBuilder.of(this.getClass()).addStr("url", url).toString(); + public void setup(WriteToGraphCallback writeToGraphCallback) { + this.saveResultOnGraph = writeToGraphCallback; } @Override @@ -69,8 +69,8 @@ protected void runPolling() { // Handle update in graph writer runnable if (!changedImpedances.isEmpty()) { - saveResultOnGraph.execute((graph, transitModel) -> - updateHandler.update(graph, changedImpedances) + saveResultOnGraph.execute((rtUpdateContext) -> + updateHandler.update(rtUpdateContext.graph(), changedImpedances) ); } else { LOG.error("Impedance data unchanged (not updating graph)."); diff --git a/application/src/test/java/org/opentripplanner/osm/model/OsmWayTest.java b/application/src/test/java/org/opentripplanner/osm/model/OsmWayTest.java index 2d04a970ec8..b1fd1dcf4ea 100644 --- a/application/src/test/java/org/opentripplanner/osm/model/OsmWayTest.java +++ b/application/src/test/java/org/opentripplanner/osm/model/OsmWayTest.java @@ -9,10 +9,9 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.opentripplanner.openstreetmap.wayproperty.specifier.WayTestData; import org.opentripplanner.osm.wayproperty.specifier.WayTestData; -public class OsmWayTest { +class OsmWayTest { @Test void testIsBicycleDismountForced() { @@ -182,19 +181,19 @@ void escalator() { assertFalse(escalator.isEscalator()); } - private static OSMWay createGenericHighway() { - var osm = new OSMWay(); + private static OsmWay createGenericHighway() { + var osm = new OsmWay(); osm.addTag("highway", "primary"); return osm; } - private static OSMWay createGenericFootway() { - var osm = new OSMWay(); + private static OsmWay createGenericFootway() { + var osm = new OsmWay(); osm.addTag("highway", "footway"); return osm; } - private static OSMWay createFootway( + private static OsmWay createFootway( String footwayValue, String crossingTag, String crossingValue @@ -215,14 +214,14 @@ void footway() { void serviceRoad() { assertFalse(createGenericHighway().isServiceRoad()); - var osm2 = new OSMWay(); + var osm2 = new OsmWay(); osm2.addTag("highway", "service"); assertTrue(osm2.isServiceRoad()); } @ParameterizedTest @MethodSource("createCrossingCases") - void markedCrossing(OSMWay way, boolean result) { + void markedCrossing(OsmWay way, boolean result) { assertEquals(result, way.isMarkedCrossing()); } @@ -239,15 +238,15 @@ static Stream createCrossingCases() { ); } - private static OSMWay createPlatform(String kind) { - var osm = new OSMWay(); + private static OsmWay createPlatform(String kind) { + var osm = new OsmWay(); osm.addTag(kind, "platform"); return osm; } @ParameterizedTest @MethodSource("createTransitPlatformCases") - void transitPlatform(OSMWay way, boolean result) { + void transitPlatform(OsmWay way, boolean result) { assertEquals(result, way.isTransitPlatform()); } From 76719d87b996992296b49e233f1d77b3c8b95afa Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 19 Nov 2024 15:51:59 -0500 Subject: [PATCH 108/111] refactor(OsmImpedanceUpdater): Close HTTP resource factory after polling. --- .../updater/impedance/OsmImpedanceUpdater.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java b/application/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java index fb3c7d2eefe..ed4a7e07f2a 100644 --- a/application/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/impedance/OsmImpedanceUpdater.java @@ -25,7 +25,6 @@ public class OsmImpedanceUpdater extends PollingGraphUpdater { private final String url; private final ImpedanceUpdateHandler updateHandler; private final HttpHeaders headers; - private final OtpHttpClient otpHttpClient; private WriteToGraphCallback saveResultOnGraph; private Map previousImpedances = Map.of(); @@ -35,7 +34,6 @@ public OsmImpedanceUpdater(OsmImpedanceUpdaterParameters config) { this.headers = HttpHeaders.of().add(config.headers()).build(); this.updateHandler = new ImpedanceUpdateHandler(); - this.otpHttpClient = new OtpHttpClientFactory().create(LOG); LOG.info("Creating impedance updater running every {}: {}", pollingPeriod(), url); } @@ -52,7 +50,8 @@ public void setup(WriteToGraphCallback writeToGraphCallback) { @Override protected void runPolling() { LOG.info("Fetching mobility impedances..."); - try { + try (OtpHttpClientFactory clientFactory = new OtpHttpClientFactory()) { + OtpHttpClient otpHttpClient = clientFactory.create(LOG); final Map impedances = otpHttpClient.getAndMap( URI.create(url), this.headers.asMap(), @@ -69,8 +68,8 @@ protected void runPolling() { // Handle update in graph writer runnable if (!changedImpedances.isEmpty()) { - saveResultOnGraph.execute((rtUpdateContext) -> - updateHandler.update(rtUpdateContext.graph(), changedImpedances) + saveResultOnGraph.execute(updateContext -> + updateHandler.update(updateContext.graph(), changedImpedances) ); } else { LOG.error("Impedance data unchanged (not updating graph)."); From 334a52c15e63e0da7719060902d9dfa0f6d9ea91 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 19 Nov 2024 16:01:32 -0500 Subject: [PATCH 109/111] refactor(MobilityProfileRoutingTest): Migrate types. --- .../MobilityProfileRoutingTest.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/application/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java b/application/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java index c21811ce049..e749352e5cd 100644 --- a/application/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java +++ b/application/src/ext-test/java/org/opentripplanner/ext/mobilityprofile/MobilityProfileRoutingTest.java @@ -7,7 +7,7 @@ import java.util.EnumMap; import java.util.Map; import org.junit.jupiter.api.Test; -import org.opentripplanner.openstreetmap.model.OSMWay; +import org.opentripplanner.osm.model.OsmWay; import org.opentripplanner.street.model.StreetTraversalPermission; import org.opentripplanner.street.model.edge.StreetEdge; import org.opentripplanner.street.model.edge.StreetEdgeBuilder; @@ -33,21 +33,21 @@ void canDetectHighwayFootwayTag() { assertFalse(createServiceWay().isFootway()); } - private static OSMWay createServiceWay() { - OSMWay serviceWay = new OSMWay(); + private static OsmWay createServiceWay() { + OsmWay serviceWay = new OsmWay(); serviceWay.addTag("highway", "service"); return serviceWay; } - private static OSMWay createFootway() { - OSMWay footway = new OSMWay(); + private static OsmWay createFootway() { + OsmWay footway = new OsmWay(); footway.addTag("highway", "footway"); return footway; } @Test void canRemoveWalkPermissionOnNonFootway() { - OSMWay serviceWay = createServiceWay(); + OsmWay serviceWay = createServiceWay(); StreetTraversalPermission permissions = StreetTraversalPermission.ALL; assertEquals( StreetTraversalPermission.BICYCLE_AND_CAR, @@ -57,7 +57,7 @@ void canRemoveWalkPermissionOnNonFootway() { @Test void canPreserveWalkPermissionOnFootway() { - OSMWay footway = createFootway(); + OsmWay footway = createFootway(); StreetTraversalPermission permissions = StreetTraversalPermission.ALL; assertEquals( StreetTraversalPermission.ALL, From 42ce24f032e7ac4f1ed7e6043dc851ac197a1055 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 19 Nov 2024 16:04:57 -0500 Subject: [PATCH 110/111] refactor(OsmModuleTest): Migrate types. --- .../module/osm/OsmModuleTest.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java index 4370322f271..12c2f59884d 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java @@ -382,12 +382,12 @@ private void testBuildingAreas(boolean skipVisibility) { @Test void testGetIntersectingStreet() { - OSMWay way = new OSMWay(); + OsmWay way = new OsmWay(); way.getNodeRefs().add(new long[] { 10001, 10002, 10003, 10004 }); - OSMWay street = new OSMWay(); + OsmWay street = new OsmWay(); street.setId(50001); street.getNodeRefs().add(new long[] { 20001, 20002, 20003, 10002, 20004, 20005 }); - OSMWay otherStreet = new OSMWay(); + OsmWay otherStreet = new OsmWay(); otherStreet.setId(50002); otherStreet.getNodeRefs().add(new long[] { 30001, 30002, 30003, 30004, 30005 }); @@ -401,19 +401,19 @@ void testGetIntersectingStreet() { @Test void testGetStreets() { - OSMWay footway = new OSMWay(); + OsmWay footway = new OsmWay(); footway.addTag("highway", "footway"); - OSMWay street = new OSMWay(); + OsmWay street = new OsmWay(); street.addTag("highway", "primary"); street.addTag("name", "3rd Street"); - OSMWay serviceRoad = new OSMWay(); + OsmWay serviceRoad = new OsmWay(); serviceRoad.addTag("highway", "service"); - OSMWay otherStreet = new OSMWay(); + OsmWay otherStreet = new OsmWay(); otherStreet.addTag("highway", "trunk"); otherStreet.addTag("oneway", "true"); - OSMWay blankPath = new OSMWay(); + OsmWay blankPath = new OsmWay(); - List streets = OsmModule.getStreets( + List streets = OsmModule.getStreets( List.of(street, footway, serviceRoad, otherStreet, blankPath) ); assertEquals(3, streets.size()); @@ -422,24 +422,24 @@ void testGetStreets() { @Test void testIsContinuationOfMarkedCrossing() { - OSMWay footway = new OSMWay(); + OsmWay footway = new OsmWay(); footway.addTag("highway", "footway"); footway.getNodeRefs().add(new long[] { 10001, 10000, 10002 }); - OSMWay crossing = new OSMWay(); + OsmWay crossing = new OsmWay(); crossing.getNodeRefs().add(new long[] { 10002, 10003, 10004 }); crossing.addTag("highway", "footway"); crossing.addTag("footway", "crossing"); crossing.addTag("crossing", "marked"); - OSMWay otherCrossing = new OSMWay(); + OsmWay otherCrossing = new OsmWay(); otherCrossing.getNodeRefs().add(new long[] { 10003, 10001, 10004 }); otherCrossing.addTag("highway", "footway"); otherCrossing.addTag("footway", "crossing"); otherCrossing.addTag("crossing", "unmarked"); // If more than one footway are adjacent to the crossing, there is no continuation. - OSMWay otherFootway = new OSMWay(); + OsmWay otherFootway = new OsmWay(); otherFootway.addTag("highway", "footway"); otherFootway.getNodeRefs().add(new long[] { 10002, 10006 }); From 153366333b9028fd2f02249aaa21efce4178b5e9 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 19 Nov 2024 16:17:39 -0500 Subject: [PATCH 111/111] docs(BuildConfiguration): Update snapshot. --- doc/user/BuildConfiguration.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/user/BuildConfiguration.md b/doc/user/BuildConfiguration.md index a5a30826cc9..639f961c90c 100644 --- a/doc/user/BuildConfiguration.md +++ b/doc/user/BuildConfiguration.md @@ -37,6 +37,7 @@ Sections follow that describe particular settings in more depth. | [osmCacheDataInMem](#osmCacheDataInMem) | `boolean` | If OSM data should be cached in memory during processing. | *Optional* | `false` | 2.0 | | [osmNaming](#osmNaming) | `enum` | A custom OSM namer to use. | *Optional* | `"default"` | 1.5 | | platformEntriesLinking | `boolean` | Link unconnected entries to public transport platforms. | *Optional* | `false` | 2.0 | +| [preventWalkingOnRoads](#preventWalkingOnRoads) | `boolean` | Determines whether to prevent pedestrian routing on roads or not. | *Optional* | `false` | 2.5 | | [readCachedElevations](#readCachedElevations) | `boolean` | Whether to read cached elevation data. | *Optional* | `true` | 2.0 | | staticBikeParkAndRide | `boolean` | Whether we should create bike P+R stations from OSM data. | *Optional* | `false` | 1.5 | | staticParkAndRide | `boolean` | Whether we should create car P+R stations from OSM data. | *Optional* | `true` | 1.5 |