remove waypoints where not relevant

This commit is contained in:
sbriat 2023-09-14 17:07:38 +02:00
parent c65a5b50c2
commit f69afc4481
38 changed files with 273 additions and 461 deletions

View File

@ -1,7 +1,7 @@
import { Frequency } from '@modules/ad/core/domain/ad.types';
import { Command, CommandProps } from '@mobicoop/ddd-library';
import { ScheduleItem } from '../../types/schedule-item.type';
import { Waypoint } from '../../types/waypoint.type';
import { Address } from '../../types/address.type';
export class CreateAdCommand extends Command {
readonly id: string;
@ -14,7 +14,7 @@ export class CreateAdCommand extends Command {
readonly seatsProposed: number;
readonly seatsRequested: number;
readonly strict: boolean;
readonly waypoints: Waypoint[];
readonly waypoints: Address[];
constructor(props: CommandProps<CreateAdCommand>) {
super(props);

View File

@ -14,9 +14,9 @@ import {
PathType,
TypedRoute,
} from '@modules/ad/core/domain/path-creator.service';
import { Point } from '../../types/point.type';
import { Waypoint } from '../../types/waypoint.type';
import { Waypoint as WaypointValueObject } from '@modules/ad/core/domain/value-objects/waypoint.value-object';
import { Point as PointValueObject } from '@modules/ad/core/domain/value-objects/point.value-object';
import { Point } from '@modules/geography/core/domain/route.types';
@CommandHandler(CreateAdCommand)
export class CreateAdService implements ICommandHandler {
@ -35,8 +35,7 @@ export class CreateAdService implements ICommandHandler {
roles,
command.waypoints.map(
(waypoint: Waypoint) =>
new WaypointValueObject({
position: waypoint.position,
new PointValueObject({
lon: waypoint.lon,
lat: waypoint.lat,
}),
@ -58,21 +57,34 @@ export class CreateAdService implements ICommandHandler {
let driverDuration: number | undefined;
let passengerDistance: number | undefined;
let passengerDuration: number | undefined;
let points: Point[] | undefined;
let points: PointValueObject[] | undefined;
let fwdAzimuth: number | undefined;
let backAzimuth: number | undefined;
typedRoutes.forEach((typedRoute: TypedRoute) => {
if (typedRoute.type !== PathType.PASSENGER) {
driverDistance = typedRoute.route.distance;
driverDuration = typedRoute.route.duration;
points = typedRoute.route.points;
points = typedRoute.route.points.map(
(point: Point) =>
new PointValueObject({
lon: point.lon,
lat: point.lat,
}),
);
fwdAzimuth = typedRoute.route.fwdAzimuth;
backAzimuth = typedRoute.route.backAzimuth;
}
if (typedRoute.type !== PathType.DRIVER) {
passengerDistance = typedRoute.route.distance;
passengerDuration = typedRoute.route.duration;
if (!points) points = typedRoute.route.points;
if (!points)
points = typedRoute.route.points.map(
(point: Point) =>
new PointValueObject({
lon: point.lon,
lat: point.lat,
}),
);
if (!fwdAzimuth) fwdAzimuth = typedRoute.route.fwdAzimuth;
if (!backAzimuth) backAzimuth = typedRoute.route.backAzimuth;
}

View File

@ -1,9 +1,9 @@
import { Route } from '@modules/geography/core/domain/route.types';
import { Waypoint } from '../../domain/value-objects/waypoint.value-object';
import { Point } from '../types/point.type';
export interface RouteProviderPort {
/**
* Get a basic route with points and overall duration / distance
*/
getBasic(waypoints: Waypoint[]): Promise<Route>;
getBasic(waypoints: Point[]): Promise<Route>;
}

View File

@ -1,58 +1,55 @@
import { CandidateEntity } from '@modules/ad/core/domain/candidate.entity';
import { Completer } from './completer.abstract';
import { Role } from '@modules/ad/core/domain/ad.types';
import {
Waypoint as WaypointValueObject,
WaypointProps,
} from '@modules/ad/core/domain/value-objects/waypoint.value-object';
import { Waypoint } from '../../../types/waypoint.type';
import { WayStepsCreator } from '@modules/ad/core/domain/waysteps-creator.service';
import { CarpoolPathCreator } from '@modules/ad/core/domain/carpool-path-creator.service';
import {
Point,
PointProps,
} from '@modules/ad/core/domain/value-objects/point.value-object';
/**
* Complete candidates by setting driver and crew waypoints
* Complete candidates with crew carpool path
*/
export class PassengerOrientedWayStepsCompleter extends Completer {
export class PassengerOrientedCarpoolPathCompleter extends Completer {
complete = async (
candidates: CandidateEntity[],
): Promise<CandidateEntity[]> => {
candidates.forEach((candidate: CandidateEntity) => {
const carpoolPathCreator = new WayStepsCreator(
const carpoolPathCreator = new CarpoolPathCreator(
candidate.getProps().role == Role.DRIVER
? candidate.getProps().waypoints.map(
(waypoint: WaypointProps) =>
new WaypointValueObject({
position: waypoint.position,
? candidate.getProps().driverWaypoints.map(
(waypoint: PointProps) =>
new Point({
lon: waypoint.lon,
lat: waypoint.lat,
}),
)
: this.query.waypoints.map(
(waypoint: Waypoint) =>
new WaypointValueObject({
position: waypoint.position,
new Point({
lon: waypoint.lon,
lat: waypoint.lat,
}),
),
candidate.getProps().role == Role.PASSENGER
? candidate.getProps().waypoints.map(
(waypoint: WaypointProps) =>
new WaypointValueObject({
position: waypoint.position,
? candidate.getProps().driverWaypoints.map(
(waypoint: PointProps) =>
new Point({
lon: waypoint.lon,
lat: waypoint.lat,
}),
)
: this.query.waypoints.map(
(waypoint: Waypoint) =>
new WaypointValueObject({
position: waypoint.position,
new Point({
lon: waypoint.lon,
lat: waypoint.lat,
}),
),
);
candidate.setWaySteps(carpoolPathCreator.getCrewCarpoolPath());
candidate.setCarpoolPath(carpoolPathCreator.createCarpoolPath());
console.log(JSON.stringify(candidate, null, 2));
});
return candidates;
};

View File

@ -12,7 +12,7 @@ import {
PathType,
TypedRoute,
} from '@modules/ad/core/domain/path-creator.service';
import { Waypoint as WaypointValueObject } from '@modules/ad/core/domain/value-objects/waypoint.value-object';
import { Point } from '@modules/ad/core/domain/value-objects/point.value-object';
export class MatchQuery extends QueryBase {
driver?: boolean;
@ -186,8 +186,7 @@ export class MatchQuery extends QueryBase {
roles,
this.waypoints.map(
(waypoint: Waypoint) =>
new WaypointValueObject({
position: waypoint.position,
new Point({
lon: waypoint.lon,
lat: waypoint.lat,
}),

View File

@ -1,6 +1,6 @@
import { Algorithm } from './algorithm.abstract';
import { MatchQuery } from './match.query';
import { PassengerOrientedWayStepsCompleter } from './completer/passenger-oriented-waysteps.completer';
import { PassengerOrientedCarpoolPathCompleter } from './completer/passenger-oriented-carpool-path.completer';
import { PassengerOrientedGeoFilter } from './filter/passenger-oriented-geo.filter';
import { AdRepositoryPort } from '../../ports/ad.repository.port';
import { PassengerOrientedSelector } from './selector/passenger-oriented.selector';
@ -13,7 +13,7 @@ export class PassengerOrientedAlgorithm extends Algorithm {
super(query, repository);
this.selector = new PassengerOrientedSelector(query, repository);
this.processors = [
new PassengerOrientedWayStepsCompleter(query),
new PassengerOrientedCarpoolPathCompleter(query),
new PassengerOrientedGeoFilter(query),
];
}

View File

@ -35,8 +35,31 @@ export class PassengerOrientedSelector extends Selector {
adsRole.ads.map((adEntity: AdEntity) =>
CandidateEntity.create({
id: adEntity.id,
role: adsRole.role,
waypoints: adEntity.getProps().waypoints,
role: adsRole.role == Role.DRIVER ? Role.PASSENGER : Role.DRIVER,
driverWaypoints:
adsRole.role == Role.PASSENGER
? adEntity.getProps().waypoints
: this.query.waypoints.map((waypoint: Waypoint) => ({
position: waypoint.position,
lon: waypoint.lon,
lat: waypoint.lat,
})),
passengerWaypoints:
adsRole.role == Role.DRIVER
? adEntity.getProps().waypoints
: this.query.waypoints.map((waypoint: Waypoint) => ({
position: waypoint.position,
lon: waypoint.lon,
lat: waypoint.lat,
})),
driverDistance:
adsRole.role == Role.PASSENGER
? (adEntity.getProps().driverDistance as number)
: (this.query.driverRoute?.distance as number),
driverDuration:
adsRole.role == Role.PASSENGER
? (adEntity.getProps().driverDuration as number)
: (this.query.driverRoute?.duration as number),
}),
),
)
@ -67,10 +90,10 @@ export class PassengerOrientedSelector extends Selector {
].join();
private _selectAsDriver = (): string =>
`${this.query.driverRoute?.duration} as duration,${this.query.driverRoute?.distance} as distance`;
`${this.query.driverRoute?.duration} as driverDuration,${this.query.driverRoute?.distance} as driverDistance`;
private _selectAsPassenger = (): string =>
`"driverDuration" as duration,"driverDistance" as distance`;
`"driverDuration","driverDistance"`;
private _createFrom = (): string =>
'FROM ad LEFT JOIN schedule_item si ON ad.uuid = si."adUuid"';

View File

@ -1,5 +1,5 @@
import { Waypoint } from '@modules/geography/core/domain/route.types';
import { Role } from '../../domain/ad.types';
import { Point } from '../../domain/value-objects/point.value-object';
export enum AlgorithmType {
PASSENGER_ORIENTED = 'PASSENGER_ORIENTED',
@ -11,8 +11,8 @@ export enum AlgorithmType {
export type Candidate = {
ad: Ad;
role: Role;
driverWaypoints: Waypoint[];
crewWaypoints: Waypoint[];
driverWaypoints: Point[];
crewWaypoints: Point[];
};
export type Ad = {

View File

@ -1,6 +1,5 @@
import { PointProps } from './value-objects/point.value-object';
import { ScheduleItemProps } from './value-objects/schedule-item.value-object';
import { WaypointProps } from './value-objects/waypoint.value-object';
// All properties that an Ad has
export interface AdProps {
@ -17,7 +16,7 @@ export interface AdProps {
driverDistance?: number;
passengerDuration?: number;
passengerDistance?: number;
waypoints: WaypointProps[];
waypoints: PointProps[];
points: PointProps[];
fwdAzimuth: number;
backAzimuth: number;
@ -35,7 +34,7 @@ export interface CreateAdProps {
seatsProposed: number;
seatsRequested: number;
strict: boolean;
waypoints: WaypointProps[];
waypoints: PointProps[];
driverDuration?: number;
driverDistance?: number;
passengerDuration?: number;

View File

@ -10,8 +10,8 @@ export class CandidateEntity extends AggregateRoot<CandidateProps> {
return new CandidateEntity({ id: create.id, props });
};
setWaySteps = (waySteps: WayStepProps[]): void => {
this.props.waySteps = waySteps;
setCarpoolPath = (waySteps: WayStepProps[]): void => {
this.props.carpoolSteps = waySteps;
};
validate(): void {

View File

@ -1,19 +1,25 @@
import { Role } from './ad.types';
import { WaypointProps } from './value-objects/waypoint.value-object';
import { PointProps } from './value-objects/point.value-object';
import { WayStepProps } from './value-objects/waystep.value-object';
// All properties that a Candidate has
export interface CandidateProps {
role: Role;
waypoints: WaypointProps[]; // waypoints of the original Ad
waySteps?: WayStepProps[]; // carpool path for the crew (driver + passenger)
driverWaypoints: PointProps[];
passengerWaypoints: PointProps[];
driverDistance: number;
driverDuration: number;
carpoolSteps?: WayStepProps[]; // carpool path for the crew (driver + passenger)
}
// Properties that are needed for a Candidate creation
export interface CreateCandidateProps {
id: string;
role: Role;
waypoints: WaypointProps[];
driverDistance: number;
driverDuration: number;
driverWaypoints: PointProps[];
passengerWaypoints: PointProps[];
}
export type Spacetime = {

View File

@ -0,0 +1,76 @@
import { Role } from './ad.types';
import { Target } from './candidate.types';
import { Actor } from './value-objects/actor.value-object';
import { Point } from './value-objects/point.value-object';
import { WayStep } from './value-objects/waystep.value-object';
export class CarpoolPathCreator {
constructor(
private readonly driverWaypoints: Point[],
private readonly passengerWaypoints: Point[],
) {}
public createCarpoolPath = (): WayStep[] => {
const passengerWaysteps: WayStep[] = this._createPassengerWaysteps();
if (this.driverWaypoints.length == 2) {
}
return passengerWaysteps;
};
private _createDriverWaysteps = (): WayStep[] =>
this.driverWaypoints.map(
(waypoint: Point, index: number) =>
new WayStep({
lon: waypoint.lon,
lat: waypoint.lat,
actors: [
new Actor({
role: Role.DRIVER,
target: this._getTarget(index, this.driverWaypoints),
}),
],
}),
);
private _createPassengerWaysteps = (): WayStep[] => {
const waysteps: WayStep[] = [];
this.passengerWaypoints.forEach(
(passengerWaypoint: Point, index: number) => {
const waystep: WayStep = new WayStep({
lon: passengerWaypoint.lon,
lat: passengerWaypoint.lat,
actors: [
new Actor({
role: Role.PASSENGER,
target: this._getTarget(index, this.passengerWaypoints),
}),
],
});
if (
this.driverWaypoints.filter((driverWaypoint: Point) =>
this._isSamePoint(driverWaypoint, passengerWaypoint),
).length > 0
) {
waystep.actors.push(
new Actor({
role: Role.DRIVER,
target: Target.NEUTRAL,
}),
);
}
waysteps.push(waystep);
},
);
return waysteps;
};
private _isSamePoint = (point1: Point, point2: Point): boolean =>
point1.lon === point2.lon && point1.lat === point2.lat;
private _getTarget = (index: number, waypoints: Point[]): Target =>
index == 0
? Target.START
: index == waypoints.length - 1
? Target.FINISH
: Target.INTERMEDIATE;
}

View File

@ -1,11 +1,11 @@
import { Route } from '@modules/geography/core/domain/route.types';
import { Role } from './ad.types';
import { Waypoint } from './value-objects/waypoint.value-object';
import { Point } from './value-objects/point.value-object';
export class PathCreator {
constructor(
private readonly roles: Role[],
private readonly waypoints: Waypoint[],
private readonly waypoints: Point[],
) {}
public getBasePaths = (): Path[] => {
@ -40,21 +40,12 @@ export class PathCreator {
PathType.PASSENGER,
);
private _firstWaypoint = (): Waypoint =>
this.waypoints.find(
(waypoint: Waypoint) => waypoint.position == 0,
) as Waypoint;
private _firstWaypoint = (): Point => this.waypoints[0];
private _lastWaypoint = (): Waypoint =>
this.waypoints.find(
(waypoint: Waypoint) =>
waypoint.position ==
Math.max(
...this.waypoints.map((waypoint: Waypoint) => waypoint.position),
),
) as Waypoint;
private _lastWaypoint = (): Point =>
this.waypoints[this.waypoints.length - 1];
private _createPath = (waypoints: Waypoint[], type: PathType): Path => ({
private _createPath = (waypoints: Point[], type: PathType): Path => ({
type,
waypoints,
});
@ -62,7 +53,7 @@ export class PathCreator {
export type Path = {
type: PathType;
waypoints: Waypoint[];
waypoints: Point[];
};
export type TypedRoute = {

View File

@ -1,32 +0,0 @@
import { ArgumentInvalidException, ValueObject } from '@mobicoop/ddd-library';
import { PointProps } from './point.value-object';
/** Note:
* Value Objects with multiple properties can contain
* other Value Objects inside if needed.
* */
export interface WaypointProps extends PointProps {
position: number;
}
export class Waypoint extends ValueObject<WaypointProps> {
get position(): number {
return this.props.position;
}
get lon(): number {
return this.props.lon;
}
get lat(): number {
return this.props.lat;
}
protected validate(props: WaypointProps): void {
if (props.position < 0)
throw new ArgumentInvalidException(
'position must be greater than or equal to 0',
);
}
}

View File

@ -2,24 +2,20 @@ import {
ArgumentOutOfRangeException,
ValueObject,
} from '@mobicoop/ddd-library';
import { WaypointProps } from './waypoint.value-object';
import { Actor } from './actor.value-object';
import { Role } from '../ad.types';
import { PointProps } from './point.value-object';
/** Note:
* Value Objects with multiple properties can contain
* other Value Objects inside if needed.
* */
export interface WayStepProps extends WaypointProps {
export interface WayStepProps extends PointProps {
actors: Actor[];
}
export class WayStep extends ValueObject<WayStepProps> {
get position(): number {
return this.props.position;
}
get lon(): number {
return this.props.lon;
}

View File

@ -1,62 +0,0 @@
import { Role } from './ad.types';
import { Target } from './candidate.types';
import { Actor } from './value-objects/actor.value-object';
import { Waypoint } from './value-objects/waypoint.value-object';
import { WayStep } from './value-objects/waystep.value-object';
export class WayStepsCreator {
constructor(
private readonly driverWaypoints: Waypoint[],
private readonly passengerWaypoints: Waypoint[],
) {}
public getCrewCarpoolPath = (): WayStep[] => this._createPassengerWaysteps();
private _createPassengerWaysteps = (): WayStep[] => {
const waysteps: WayStep[] = [];
this.passengerWaypoints.forEach((passengerWaypoint: Waypoint) => {
const waystep: WayStep = new WayStep({
lon: passengerWaypoint.lon,
lat: passengerWaypoint.lat,
position: passengerWaypoint.position,
actors: [
new Actor({
role: Role.PASSENGER,
target: this._getTarget(
passengerWaypoint.position,
this.passengerWaypoints,
),
}),
],
});
if (
this.driverWaypoints.filter((driverWaypoint: Waypoint) =>
this._isSameWaypoint(driverWaypoint, passengerWaypoint),
).length > 0
) {
waystep.actors.push(
new Actor({
role: Role.DRIVER,
target: Target.NEUTRAL,
}),
);
}
waysteps.push(waystep);
});
return waysteps;
};
private _isSameWaypoint = (
waypoint1: Waypoint,
waypoint2: Waypoint,
): boolean =>
waypoint1.lon === waypoint2.lon && waypoint1.lat === waypoint2.lat;
private _getTarget = (position: number, waypoints: Waypoint[]): Target =>
position == 0
? Target.START
: position ==
Math.max(...waypoints.map((waypoint: Waypoint) => waypoint.position))
? Target.FINISH
: Target.INTERMEDIATE;
}

View File

@ -30,7 +30,7 @@ export type AdModel = {
};
/**
* The record as returned by the peristence system
* The record as returned by the persistence system
*/
export type AdReadModel = AdModel & {
waypoints: string;

View File

@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
import { RouteProviderPort } from '../core/application/ports/route-provider.port';
import { GetBasicRouteControllerPort } from '@modules/geography/core/application/ports/get-basic-route-controller.port';
import { AD_GET_BASIC_ROUTE_CONTROLLER } from '../ad.di-tokens';
import { Route, Waypoint } from '@modules/geography/core/domain/route.types';
import { Point, Route } from '@modules/geography/core/domain/route.types';
@Injectable()
export class RouteProvider implements RouteProviderPort {
@ -11,7 +11,7 @@ export class RouteProvider implements RouteProviderPort {
private readonly getBasicRouteController: GetBasicRouteControllerPort,
) {}
getBasic = async (waypoints: Waypoint[]): Promise<Route> =>
getBasic = async (waypoints: Point[]): Promise<Route> =>
await this.getBasicRouteController.get({
waypoints,
});

View File

@ -28,12 +28,10 @@ const adEntity: AdEntity = new AdEntity({
],
waypoints: [
{
position: 0,
lat: 48.689445,
lon: 6.1765102,
},
{
position: 1,
lat: 48.8566,
lon: 2.3522,
},

View File

@ -1,14 +1,12 @@
import { AdEntity } from '@modules/ad/core/domain/ad.entity';
import { CreateAdProps, Frequency } from '@modules/ad/core/domain/ad.types';
import { WaypointProps } from '@modules/ad/core/domain/value-objects/waypoint.value-object';
import { PointProps } from '@modules/ad/core/domain/value-objects/point.value-object';
const originWaypointProps: WaypointProps = {
position: 0,
const originPointProps: PointProps = {
lat: 48.689445,
lon: 6.17651,
};
const destinationWaypointProps: WaypointProps = {
position: 1,
const destinationPointProps: PointProps = {
lat: 48.8566,
lon: 2.3522,
};
@ -30,7 +28,7 @@ const createAdProps: CreateAdProps = {
seatsProposed: 3,
seatsRequested: 1,
strict: false,
waypoints: [originWaypointProps, destinationWaypointProps],
waypoints: [originPointProps, destinationPointProps],
driverDistance: 23000,
driverDuration: 900,
passengerDistance: 23000,

View File

@ -7,16 +7,14 @@ import { CreateAdProps, Frequency } from '@modules/ad/core/domain/ad.types';
import { CreateAdService } from '@modules/ad/core/application/commands/create-ad/create-ad.service';
import { CreateAdCommand } from '@modules/ad/core/application/commands/create-ad/create-ad.command';
import { AdAlreadyExistsException } from '@modules/ad/core/domain/ad.errors';
import { WaypointProps } from '@modules/ad/core/domain/value-objects/waypoint.value-object';
import { RouteProviderPort } from '@modules/ad/core/application/ports/route-provider.port';
import { PointProps } from '@modules/ad/core/domain/value-objects/point.value-object';
const originWaypoint: WaypointProps = {
position: 0,
const originWaypoint: PointProps = {
lat: 48.689445,
lon: 6.17651,
};
const destinationWaypoint: WaypointProps = {
position: 1,
const destinationWaypoint: PointProps = {
lat: 48.8566,
lon: 2.3522,
};

View File

@ -1,4 +1,4 @@
import { PassengerOrientedWayStepsCompleter } from '@modules/ad/core/application/queries/match/completer/passenger-oriented-waysteps.completer';
import { PassengerOrientedCarpoolPathCompleter } from '@modules/ad/core/application/queries/match/completer/passenger-oriented-carpool-path.completer';
import { MatchQuery } from '@modules/ad/core/application/queries/match/match.query';
import { AlgorithmType } from '@modules/ad/core/application/types/algorithm.types';
import { Waypoint } from '@modules/ad/core/application/types/waypoint.type';
@ -44,43 +44,63 @@ const candidates: CandidateEntity[] = [
CandidateEntity.create({
id: 'cc260669-1c6d-441f-80a5-19cd59afb777',
role: Role.DRIVER,
waypoints: [
driverWaypoints: [
{
position: 0,
lat: 48.678454,
lon: 6.189745,
},
{
position: 1,
lat: 48.84877,
lon: 2.398457,
},
],
}),
CandidateEntity.create({
id: '5600ccfb-ab69-4d03-aa30-0fbe84fcedc0',
role: Role.PASSENGER,
waypoints: [
passengerWaypoints: [
{
position: 0,
lat: 48.689445,
lon: 6.17651,
},
{
position: 1,
lat: 48.8566,
lon: 2.3522,
},
],
driverDistance: 350145,
driverDuration: 13548,
}),
CandidateEntity.create({
id: '5600ccfb-ab69-4d03-aa30-0fbe84fcedc0',
role: Role.PASSENGER,
driverWaypoints: [
{
lat: 48.689445,
lon: 6.17651,
},
{
lat: 48.8566,
lon: 2.3522,
},
],
passengerWaypoints: [
{
lat: 48.689445,
lon: 6.17651,
},
{
lat: 48.8566,
lon: 2.3522,
},
],
driverDistance: 350145,
driverDuration: 13548,
}),
];
describe('Passenger oriented waysteps completer', () => {
describe('Passenger oriented carpool path completer', () => {
it('should complete candidates', async () => {
const passengerOrientedWaypointsCompleter: PassengerOrientedWayStepsCompleter =
new PassengerOrientedWayStepsCompleter(matchQuery);
const passengerOrientedCarpoolPathCompleter: PassengerOrientedCarpoolPathCompleter =
new PassengerOrientedCarpoolPathCompleter(matchQuery);
const completedCandidates: CandidateEntity[] =
await passengerOrientedWaypointsCompleter.complete(candidates);
await passengerOrientedCarpoolPathCompleter.complete(candidates);
expect(completedCandidates.length).toBe(2);
});
});

View File

@ -44,34 +44,54 @@ const candidates: CandidateEntity[] = [
CandidateEntity.create({
id: 'cc260669-1c6d-441f-80a5-19cd59afb777',
role: Role.DRIVER,
waypoints: [
driverWaypoints: [
{
position: 0,
lat: 48.678454,
lon: 6.189745,
},
{
position: 1,
lat: 48.84877,
lon: 2.398457,
},
],
passengerWaypoints: [
{
lat: 48.689445,
lon: 6.17651,
},
{
lat: 48.8566,
lon: 2.3522,
},
],
driverDistance: 350145,
driverDuration: 13548,
}),
CandidateEntity.create({
id: '5600ccfb-ab69-4d03-aa30-0fbe84fcedc0',
role: Role.PASSENGER,
waypoints: [
driverWaypoints: [
{
position: 0,
lat: 48.668487,
lon: 6.178457,
lat: 48.689445,
lon: 6.17651,
},
{
position: 1,
lat: 48.897457,
lon: 2.3688487,
lat: 48.8566,
lon: 2.3522,
},
],
passengerWaypoints: [
{
lat: 48.689445,
lon: 6.17651,
},
{
lat: 48.8566,
lon: 2.3522,
},
],
driverDistance: 350145,
driverDuration: 13548,
}),
];

View File

@ -4,28 +4,20 @@ import {
PathCreator,
PathType,
} from '@modules/ad/core/domain/path-creator.service';
import { Waypoint } from '@modules/ad/core/domain/value-objects/waypoint.value-object';
import { Point } from '@modules/ad/core/domain/value-objects/point.value-object';
const originWaypoint: Waypoint = new Waypoint({
position: 0,
const originWaypoint: Point = new Point({
lat: 48.689445,
lon: 6.17651,
});
const destinationWaypoint: Waypoint = new Waypoint({
position: 1,
const destinationWaypoint: Point = new Point({
lat: 48.8566,
lon: 2.3522,
});
const intermediateWaypoint: Waypoint = new Waypoint({
position: 1,
const intermediateWaypoint: Point = new Point({
lat: 48.74488,
lon: 4.8972,
});
const destinationWaypointWithIntermediateWaypoint: Waypoint = new Waypoint({
position: 2,
lat: 48.8566,
lon: 2.3522,
});
describe('Path Creator Service', () => {
it('should create a path for a driver only', () => {
@ -58,11 +50,7 @@ describe('Path Creator Service', () => {
it('should create two different paths for a driver and passenger with intermediate waypoint', () => {
const pathCreator: PathCreator = new PathCreator(
[Role.DRIVER, Role.PASSENGER],
[
originWaypoint,
intermediateWaypoint,
destinationWaypointWithIntermediateWaypoint,
],
[originWaypoint, intermediateWaypoint, destinationWaypoint],
);
const paths: Path[] = pathCreator.getBasePaths();
expect(paths).toHaveLength(2);

View File

@ -1,69 +0,0 @@
import {
ArgumentInvalidException,
ArgumentOutOfRangeException,
} from '@mobicoop/ddd-library';
import { Waypoint } from '@modules/ad/core/domain/value-objects/waypoint.value-object';
describe('Waypoint value object', () => {
it('should create a waypoint value object', () => {
const waypointVO = new Waypoint({
position: 0,
lat: 48.689445,
lon: 6.17651,
});
expect(waypointVO.position).toBe(0);
expect(waypointVO.lat).toBe(48.689445);
expect(waypointVO.lon).toBe(6.17651);
});
it('should throw an exception if position is invalid', () => {
try {
new Waypoint({
position: -1,
lat: 48.689445,
lon: 6.17651,
});
} catch (e: any) {
expect(e).toBeInstanceOf(ArgumentInvalidException);
}
});
it('should throw an exception if longitude is invalid', () => {
try {
new Waypoint({
position: 0,
lat: 48.689445,
lon: 186.17651,
});
} catch (e: any) {
expect(e).toBeInstanceOf(ArgumentOutOfRangeException);
}
try {
new Waypoint({
position: 0,
lat: 48.689445,
lon: -186.17651,
});
} catch (e: any) {
expect(e).toBeInstanceOf(ArgumentOutOfRangeException);
}
});
it('should throw an exception if latitude is invalid', () => {
try {
new Waypoint({
position: 0,
lat: 148.689445,
lon: 6.17651,
});
} catch (e: any) {
expect(e).toBeInstanceOf(ArgumentOutOfRangeException);
}
try {
new Waypoint({
position: 0,
lat: -148.689445,
lon: 6.17651,
});
} catch (e: any) {
expect(e).toBeInstanceOf(ArgumentOutOfRangeException);
}
});
});

View File

@ -9,7 +9,6 @@ describe('WayStep value object', () => {
const wayStepVO = new WayStep({
lat: 48.689445,
lon: 6.17651,
position: 0,
actors: [
new Actor({
role: Role.DRIVER,
@ -21,7 +20,6 @@ describe('WayStep value object', () => {
}),
],
});
expect(wayStepVO.position).toBe(0);
expect(wayStepVO.lon).toBe(6.17651);
expect(wayStepVO.lat).toBe(48.689445);
expect(wayStepVO.actors).toHaveLength(2);
@ -31,7 +29,6 @@ describe('WayStep value object', () => {
new WayStep({
lat: 48.689445,
lon: 6.17651,
position: 0,
actors: [],
});
} catch (e: any) {
@ -43,7 +40,6 @@ describe('WayStep value object', () => {
new WayStep({
lat: 48.689445,
lon: 6.17651,
position: 0,
actors: [
new Actor({
role: Role.DRIVER,

View File

@ -61,14 +61,8 @@ describe('Route provider', () => {
it('should provide a route', async () => {
const route: Route = await routeProvider.getBasic([
{
position: 0,
...originPoint,
},
{
position: 1,
...destinationPoint,
},
originPoint,
destinationPoint,
]);
expect(route.distance).toBe(350101);
expect(route.duration).toBe(14422);

View File

@ -1,6 +1,6 @@
import { Route, Waypoint } from '../../domain/route.types';
import { Route, Point } from '../../domain/route.types';
import { GeorouterSettings } from '../types/georouter-settings.type';
export interface GeorouterPort {
route(waypoints: Waypoint[], settings: GeorouterSettings): Promise<Route>;
route(waypoints: Point[], settings: GeorouterSettings): Promise<Route>;
}

View File

@ -1,12 +1,12 @@
import { QueryBase } from '@mobicoop/ddd-library';
import { GeorouterSettings } from '../../types/georouter-settings.type';
import { Waypoint } from '@modules/geography/core/domain/route.types';
import { Point } from '@modules/geography/core/domain/route.types';
export class GetRouteQuery extends QueryBase {
readonly waypoints: Waypoint[];
readonly waypoints: Point[];
readonly georouterSettings: GeorouterSettings;
constructor(waypoints: Waypoint[], georouterSettings: GeorouterSettings) {
constructor(waypoints: Point[], georouterSettings: GeorouterSettings) {
super();
this.waypoints = waypoints;
this.georouterSettings = georouterSettings;

View File

@ -1,7 +1,6 @@
import { GeorouterPort } from '../application/ports/georouter.port';
import { GeorouterSettings } from '../application/types/georouter-settings.type';
import { PointProps } from './value-objects/point.value-object';
import { WaypointProps } from './value-objects/waypoint.value-object';
// All properties that a Route has
export interface RouteProps {
@ -15,7 +14,7 @@ export interface RouteProps {
// Properties that are needed for a Route creation
export interface CreateRouteProps {
waypoints: WaypointProps[];
waypoints: PointProps[];
georouter: GeorouterPort;
georouterSettings: GeorouterSettings;
}
@ -36,13 +35,7 @@ export type Point = {
lat: number;
};
export type Waypoint = Point & {
position: number;
};
export type Spacetime = {
export type Step = Point & {
duration: number;
distance?: number;
};
export type Step = Point & Spacetime;

View File

@ -1,32 +0,0 @@
import { ArgumentInvalidException, ValueObject } from '@mobicoop/ddd-library';
import { PointProps } from './point.value-object';
/** Note:
* Value Objects with multiple properties can contain
* other Value Objects inside if needed.
* */
export interface WaypointProps extends PointProps {
position: number;
}
export class Waypoint extends ValueObject<WaypointProps> {
get position(): number {
return this.props.position;
}
get lon(): number {
return this.props.lon;
}
get lat(): number {
return this.props.lat;
}
protected validate(props: WaypointProps): void {
if (props.position < 0)
throw new ArgumentInvalidException(
'position must be greater than or equal to 0',
);
}
}

View File

@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { GeorouterPort } from '../core/application/ports/georouter.port';
import { GeorouterSettings } from '../core/application/types/georouter-settings.type';
import { Route, Step, Waypoint } from '../core/domain/route.types';
import { Route, Step, Point } from '../core/domain/route.types';
import { DefaultParamsProviderPort } from '../core/application/ports/default-params-provider.port';
import { GEODESIC, PARAMS_PROVIDER } from '../geography.di-tokens';
import { catchError, lastValueFrom, map } from 'rxjs';
@ -31,7 +31,7 @@ export class GraphhopperGeorouter implements GeorouterPort {
}
route = async (
waypoints: Waypoint[],
waypoints: Point[],
settings: GeorouterSettings,
): Promise<Route> => {
this._setDefaultUrlArgs();
@ -57,12 +57,12 @@ export class GraphhopperGeorouter implements GeorouterPort {
}
};
private _getRoute = async (waypoints: Waypoint[]): Promise<Route> => {
private _getRoute = async (waypoints: Point[]): Promise<Route> => {
const url: string = [
this.getUrl(),
'&point=',
waypoints
.map((waypoint: Waypoint) => [waypoint.lat, waypoint.lon].join('%2C'))
.map((point: Point) => [point.lat, point.lon].join('%2C'))
.join('&point='),
].join('');
return await lastValueFrom(

View File

@ -1,5 +1,5 @@
import { Waypoint } from '@modules/geography/core/domain/route.types';
import { Point } from '@modules/geography/core/domain/route.types';
export type GetRouteRequestDto = {
waypoints: Waypoint[];
waypoints: Point[];
};

View File

@ -2,17 +2,15 @@ import { GeorouterPort } from '@modules/geography/core/application/ports/georout
import { GetRouteQuery } from '@modules/geography/core/application/queries/get-route/get-route.query';
import { GetRouteQueryHandler } from '@modules/geography/core/application/queries/get-route/get-route.query-handler';
import { RouteEntity } from '@modules/geography/core/domain/route.entity';
import { Waypoint } from '@modules/geography/core/domain/route.types';
import { Point } from '@modules/geography/core/domain/route.types';
import { GEOROUTER } from '@modules/geography/geography.di-tokens';
import { Test, TestingModule } from '@nestjs/testing';
const originWaypoint: Waypoint = {
position: 0,
const originWaypoint: Point = {
lat: 48.689445,
lon: 6.17651,
};
const destinationWaypoint: Waypoint = {
position: 1,
const destinationWaypoint: Point = {
lat: 48.8566,
lon: 2.3522,
};

View File

@ -44,16 +44,7 @@ const mockGeorouter: GeorouterPort = {
};
const createRouteProps: CreateRouteProps = {
waypoints: [
{
position: 0,
...originPoint,
},
{
position: 1,
...destinationPoint,
},
],
waypoints: [originPoint, destinationPoint],
georouter: mockGeorouter,
georouterSettings: {
points: true,

View File

@ -1,69 +0,0 @@
import {
ArgumentInvalidException,
ArgumentOutOfRangeException,
} from '@mobicoop/ddd-library';
import { Waypoint } from '@modules/geography/core/domain/value-objects/waypoint.value-object';
describe('Waypoint value object', () => {
it('should create a waypoint value object', () => {
const waypointVO = new Waypoint({
position: 0,
lat: 48.689445,
lon: 6.17651,
});
expect(waypointVO.position).toBe(0);
expect(waypointVO.lat).toBe(48.689445);
expect(waypointVO.lon).toBe(6.17651);
});
it('should throw an exception if position is invalid', () => {
try {
new Waypoint({
position: -1,
lat: 48.689445,
lon: 6.17651,
});
} catch (e: any) {
expect(e).toBeInstanceOf(ArgumentInvalidException);
}
});
it('should throw an exception if longitude is invalid', () => {
try {
new Waypoint({
position: 0,
lat: 48.689445,
lon: 186.17651,
});
} catch (e: any) {
expect(e).toBeInstanceOf(ArgumentOutOfRangeException);
}
try {
new Waypoint({
position: 0,
lat: 48.689445,
lon: -186.17651,
});
} catch (e: any) {
expect(e).toBeInstanceOf(ArgumentOutOfRangeException);
}
});
it('should throw an exception if latitude is invalid', () => {
try {
new Waypoint({
position: 0,
lat: 148.689445,
lon: 6.17651,
});
} catch (e: any) {
expect(e).toBeInstanceOf(ArgumentOutOfRangeException);
}
try {
new Waypoint({
position: 0,
lat: -148.689445,
lon: 6.17651,
});
} catch (e: any) {
expect(e).toBeInstanceOf(ArgumentOutOfRangeException);
}
});
});

View File

@ -297,12 +297,10 @@ describe('Graphhopper Georouter', () => {
graphhopperGeorouter.route(
[
{
position: 0,
lon: 0,
lat: 0,
},
{
position: 1,
lon: 1,
lat: 1,
},
@ -321,12 +319,10 @@ describe('Graphhopper Georouter', () => {
graphhopperGeorouter.route(
[
{
position: 0,
lon: 0,
lat: 0,
},
{
position: 1,
lon: 1,
lat: 1,
},
@ -344,12 +340,10 @@ describe('Graphhopper Georouter', () => {
const route: Route = await graphhopperGeorouter.route(
[
{
position: 0,
lon: 0,
lat: 0,
},
{
position: 1,
lon: 10,
lat: 10,
},
@ -367,12 +361,10 @@ describe('Graphhopper Georouter', () => {
const route: Route = await graphhopperGeorouter.route(
[
{
position: 0,
lon: 0,
lat: 0,
},
{
position: 1,
lon: 10,
lat: 10,
},
@ -394,12 +386,10 @@ describe('Graphhopper Georouter', () => {
const route: Route = await graphhopperGeorouter.route(
[
{
position: 0,
lon: 0,
lat: 0,
},
{
position: 1,
lon: 10,
lat: 10,
},
@ -419,17 +409,14 @@ describe('Graphhopper Georouter', () => {
const route: Route = await graphhopperGeorouter.route(
[
{
position: 0,
lon: 0,
lat: 0,
},
{
position: 1,
lon: 5,
lat: 5,
},
{
position: 2,
lon: 10,
lat: 10,
},
@ -452,12 +439,10 @@ describe('Graphhopper Georouter', () => {
const route: Route = await graphhopperGeorouter.route(
[
{
position: 0,
lon: 0,
lat: 0,
},
{
position: 1,
lon: 10,
lat: 10,
},

View File

@ -49,12 +49,10 @@ describe('Get Basic Route Controller', () => {
await getBasicRouteController.get({
waypoints: [
{
position: 0,
lat: 48.689445,
lon: 6.17651,
},
{
position: 1,
lat: 48.8566,
lon: 2.3522,
},