diff --git a/application/src/ext-test/java/org/opentripplanner/ext/fares/impl/GtfsFaresV2ServiceTest.java b/application/src/ext-test/java/org/opentripplanner/ext/fares/impl/GtfsFaresV2ServiceTest.java index 19b55489778..a7b2694e2e8 100644 --- a/application/src/ext-test/java/org/opentripplanner/ext/fares/impl/GtfsFaresV2ServiceTest.java +++ b/application/src/ext-test/java/org/opentripplanner/ext/fares/impl/GtfsFaresV2ServiceTest.java @@ -12,7 +12,7 @@ import java.util.Set; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.opentripplanner.ext.fares.model.Distance; +import org.opentripplanner.transit.model.basic.Distance; import org.opentripplanner.ext.fares.model.FareDistance; import org.opentripplanner.ext.fares.model.FareDistance.LinearDistance; import org.opentripplanner.ext.fares.model.FareLegRule; diff --git a/application/src/ext/java/org/opentripplanner/ext/fares/model/FareDistance.java b/application/src/ext/java/org/opentripplanner/ext/fares/model/FareDistance.java index 9c18e3947b3..bbbc5e64426 100644 --- a/application/src/ext/java/org/opentripplanner/ext/fares/model/FareDistance.java +++ b/application/src/ext/java/org/opentripplanner/ext/fares/model/FareDistance.java @@ -1,5 +1,7 @@ package org.opentripplanner.ext.fares.model; +import org.opentripplanner.transit.model.basic.Distance; + /** Represents a distance metric used in distance-based fare computation*/ public sealed interface FareDistance { /** Represents the number of stops as a distance metric in fare computation */ diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RentalVehicleImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RentalVehicleImpl.java index c4fb92c0ef4..5697aa15a5f 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RentalVehicleImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RentalVehicleImpl.java @@ -4,6 +4,7 @@ import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.service.vehiclerental.model.RentalVehicleFuel; import org.opentripplanner.service.vehiclerental.model.RentalVehicleType; import org.opentripplanner.service.vehiclerental.model.VehicleRentalStationUris; import org.opentripplanner.service.vehiclerental.model.VehicleRentalSystem; @@ -16,6 +17,11 @@ public DataFetcher allowPickupNow() { return environment -> getSource(environment).allowPickupNow(); } + @Override + public DataFetcher fuel() { + return environment -> getSource(environment).getFuel(); + } + @Override public DataFetcher id() { return environment -> diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java index 9c95ea65e91..091e0be59f1 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -58,6 +58,7 @@ import org.opentripplanner.service.vehicleparking.model.VehicleParkingSpaces; import org.opentripplanner.service.vehicleparking.model.VehicleParkingState; import org.opentripplanner.service.vehiclerental.model.RentalVehicleEntityCounts; +import org.opentripplanner.service.vehiclerental.model.RentalVehicleFuel; import org.opentripplanner.service.vehiclerental.model.RentalVehicleType; import org.opentripplanner.service.vehiclerental.model.RentalVehicleTypeCount; import org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace; @@ -897,6 +898,8 @@ public interface GraphQLRentalPlace extends TypeResolver {} public interface GraphQLRentalVehicle { public DataFetcher allowPickupNow(); + public DataFetcher fuel(); + public DataFetcher id(); public DataFetcher lat(); @@ -924,6 +927,13 @@ public interface GraphQLRentalVehicleEntityCounts { public DataFetcher total(); } + /** Rental vehicle fuel represent the current status of the battery or fuel of a rental vehicle */ + public interface GraphQLRentalVehicleFuel { + public DataFetcher percent(); + + public DataFetcher range(); + } + public interface GraphQLRentalVehicleType { public DataFetcher formFactor(); diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml index a9bb87a6ea5..3272efa894f 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml @@ -134,4 +134,5 @@ config: CallRealTime: org.opentripplanner.apis.gtfs.model.CallRealTime#CallRealTime RentalPlace: org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace#VehicleRentalPlace CallSchedule: org.opentripplanner.apis.gtfs.model.CallSchedule#CallSchedule + RentalVehicleFuel: org.opentripplanner.service.vehiclerental.model.RentalVehicleFuel#RentalVehicleFuel diff --git a/application/src/main/java/org/opentripplanner/apis/transmodel/model/stop/RentalVehicleType.java b/application/src/main/java/org/opentripplanner/apis/transmodel/model/stop/RentalVehicleType.java index e44639e09e7..8694fe0d95d 100644 --- a/application/src/main/java/org/opentripplanner/apis/transmodel/model/stop/RentalVehicleType.java +++ b/application/src/main/java/org/opentripplanner/apis/transmodel/model/stop/RentalVehicleType.java @@ -71,7 +71,7 @@ public static GraphQLObjectType create( .name("currentRangeMeters") .type(Scalars.GraphQLFloat) .dataFetcher(environment -> - ((VehicleRentalVehicle) environment.getSource()).currentRangeMeters + ((VehicleRentalVehicle) environment.getSource()).getFuel().getRange() ) .build() ) diff --git a/application/src/main/java/org/opentripplanner/gtfs/mapping/FareLegRuleMapper.java b/application/src/main/java/org/opentripplanner/gtfs/mapping/FareLegRuleMapper.java index 2218be9cf30..e24aa300406 100644 --- a/application/src/main/java/org/opentripplanner/gtfs/mapping/FareLegRuleMapper.java +++ b/application/src/main/java/org/opentripplanner/gtfs/mapping/FareLegRuleMapper.java @@ -4,10 +4,10 @@ import java.util.Collection; import java.util.Objects; -import org.opentripplanner.ext.fares.model.Distance; import org.opentripplanner.ext.fares.model.FareDistance; import org.opentripplanner.ext.fares.model.FareLegRule; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; +import org.opentripplanner.transit.model.basic.Distance; import org.opentripplanner.transit.model.framework.FeedScopedId; public final class FareLegRuleMapper { diff --git a/application/src/main/java/org/opentripplanner/service/vehiclerental/model/RentalVehicleFuel.java b/application/src/main/java/org/opentripplanner/service/vehiclerental/model/RentalVehicleFuel.java new file mode 100644 index 00000000000..2e24613f49d --- /dev/null +++ b/application/src/main/java/org/opentripplanner/service/vehiclerental/model/RentalVehicleFuel.java @@ -0,0 +1,44 @@ +package org.opentripplanner.service.vehiclerental.model; + +import javax.annotation.Nullable; +import org.opentripplanner.transit.model.basic.Distance; +import org.opentripplanner.transit.model.basic.Ratio; + +/** + * Contains information about the current battery or fuel status. + * See the GBFS + * vehicle_status specification for more details. + */ +public class RentalVehicleFuel { + + /** + * Current fuel percentage, expressed from 0 to 1. + *

+ * May be {@code null}. + */ + @Nullable + public final Ratio percent; + + /** + * Distance that the vehicle can travel with the current charge or fuel. + *

+ * May be {@code null}. + */ + @Nullable + public final Distance range; + + public RentalVehicleFuel(@Nullable Ratio fuelPercent, @Nullable Distance range) { + this.percent = fuelPercent; + this.range = range; + } + + @Nullable + public Double getPercent() { + return this.percent != null ? this.percent.ratio : null; + } + + @Nullable + public Integer getRange() { + return this.range != null ? this.range.toMeters() : null; + } +} diff --git a/application/src/main/java/org/opentripplanner/service/vehiclerental/model/VehicleRentalVehicle.java b/application/src/main/java/org/opentripplanner/service/vehiclerental/model/VehicleRentalVehicle.java index 042e608c88f..446711bad36 100644 --- a/application/src/main/java/org/opentripplanner/service/vehiclerental/model/VehicleRentalVehicle.java +++ b/application/src/main/java/org/opentripplanner/service/vehiclerental/model/VehicleRentalVehicle.java @@ -22,9 +22,9 @@ public class VehicleRentalVehicle implements VehicleRentalPlace { public boolean isReserved = false; public boolean isDisabled = false; public Instant lastReported; - public Double currentRangeMeters; public VehicleRentalStation station; public String pricingPlanId; + public RentalVehicleFuel fuel; @Override public FeedScopedId getId() { @@ -133,4 +133,8 @@ public VehicleRentalStationUris getRentalUris() { public VehicleRentalSystem getVehicleRentalSystem() { return system; } + + public RentalVehicleFuel getFuel() { + return fuel; + } } diff --git a/application/src/ext/java/org/opentripplanner/ext/fares/model/Distance.java b/application/src/main/java/org/opentripplanner/transit/model/basic/Distance.java similarity index 59% rename from application/src/ext/java/org/opentripplanner/ext/fares/model/Distance.java rename to application/src/main/java/org/opentripplanner/transit/model/basic/Distance.java index f30712d4cad..fef694ec023 100644 --- a/application/src/ext/java/org/opentripplanner/ext/fares/model/Distance.java +++ b/application/src/main/java/org/opentripplanner/transit/model/basic/Distance.java @@ -1,5 +1,6 @@ -package org.opentripplanner.ext.fares.model; +package org.opentripplanner.transit.model.basic; +import java.util.Objects; import org.opentripplanner.utils.tostring.ValueObjectToStringBuilder; public class Distance { @@ -8,23 +9,35 @@ public class Distance { private final double meters; /** Returns a Distance object representing the given number of meters */ - public Distance(double value) { - this.meters = value; + public Distance(double distanceInMeters) { + if (distanceInMeters < 0) { + throw new IllegalArgumentException("Distance cannot be negative"); + } + + this.meters = distanceInMeters; } /** Returns a Distance object representing the given number of meters */ - public static Distance ofMeters(double value) { + public static Distance ofMeters(double value) throws IllegalArgumentException { + if (value < 0) { + throw new IllegalArgumentException("Distance cannot be negative"); + } + return new Distance(value); } /** Returns a Distance object representing the given number of kilometers */ public static Distance ofKilometers(double value) { + if (value < 0) { + throw new IllegalArgumentException("Distance cannot be negative"); + } + return new Distance(value * METERS_PER_KM); } /** Returns the distance in meters */ - public double toMeters() { - return this.meters; + public int toMeters() { + return (int) Math.round(this.meters); } @Override @@ -36,6 +49,11 @@ public boolean equals(Object other) { } } + @Override + public int hashCode() { + return Objects.hash(meters, "Distance"); + } + @Override public String toString() { if (meters < METERS_PER_KM) { diff --git a/application/src/main/java/org/opentripplanner/transit/model/basic/Ratio.java b/application/src/main/java/org/opentripplanner/transit/model/basic/Ratio.java new file mode 100644 index 00000000000..0c980be6eee --- /dev/null +++ b/application/src/main/java/org/opentripplanner/transit/model/basic/Ratio.java @@ -0,0 +1,35 @@ +package org.opentripplanner.transit.model.basic; + +import java.util.Objects; + +public class Ratio { + + public final Double ratio; + + public Ratio(Double ratio) throws IllegalArgumentException { + if (ratio < 0d || ratio > 1d) { + throw new IllegalArgumentException("Ratio must be in range [0,1]"); + } + + this.ratio = ratio; + } + + @Override + public boolean equals(Object other) { + if (other instanceof Ratio ratio) { + return ratio.ratio == this.ratio; + } else { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.ratio, "Ratio"); + } + + @Override + public String toString() { + return this.ratio.toString(); + } +} diff --git a/application/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFreeVehicleStatusMapper.java b/application/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFreeVehicleStatusMapper.java index 959521e017e..f2fd9315dd3 100644 --- a/application/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFreeVehicleStatusMapper.java +++ b/application/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFreeVehicleStatusMapper.java @@ -9,14 +9,21 @@ import org.mobilitydata.gbfs.v2_3.free_bike_status.GBFSBike; import org.mobilitydata.gbfs.v2_3.free_bike_status.GBFSRentalUris; import org.opentripplanner.framework.i18n.NonLocalizedString; +import org.opentripplanner.service.vehiclerental.model.RentalVehicleFuel; import org.opentripplanner.service.vehiclerental.model.RentalVehicleType; import org.opentripplanner.service.vehiclerental.model.VehicleRentalStationUris; import org.opentripplanner.service.vehiclerental.model.VehicleRentalSystem; import org.opentripplanner.service.vehiclerental.model.VehicleRentalVehicle; +import org.opentripplanner.transit.model.basic.Distance; +import org.opentripplanner.transit.model.basic.Ratio; import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class GbfsFreeVehicleStatusMapper { + private static final Logger LOG = LoggerFactory.getLogger(GbfsFreeVehicleStatusMapper.class); + private final VehicleRentalSystem system; private final Map vehicleTypes; @@ -52,7 +59,40 @@ public VehicleRentalVehicle mapFreeVehicleStatus(GBFSBike vehicle) { vehicle.getLastReported() != null ? Instant.ofEpochSecond((long) (double) vehicle.getLastReported()) : null; - rentalVehicle.currentRangeMeters = vehicle.getCurrentRangeMeters(); + Ratio fuelPercent = null; + try { + fuelPercent = new Ratio(vehicle.getCurrentFuelPercent()); + } catch (IllegalArgumentException e) { + LOG.warn( + "Current fuel percent value not valid: {} - {}", + vehicle.getCurrentFuelPercent(), + e.getMessage() + ); + } catch (NullPointerException e) {} + Distance rangeMeters = null; + try { + rangeMeters = + vehicle.getCurrentRangeMeters() != null + ? Distance.ofMeters(vehicle.getCurrentRangeMeters()) + : null; + } catch (IllegalArgumentException e) { + LOG.warn( + "Current range meter value not valid: {} - {}", + vehicle.getCurrentRangeMeters(), + e.getMessage() + ); + } + // if the propulsion type has an engine current_range_meters is required + if ( + vehicle.getVehicleTypeId() != null && + vehicleTypes.get(vehicle.getVehicleTypeId()) != null && + vehicleTypes.get(vehicle.getVehicleTypeId()).propulsionType != + RentalVehicleType.PropulsionType.HUMAN && + rangeMeters == null + ) { + return null; + } + rentalVehicle.fuel = new RentalVehicleFuel(fuelPercent, rangeMeters); rentalVehicle.pricingPlanId = vehicle.getPricingPlanId(); GBFSRentalUris rentalUris = vehicle.getRentalUris(); if (rentalUris != null) { diff --git a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 537d9b680b4..8825f0fbf54 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -1874,6 +1874,8 @@ type RealTimeEstimate { type RentalVehicle implements Node & PlaceInterface { "If true, vehicle is currently available for renting." allowPickupNow: Boolean + "Fuel or battery status of the rental vehicle" + fuel: RentalVehicleFuel "Global object ID provided by Relay. This value can be used to refetch this object using **node** query." id: ID! "Latitude of the vehicle (WGS 84)" @@ -1903,6 +1905,14 @@ type RentalVehicleEntityCounts { total: Int! } +"Rental vehicle fuel represent the current status of the battery or fuel of a rental vehicle" +type RentalVehicleFuel { + "Fuel or battery power remaining in the vehicle. Expressed from 0 to 1." + percent: Ratio + "Range in meters that the vehicle can travel with the current charge or fuel." + range: Int +} + type RentalVehicleType { "The vehicle's general form factor" formFactor: FormFactor diff --git a/application/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java b/application/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java index 7e1bf24287a..d10c56aee7f 100644 --- a/application/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java +++ b/application/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java @@ -133,10 +133,17 @@ class GraphQLIntegrationTest { .withSystem("Network-1", "https://foo.bar") .build(); - private static final VehicleRentalVehicle RENTAL_VEHICLE = new TestFreeFloatingRentalVehicleBuilder() + private static final VehicleRentalVehicle RENTAL_VEHICLE_1 = new TestFreeFloatingRentalVehicleBuilder() .withSystem("Network-1", "https://foo.bar") .build(); + private static final VehicleRentalVehicle RENTAL_VEHICLE_2 = new TestFreeFloatingRentalVehicleBuilder() + .withSystem("Network-2", "https://foo.bar.baz") + .withNetwork("Network-2") + .withCurrentRangeMeters(null) + .withCurrentFuelPercent(null) + .build(); + static final Graph GRAPH = new Graph(); static final Instant ALERT_START_TIME = OffsetDateTime @@ -344,7 +351,8 @@ public Set findRoutes(StopLocation stop) { DefaultVehicleRentalService defaultVehicleRentalService = new DefaultVehicleRentalService(); defaultVehicleRentalService.addVehicleRentalStation(VEHICLE_RENTAL_STATION); - defaultVehicleRentalService.addVehicleRentalStation(RENTAL_VEHICLE); + defaultVehicleRentalService.addVehicleRentalStation(RENTAL_VEHICLE_1); + defaultVehicleRentalService.addVehicleRentalStation(RENTAL_VEHICLE_2); context = new GraphQLRequestContext( @@ -511,7 +519,7 @@ public List findClosestPlaces( return List.of( new PlaceAtDistance(stop, 0), new PlaceAtDistance(VEHICLE_RENTAL_STATION, 30), - new PlaceAtDistance(RENTAL_VEHICLE, 50) + new PlaceAtDistance(RENTAL_VEHICLE_1, 50) ); } }; diff --git a/application/src/test/java/org/opentripplanner/gtfs/mapping/FareLegRuleMapperTest.java b/application/src/test/java/org/opentripplanner/gtfs/mapping/FareLegRuleMapperTest.java index dbe833a7aaa..890da8264f4 100644 --- a/application/src/test/java/org/opentripplanner/gtfs/mapping/FareLegRuleMapperTest.java +++ b/application/src/test/java/org/opentripplanner/gtfs/mapping/FareLegRuleMapperTest.java @@ -12,11 +12,11 @@ import org.onebusaway.gtfs.model.FareLegRule; import org.onebusaway.gtfs.model.FareMedium; import org.onebusaway.gtfs.model.FareProduct; -import org.opentripplanner.ext.fares.model.Distance; import org.opentripplanner.ext.fares.model.FareDistance; import org.opentripplanner.ext.fares.model.FareDistance.LinearDistance; import org.opentripplanner.ext.fares.model.FareDistance.Stops; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; +import org.opentripplanner.transit.model.basic.Distance; class FareLegRuleMapperTest { diff --git a/application/src/test/java/org/opentripplanner/routing/core/DistanceTest.java b/application/src/test/java/org/opentripplanner/routing/core/DistanceTest.java new file mode 100644 index 00000000000..2543eae70cd --- /dev/null +++ b/application/src/test/java/org/opentripplanner/routing/core/DistanceTest.java @@ -0,0 +1,31 @@ +package org.opentripplanner.routing.core; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +import org.junit.jupiter.api.Test; +import org.opentripplanner.transit.model.basic.Distance; + +public class DistanceTest { + + private static final Distance ONE_THOUSAND_FIVE_HUNDRED_METERS = Distance.ofMeters(1500d); + private static final Distance ONE_POINT_FIVE_KILOMETERS = Distance.ofKilometers(1.5d); + private static final Distance TWO_KILOMETERS = Distance.ofKilometers(2d); + private static final Distance ONE_HUNDRED_METERS = Distance.ofMeters(100d); + private static final Distance POINT_ONE_KILOMETER = Distance.ofKilometers(0.1d); + private static final Distance ONE_HUNDRED_POINT_FIVE_METERS = Distance.ofMeters(100.5d); + + @Test + void equals() { + assertEquals(ONE_THOUSAND_FIVE_HUNDRED_METERS, ONE_POINT_FIVE_KILOMETERS); + assertEquals(POINT_ONE_KILOMETER, ONE_HUNDRED_METERS); + assertNotEquals(ONE_HUNDRED_POINT_FIVE_METERS, ONE_HUNDRED_METERS); + assertNotEquals(TWO_KILOMETERS, ONE_POINT_FIVE_KILOMETERS); + } + + @Test + void testHashCode() { + assertEquals(Distance.ofMeters(5d).hashCode(), Distance.ofMeters(5d).hashCode()); + assertNotEquals(Distance.ofMeters(5d).hashCode(), Double.valueOf(5d).hashCode()); + } +} diff --git a/application/src/test/java/org/opentripplanner/routing/core/RatioTest.java b/application/src/test/java/org/opentripplanner/routing/core/RatioTest.java new file mode 100644 index 00000000000..4195fc5c5c0 --- /dev/null +++ b/application/src/test/java/org/opentripplanner/routing/core/RatioTest.java @@ -0,0 +1,42 @@ +package org.opentripplanner.routing.core; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; +import org.opentripplanner.transit.model.basic.Ratio; + +public class RatioTest { + + private static final Double HALF = 0.5d; + private static final Double ZERO = 0d; + private static final Double ONE = 1d; + private static final Double TOO_HIGH = 1.1d; + private static final Double TOO_LOW = -1.1d; + + @Test + void validRatios() { + assertDoesNotThrow(() -> new Ratio(HALF)); + assertDoesNotThrow(() -> new Ratio(ZERO)); + assertDoesNotThrow(() -> new Ratio(ONE)); + } + + @Test + void invalidRatios() { + assertThrows(IllegalArgumentException.class, () -> new Ratio(TOO_HIGH)); + assertThrows(IllegalArgumentException.class, () -> new Ratio(TOO_LOW)); + } + + @Test + void testHashCode() { + Ratio half = new Ratio(HALF); + + Ratio half2 = new Ratio(HALF); + assertEquals(half.hashCode(), half2.hashCode()); + + Double halfDouble = 2d; + assertNotEquals(half.hashCode(), halfDouble); + } +} diff --git a/application/src/test/java/org/opentripplanner/service/vehiclerental/model/TestFreeFloatingRentalVehicleBuilder.java b/application/src/test/java/org/opentripplanner/service/vehiclerental/model/TestFreeFloatingRentalVehicleBuilder.java index a9b2398f686..85dd57bfc1d 100644 --- a/application/src/test/java/org/opentripplanner/service/vehiclerental/model/TestFreeFloatingRentalVehicleBuilder.java +++ b/application/src/test/java/org/opentripplanner/service/vehiclerental/model/TestFreeFloatingRentalVehicleBuilder.java @@ -1,7 +1,10 @@ package org.opentripplanner.service.vehiclerental.model; +import javax.annotation.Nullable; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.street.model.RentalFormFactor; +import org.opentripplanner.transit.model.basic.Distance; +import org.opentripplanner.transit.model.basic.Ratio; import org.opentripplanner.transit.model.framework.FeedScopedId; public class TestFreeFloatingRentalVehicleBuilder { @@ -9,10 +12,15 @@ public class TestFreeFloatingRentalVehicleBuilder { public static final String NETWORK_1 = "Network-1"; public static final double DEFAULT_LATITUDE = 47.520; public static final double DEFAULT_LONGITUDE = 19.01; + public static final double DEFAULT_CURRENT_FUEL_PERCENT = 0.5; + public static final double DEFAULT_CURRENT_RANGE_METERS = 5500.7; private double latitude = DEFAULT_LATITUDE; private double longitude = DEFAULT_LONGITUDE; + private Ratio currentFuelPercent = new Ratio(DEFAULT_CURRENT_FUEL_PERCENT); + private Double currentRangeMeters = DEFAULT_CURRENT_RANGE_METERS; private VehicleRentalSystem system = null; + private String network = NETWORK_1; private RentalVehicleType vehicleType = RentalVehicleType.getDefaultType(NETWORK_1); @@ -30,6 +38,27 @@ public TestFreeFloatingRentalVehicleBuilder withLongitude(double longitude) { return this; } + public TestFreeFloatingRentalVehicleBuilder withCurrentFuelPercent( + @Nullable Double currentFuelPercent + ) { + if (currentFuelPercent == null) { + this.currentFuelPercent = null; + } else { + this.currentFuelPercent = new Ratio(currentFuelPercent); + } + return this; + } + + public TestFreeFloatingRentalVehicleBuilder withCurrentRangeMeters(Double currentRangeMeters) { + this.currentRangeMeters = currentRangeMeters; + return this; + } + + public TestFreeFloatingRentalVehicleBuilder withNetwork(String network) { + this.network = network; + return this; + } + public TestFreeFloatingRentalVehicleBuilder withSystem(String id, String url) { this.system = new VehicleRentalSystem( @@ -64,6 +93,23 @@ public TestFreeFloatingRentalVehicleBuilder withVehicleCar() { return buildVehicleType(RentalFormFactor.CAR); } + public VehicleRentalVehicle build() { + var vehicle = new VehicleRentalVehicle(); + var stationName = "free-floating-" + vehicleType.formFactor.name().toLowerCase(); + vehicle.id = new FeedScopedId(this.network, stationName); + vehicle.name = new NonLocalizedString(stationName); + vehicle.latitude = latitude; + vehicle.longitude = longitude; + vehicle.vehicleType = vehicleType; + vehicle.system = system; + vehicle.fuel = + new RentalVehicleFuel( + currentFuelPercent, + currentRangeMeters != null ? Distance.ofMeters(currentRangeMeters) : null + ); + return vehicle; + } + private TestFreeFloatingRentalVehicleBuilder buildVehicleType(RentalFormFactor rentalFormFactor) { this.vehicleType = new RentalVehicleType( @@ -75,16 +121,4 @@ private TestFreeFloatingRentalVehicleBuilder buildVehicleType(RentalFormFactor r ); return this; } - - public VehicleRentalVehicle build() { - var vehicle = new VehicleRentalVehicle(); - var stationName = "free-floating-" + vehicleType.formFactor.name().toLowerCase(); - vehicle.id = new FeedScopedId(NETWORK_1, stationName); - vehicle.name = new NonLocalizedString(stationName); - vehicle.latitude = latitude; - vehicle.longitude = longitude; - vehicle.vehicleType = vehicleType; - vehicle.system = system; - return vehicle; - } } diff --git a/application/src/test/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFreeVehicleStatusMapperTest.java b/application/src/test/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFreeVehicleStatusMapperTest.java index 02295ce09e9..79322d325fc 100644 --- a/application/src/test/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFreeVehicleStatusMapperTest.java +++ b/application/src/test/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFreeVehicleStatusMapperTest.java @@ -37,7 +37,7 @@ class GbfsFreeVehicleStatusMapperTest { new FeedScopedId("1", "scooter"), "Scooter", RentalFormFactor.SCOOTER, - null, + RentalVehicleType.PropulsionType.COMBUSTION, null ) ) @@ -62,7 +62,6 @@ void withDefaultType() { bike.setLon(1d); bike.setVehicleTypeId("bike"); var mapped = MAPPER.mapFreeVehicleStatus(bike); - assertEquals("Default vehicle type", mapped.name.toString()); } @@ -73,6 +72,7 @@ void withType() { bike.setLat(1d); bike.setLon(1d); bike.setVehicleTypeId("scooter"); + bike.setCurrentRangeMeters(2000d); var mapped = MAPPER.mapFreeVehicleStatus(bike); assertEquals("Scooter", mapped.name.toString()); diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/rental-vehicle.json b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/rental-vehicle.json index bcff74d0413..7d739570a9d 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/rental-vehicle.json +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/rental-vehicle.json @@ -15,6 +15,10 @@ "rentalNetwork": { "networkId": "Network-1", "url": "https://foo.bar" + }, + "fuel": { + "percent": 0.5, + "range": 5501 } } } diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/vehicle-rentals-bybbox.json b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/vehicle-rentals-bybbox.json index d28e62f8d93..10b7924d745 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/vehicle-rentals-bybbox.json +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/vehicle-rentals-bybbox.json @@ -76,6 +76,32 @@ "rentalNetwork": { "networkId": "Network-1", "url": "https://foo.bar" + }, + "fuel": { + "percent": 0.5, + "range": 5501 + } + }, + { + "__typename": "RentalVehicle", + "vehicleId": "Network-2:free-floating-bicycle", + "name": "free-floating-bicycle", + "allowPickupNow": true, + "lon": 19.01, + "lat": 47.52, + "rentalUris": null, + "operative": true, + "vehicleType": { + "formFactor": "BICYCLE", + "propulsionType": "HUMAN" + }, + "rentalNetwork": { + "networkId": "Network-2", + "url": "https://foo.bar.baz" + }, + "fuel": { + "percent": null, + "range": null } } ] diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/rental-vehicle.graphql b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/rental-vehicle.graphql index 9a912781c56..8f32632abc0 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/rental-vehicle.graphql +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/rental-vehicle.graphql @@ -19,5 +19,9 @@ networkId url } + fuel { + percent + range + } } } diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/vehicle-rentals-bybbox.graphql b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/vehicle-rentals-bybbox.graphql index 26209f427f9..55c71954692 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/vehicle-rentals-bybbox.graphql +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/vehicle-rentals-bybbox.graphql @@ -26,6 +26,10 @@ networkId url } + fuel { + percent + range + } } ... on VehicleRentalStation { stationId