Skip to content

Commit

Permalink
return constraint support, allow roundtrip for direct connections
Browse files Browse the repository at this point in the history
  • Loading branch information
pablohoch committed Nov 29, 2024
1 parent dc7eeea commit 1a1545b
Show file tree
Hide file tree
Showing 16 changed files with 116 additions and 25 deletions.
1 change: 1 addition & 0 deletions include/motis/gbfs/data.h
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ struct provider_products {
std::vector<vehicle_type_idx_t> vehicle_types_;
vehicle_form_factor form_factor_{vehicle_form_factor::kBicycle};
propulsion_type propulsion_type_{propulsion_type::kHuman};
return_constraint return_constraint_{return_constraint::kNone};

bool has_vehicles_to_rent_{};
};
Expand Down
2 changes: 2 additions & 0 deletions include/motis/gbfs/mode.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ vehicle_form_factor from_api_form_factor(api::RentalFormFactorEnum);
api::RentalPropulsionTypeEnum to_api_propulsion_type(propulsion_type);
propulsion_type from_api_propulsion_type(api::RentalPropulsionTypeEnum);

api::RentalReturnConstraintEnum to_api_return_constraint(return_constraint);

bool products_match(
provider_products const& prod,
std::optional<std::vector<api::RentalFormFactorEnum>> const& form_factors,
Expand Down
9 changes: 9 additions & 0 deletions openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1403,6 +1403,13 @@ components:
- PLUG_IN_HYBRID
- HYDROGEN_FUEL_CELL

RentalReturnConstraint:
type: string
enum:
- NONE
- ANY_STATION
- ROUNDTRIP_STATION

Rental:
description: Vehicle rental
type: object
Expand Down Expand Up @@ -1434,6 +1441,8 @@ components:
$ref: '#/components/schemas/RentalFormFactor'
propulsionType:
$ref: '#/components/schemas/RentalPropulsionType'
returnConstraint:
$ref: '#/components/schemas/RentalReturnConstraint'

Leg:
type: object
Expand Down
4 changes: 3 additions & 1 deletion src/endpoints/routing.cc
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,9 @@ std::vector<n::routing::offset> routing::get_offsets(
}
auto provider_rd = std::shared_ptr<gbfs::provider_routing_data>{};
for (auto const& prod : provider->products_) {
if (!gbfs::products_match(prod, form_factors, propulsion_types)) {
if (prod.return_constraint_ ==
gbfs::return_constraint::kRoundtripStation ||
!gbfs::products_match(prod, form_factors, propulsion_types)) {
continue;
}
if (!provider_rd) {
Expand Down
12 changes: 12 additions & 0 deletions src/gbfs/mode.cc
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,18 @@ propulsion_type from_api_propulsion_type(
throw utl::fail("invalid rental propulsion type");
}

api::RentalReturnConstraintEnum to_api_return_constraint(
return_constraint const rc) {
switch (rc) {
case return_constraint::kNone: return api::RentalReturnConstraintEnum::NONE;
case return_constraint::kAnyStation:
return api::RentalReturnConstraintEnum::ANY_STATION;
case return_constraint::kRoundtripStation:
return api::RentalReturnConstraintEnum::ROUNDTRIP_STATION;
}
std::unreachable();
}

bool products_match(
provider_products const& prod,
std::optional<std::vector<api::RentalFormFactorEnum>> const& form_factors,
Expand Down
7 changes: 6 additions & 1 deletion src/gbfs/osr_mapping.cc
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ struct osr_mapping {
break;
}

if (prod.return_constraint_ == return_constraint::kAnyStation) {
default_restrictions.station_parking_ = true;
}

if (default_restrictions.ride_end_allowed_ &&
!default_restrictions.station_parking_) {
rd.end_allowed_.one_out();
Expand Down Expand Up @@ -242,7 +246,8 @@ struct osr_mapping {
for (auto const [vehicle_idx, vs] :
utl::enumerate(provider_.vehicle_status_)) {
if (vs.is_disabled_ || vs.is_reserved_ || !vs.station_id_.empty() ||
!vs.home_station_id_.empty() ||
(!vs.home_station_id_.empty() &&
prod.return_constraint_ != return_constraint::kRoundtripStation) ||
!prod.includes_vehicle_type(vs.vehicle_type_idx_)) {
continue;
}
Expand Down
12 changes: 5 additions & 7 deletions src/gbfs/parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ void load_station_status(gbfs_provider& provider, json::value const& root) {
auto const& vta = station_obj.at("vehicle_types_available").as_array();
auto unrestricted_available = 0U;
auto any_station_available = 0U;
auto roundtrip_available = 0U;
for (auto const& vt : vta) {
auto const vehicle_type_id =
static_cast<std::string>(vt.at("vehicle_type_id").as_string());
Expand All @@ -239,12 +240,14 @@ void load_station_status(gbfs_provider& provider, json::value const& root) {
provider.vehicle_types_[vehicle_type_idx].return_constraint_) {
case return_constraint::kNone: ++unrestricted_available; break;
case return_constraint::kAnyStation: ++any_station_available; break;
case return_constraint::kRoundtripStation: break;
case return_constraint::kRoundtripStation:
++roundtrip_available;
break;
}
}
}
station.status_.num_vehicles_available_ =
unrestricted_available + any_station_available;
unrestricted_available + any_station_available + roundtrip_available;
}

if (station_obj.contains("vehicle_docks_available")) {
Expand Down Expand Up @@ -364,11 +367,6 @@ void load_vehicle_status(gbfs_provider& provider, json::value const& root) {
if (auto const it = provider.vehicle_types_map_.find(type_id);
it != end(provider.vehicle_types_map_)) {
type_idx = it->second;
if (provider.vehicle_types_[it->second].return_constraint_ ==
return_constraint::kRoundtripStation) {
// roundtrip vehicles currently not supported
continue;
}
}

provider.vehicle_status_.emplace_back(vehicle_status{
Expand Down
21 changes: 15 additions & 6 deletions src/gbfs/update.cc
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,16 @@ struct gbfs_update {
part.refine(vt_indices);
}

// refine by return constraints
auto by_return_constraint =
hash_map<return_constraint, std::vector<vehicle_type_idx_t>>{};
for (auto const& vt : provider.vehicle_types_) {
by_return_constraint[vt.return_constraint_].push_back(vt.idx_);
}
for (auto const& [_, vt_indices] : by_return_constraint) {
part.refine(vt_indices);
}

// refine by return stations
// TODO: only do this if the station is not in a zone where vehicles
// can be returned anywhere
Expand All @@ -457,12 +467,11 @@ struct gbfs_update {
auto& prod = provider.products_.emplace_back();
prod.idx_ = prod_idx;
prod.vehicle_types_ = set;
prod.form_factor_ =
provider.vehicle_types_.at(prod.vehicle_types_.front())
.form_factor_;
prod.propulsion_type_ =
provider.vehicle_types_.at(prod.vehicle_types_.front())
.propulsion_type_;
auto const first_vt =
provider.vehicle_types_.at(prod.vehicle_types_.front());
prod.form_factor_ = first_vt.form_factor_;
prod.propulsion_type_ = first_vt.propulsion_type_;
prod.return_constraint_ = first_vt.return_constraint_;
prod.has_vehicles_to_rent_ =
utl::any_of(provider.stations_,
[&](auto const& st) {
Expand Down
4 changes: 3 additions & 1 deletion src/street_routing.cc
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,9 @@ struct sharing {
.url_ = provider_.sys_info_.url_,
.formFactor_ = gbfs::to_api_form_factor(products_.form_factor_),
.propulsionType_ =
gbfs::to_api_propulsion_type(products_.propulsion_type_)};
gbfs::to_api_propulsion_type(products_.propulsion_type_),
.returnConstraint_ =
gbfs::to_api_return_constraint(products_.return_constraint_)};
};

api::Itinerary dummy_itinerary(api::Place const& from,
Expand Down
3 changes: 2 additions & 1 deletion ui/src/lib/i18n/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ const translations: Translations = {
return `Fahrt ${n} Stationen`;
}
},
sharingProvider: 'Anbieter'
sharingProvider: 'Anbieter',
roundtripStationReturnConstraint: 'Das Fahrzeug muss wieder an der Abfahrtsstation abgestellt werden.'
};

export default translations;
3 changes: 2 additions & 1 deletion ui/src/lib/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ const translations: Translations = {
return `${n} intermediate stops`;
}
},
sharingProvider: 'Provider'
sharingProvider: 'Provider',
roundtripStationReturnConstraint: 'The vehicle must be returned to the departure station.'
};

export default translations;
3 changes: 2 additions & 1 deletion ui/src/lib/i18n/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ const translations: Translations = {
}
},
sharingProvider: 'Fournisseur',
transfers: 'Transferts'
transfers: 'Transferts',
roundtripStationReturnConstraint: 'Le véhicule doit être retourné à la station de départ.'
};

export default translations;
1 change: 1 addition & 0 deletions ui/src/lib/i18n/translation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export type Translations = {
track: string;
tripIntermediateStops: (n: number) => string;
sharingProvider: string;
roundtripStationReturnConstraint: string;
};

const translations: Map<string, Translations> = new Map(
Expand Down
8 changes: 8 additions & 0 deletions ui/src/lib/openapi/schemas.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,11 @@ export const RentalPropulsionTypeSchema = {
enum: ['HUMAN', 'ELECTRIC_ASSIST', 'ELECTRIC', 'COMBUSTION', 'COMBUSTION_DIESEL', 'HYBRID', 'PLUG_IN_HYBRID', 'HYDROGEN_FUEL_CELL']
} as const;

export const RentalReturnConstraintSchema = {
type: 'string',
enum: ['NONE', 'ANY_STATION', 'ROUNDTRIP_STATION']
} as const;

export const RentalSchema = {
description: 'Vehicle rental',
type: 'object',
Expand Down Expand Up @@ -458,6 +463,9 @@ export const RentalSchema = {
},
propulsionType: {
'$ref': '#/components/schemas/RentalPropulsionType'
},
returnConstraint: {
'$ref': '#/components/schemas/RentalReturnConstraint'
}
}
} as const;
Expand Down
44 changes: 39 additions & 5 deletions ui/src/lib/openapi/types.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,8 @@ export type RentalFormFactor = 'BICYCLE' | 'CARGO_BICYCLE' | 'CAR' | 'MOPED' | '

export type RentalPropulsionType = 'HUMAN' | 'ELECTRIC_ASSIST' | 'ELECTRIC' | 'COMBUSTION' | 'COMBUSTION_DIESEL' | 'HYBRID' | 'PLUG_IN_HYBRID' | 'HYDROGEN_FUEL_CELL';

export type RentalReturnConstraint = 'NONE' | 'ANY_STATION' | 'ROUNDTRIP_STATION';

/**
* Vehicle rental
*/
Expand Down Expand Up @@ -376,6 +378,7 @@ export type Rental = {
rentalUriWeb?: string;
formFactor?: RentalFormFactor;
propulsionType?: RentalPropulsionType;
returnConstraint?: RentalReturnConstraint;
};

export type Leg = {
Expand Down Expand Up @@ -734,6 +737,14 @@ export type PlanData = {
*
*/
directRentalPropulsionTypes?: Array<RentalPropulsionType>;
/**
* Optional. Only applies to direct connections.
*
* A list of rental providers that are allowed to be used for direct connections.
* If empty (the default), all providers are allowed.
*
*/
directRentalProviders?: Array<(string)>;
/**
* \`latitude,longitude,level\` tuple in degrees OR stop id
*/
Expand All @@ -758,6 +769,13 @@ export type PlanData = {
*
*/
maxHours?: number;
/**
* Optional. Default is 25 meters.
*
* Maximum matching distance in meters to match geo coordinates to the street network.
*
*/
maxMatchingDistance: number;
/**
* Optional. Default is 15min which is `900`.
* Maximum time in seconds for the last street leg.
Expand Down Expand Up @@ -811,7 +829,7 @@ export type PlanData = {
*/
postTransitModes?: Array<Mode>;
/**
* Optional. Only applies if the `to` place is a coordinate (not a transit stop). Does not apply to direct connections (see `directModes`).
* Optional. Only applies if the `to` place is a coordinate (not a transit stop). Does not apply to direct connections (see `directRentalFormFactors`).
*
* A list of vehicle type form factors that are allowed to be used from the last transit stop to the `to` coordinate.
* If empty (the default), all form factors are allowed.
Expand All @@ -820,14 +838,22 @@ export type PlanData = {
*/
postTransitRentalFormFactors?: Array<RentalFormFactor>;
/**
* Optional. Only applies if the `to` place is a coordinate (not a transit stop). Does not apply to direct connections (see `directModes`).
* Optional. Only applies if the `to` place is a coordinate (not a transit stop). Does not apply to direct connections (see `directRentalPropulsionTypes`).
*
* A list of vehicle propulsion types that are allowed to be used from the last transit stop to the `to` coordinate..
* A list of vehicle propulsion types that are allowed to be used from the last transit stop to the `to` coordinate.
* If empty (the default), all propulsion types are allowed.
* Example: `HUMAN,ELECTRIC,ELECTRIC_ASSIST`.
*
*/
postTransitRentalPropulsionTypes?: Array<RentalPropulsionType>;
/**
* Optional. Only applies if the `to` place is a coordinate (not a transit stop). Does not apply to direct connections (see `directRentalProviders`).
*
* A list of rental providers that are allowed to be used from the last transit stop to the `to` coordinate.
* If empty (the default), all providers are allowed.
*
*/
postTransitRentalProviders?: Array<(string)>;
/**
* Optional. Default is `WALK`. Only applies if the `from` place is a coordinate (not a transit stop). Does not apply to direct connections (see `directModes`).
*
Expand All @@ -836,7 +862,7 @@ export type PlanData = {
*/
preTransitModes?: Array<Mode>;
/**
* Optional. Only applies if the `from` place is a coordinate (not a transit stop). Does not apply to direct connections (see `directModes`).
* Optional. Only applies if the `from` place is a coordinate (not a transit stop). Does not apply to direct connections (see `directRentalFormFactors`).
*
* A list of vehicle type form factors that are allowed to be used from the `from` coordinate to the first transit stop.
* If empty (the default), all form factors are allowed.
Expand All @@ -845,14 +871,22 @@ export type PlanData = {
*/
preTransitRentalFormFactors?: Array<RentalFormFactor>;
/**
* Optional. Only applies if the `from` place is a coordinate (not a transit stop). Does not apply to direct connections (see `directModes`).
* Optional. Only applies if the `from` place is a coordinate (not a transit stop). Does not apply to direct connections (see `directRentalPropulsionTypes`).
*
* A list of vehicle propulsion types that are allowed to be used from the `from` coordinate to the first transit stop.
* If empty (the default), all propulsion types are allowed.
* Example: `HUMAN,ELECTRIC,ELECTRIC_ASSIST`.
*
*/
preTransitRentalPropulsionTypes?: Array<RentalPropulsionType>;
/**
* Optional. Only applies if the `from` place is a coordinate (not a transit stop). Does not apply to direct connections (see `directRentalProviders`).
*
* A list of rental providers that are allowed to be used from the `from` coordinate to the first transit stop.
* If empty (the default), all providers are allowed.
*
*/
preTransitRentalProviders?: Array<(string)>;
/**
* Optional. Default is `false`.
*
Expand Down
7 changes: 6 additions & 1 deletion ui/src/routes/ConnectionDetail.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
{/snippet}

{#snippet streetLeg(l: Leg)}
<div class="py-12 pl-8 flex flex-col text-muted-foreground">
<div class="py-12 pl-8 flex flex-col gap-y-4 text-muted-foreground">
<span class="ml-6">
{formatDurationSec(l.duration)}
{getModeName(l)}
Expand All @@ -64,6 +64,11 @@
{t.sharingProvider}: <a href={l.rental.url} target="_blank">{l.rental.systemName}</a>
</span>
{/if}
{#if l.rental?.returnConstraint == 'ROUNDTRIP_STATION'}
<span class="ml-6">
{t.roundtripStationReturnConstraint}
</span>
{/if}
</div>
{/snippet}

Expand Down

0 comments on commit 1a1545b

Please sign in to comment.