add actorTime
This commit is contained in:
parent
996759d001
commit
6b6a169dee
|
@ -1,5 +1,5 @@
|
||||||
import { Route } from '@modules/geography/core/domain/route.types';
|
|
||||||
import { Point } from '../types/point.type';
|
import { Point } from '../types/point.type';
|
||||||
|
import { Route } from '../types/route.type';
|
||||||
|
|
||||||
export interface RouteProviderPort {
|
export interface RouteProviderPort {
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { CandidateEntity } from '@modules/ad/core/domain/candidate.entity';
|
||||||
|
import { Completer } from './completer.abstract';
|
||||||
|
|
||||||
|
export class JourneyCompleter extends Completer {
|
||||||
|
complete = async (
|
||||||
|
candidates: CandidateEntity[],
|
||||||
|
): Promise<CandidateEntity[]> =>
|
||||||
|
candidates.map((candidate: CandidateEntity) => candidate.createJourney());
|
||||||
|
}
|
|
@ -1,7 +1,8 @@
|
||||||
import { CandidateEntity } from '@modules/ad/core/domain/candidate.entity';
|
import { CandidateEntity } from '@modules/ad/core/domain/candidate.entity';
|
||||||
import { Completer } from './completer.abstract';
|
import { Completer } from './completer.abstract';
|
||||||
import { MatchQuery } from '../match.query';
|
import { MatchQuery } from '../match.query';
|
||||||
import { WayStep } from '@modules/ad/core/domain/value-objects/waystep.value-object';
|
import { CarpoolStep } from '@modules/ad/core/domain/value-objects/carpool-step.value-object';
|
||||||
|
import { Step } from '../../../types/step.type';
|
||||||
|
|
||||||
export class RouteCompleter extends Completer {
|
export class RouteCompleter extends Completer {
|
||||||
protected readonly type: RouteCompleterType;
|
protected readonly type: RouteCompleterType;
|
||||||
|
@ -18,8 +19,8 @@ export class RouteCompleter extends Completer {
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case RouteCompleterType.BASIC:
|
case RouteCompleterType.BASIC:
|
||||||
const basicCandidateRoute = await this.query.routeProvider.getBasic(
|
const basicCandidateRoute = await this.query.routeProvider.getBasic(
|
||||||
(candidate.getProps().carpoolSteps as WayStep[]).map(
|
(candidate.getProps().carpoolSteps as CarpoolStep[]).map(
|
||||||
(wayStep: WayStep) => wayStep.point,
|
(carpoolStep: CarpoolStep) => carpoolStep.point,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
candidate.setMetrics(
|
candidate.setMetrics(
|
||||||
|
@ -30,14 +31,11 @@ export class RouteCompleter extends Completer {
|
||||||
case RouteCompleterType.DETAILED:
|
case RouteCompleterType.DETAILED:
|
||||||
const detailedCandidateRoute =
|
const detailedCandidateRoute =
|
||||||
await this.query.routeProvider.getDetailed(
|
await this.query.routeProvider.getDetailed(
|
||||||
(candidate.getProps().carpoolSteps as WayStep[]).map(
|
(candidate.getProps().carpoolSteps as CarpoolStep[]).map(
|
||||||
(wayStep: WayStep) => wayStep.point,
|
(carpoolStep: CarpoolStep) => carpoolStep.point,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
candidate.setMetrics(
|
candidate.setSteps(detailedCandidateRoute.steps as Step[]);
|
||||||
detailedCandidateRoute.distance,
|
|
||||||
detailedCandidateRoute.duration,
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return candidate;
|
return candidate;
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { Frequency, Role } from '@modules/ad/core/domain/ad.types';
|
||||||
import { MatchRequestDto } from '@modules/ad/interface/grpc-controllers/dtos/match.request.dto';
|
import { MatchRequestDto } from '@modules/ad/interface/grpc-controllers/dtos/match.request.dto';
|
||||||
import { DateTimeTransformerPort } from '../../ports/datetime-transformer.port';
|
import { DateTimeTransformerPort } from '../../ports/datetime-transformer.port';
|
||||||
import { RouteProviderPort } from '../../ports/route-provider.port';
|
import { RouteProviderPort } from '../../ports/route-provider.port';
|
||||||
import { Route } from '@modules/geography/core/domain/route.types';
|
|
||||||
import {
|
import {
|
||||||
Path,
|
Path,
|
||||||
PathCreator,
|
PathCreator,
|
||||||
|
@ -13,6 +12,7 @@ import {
|
||||||
TypedRoute,
|
TypedRoute,
|
||||||
} from '@modules/ad/core/domain/path-creator.service';
|
} from '@modules/ad/core/domain/path-creator.service';
|
||||||
import { Point } from '@modules/ad/core/domain/value-objects/point.value-object';
|
import { Point } from '@modules/ad/core/domain/value-objects/point.value-object';
|
||||||
|
import { Route } from '../../types/route.type';
|
||||||
|
|
||||||
export class MatchQuery extends QueryBase {
|
export class MatchQuery extends QueryBase {
|
||||||
driver?: boolean;
|
driver?: boolean;
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { Point } from './point.type';
|
||||||
|
import { Step } from './step.type';
|
||||||
|
|
||||||
|
export type Route = {
|
||||||
|
distance: number;
|
||||||
|
duration: number;
|
||||||
|
fwdAzimuth: number;
|
||||||
|
backAzimuth: number;
|
||||||
|
distanceAzimuth: number;
|
||||||
|
points: Point[];
|
||||||
|
steps?: Step[];
|
||||||
|
};
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { Point } from './point.type';
|
||||||
|
|
||||||
|
export type Step = Point & {
|
||||||
|
duration: number;
|
||||||
|
distance?: number;
|
||||||
|
};
|
|
@ -1,6 +1,7 @@
|
||||||
import { AggregateRoot, AggregateID } from '@mobicoop/ddd-library';
|
import { AggregateRoot, AggregateID } from '@mobicoop/ddd-library';
|
||||||
import { CandidateProps, CreateCandidateProps } from './candidate.types';
|
import { CandidateProps, CreateCandidateProps } from './candidate.types';
|
||||||
import { WayStepProps } from './value-objects/waystep.value-object';
|
import { CarpoolStepProps } from './value-objects/carpool-step.value-object';
|
||||||
|
import { StepProps } from './value-objects/step.value-object';
|
||||||
|
|
||||||
export class CandidateEntity extends AggregateRoot<CandidateProps> {
|
export class CandidateEntity extends AggregateRoot<CandidateProps> {
|
||||||
protected readonly _id: AggregateID;
|
protected readonly _id: AggregateID;
|
||||||
|
@ -10,8 +11,8 @@ export class CandidateEntity extends AggregateRoot<CandidateProps> {
|
||||||
return new CandidateEntity({ id: create.id, props });
|
return new CandidateEntity({ id: create.id, props });
|
||||||
};
|
};
|
||||||
|
|
||||||
setCarpoolPath = (waySteps: WayStepProps[]): CandidateEntity => {
|
setCarpoolPath = (carpoolSteps: CarpoolStepProps[]): CandidateEntity => {
|
||||||
this.props.carpoolSteps = waySteps;
|
this.props.carpoolSteps = carpoolSteps;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -21,9 +22,16 @@ export class CandidateEntity extends AggregateRoot<CandidateProps> {
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
setSteps = (steps: StepProps[]): CandidateEntity => {
|
||||||
|
this.props.steps = steps;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
isDetourValid = (): boolean =>
|
isDetourValid = (): boolean =>
|
||||||
this._validateDistanceDetour() && this._validateDurationDetour();
|
this._validateDistanceDetour() && this._validateDurationDetour();
|
||||||
|
|
||||||
|
createJourney = (): CandidateEntity => this;
|
||||||
|
|
||||||
private _validateDurationDetour = (): boolean =>
|
private _validateDurationDetour = (): boolean =>
|
||||||
this.props.duration
|
this.props.duration
|
||||||
? this.props.duration <=
|
? this.props.duration <=
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import { Role } from './ad.types';
|
import { Role } from './ad.types';
|
||||||
import { PointProps } from './value-objects/point.value-object';
|
import { PointProps } from './value-objects/point.value-object';
|
||||||
import { ScheduleItemProps } from './value-objects/schedule-item.value-object';
|
import { ScheduleItemProps } from './value-objects/schedule-item.value-object';
|
||||||
import { WayStepProps } from './value-objects/waystep.value-object';
|
import { CarpoolStepProps } from './value-objects/carpool-step.value-object';
|
||||||
|
import { JourneyProps } from './value-objects/journey.value-object';
|
||||||
|
import { StepProps } from './value-objects/step.value-object';
|
||||||
|
|
||||||
// All properties that a Candidate has
|
// All properties that a Candidate has
|
||||||
export interface CandidateProps {
|
export interface CandidateProps {
|
||||||
|
@ -10,11 +12,13 @@ export interface CandidateProps {
|
||||||
passengerWaypoints: PointProps[];
|
passengerWaypoints: PointProps[];
|
||||||
driverDistance: number;
|
driverDistance: number;
|
||||||
driverDuration: number;
|
driverDuration: number;
|
||||||
carpoolSteps?: WayStepProps[]; // carpool path for the crew (driver + passenger)
|
carpoolSteps?: CarpoolStepProps[];
|
||||||
distance?: number;
|
distance?: number;
|
||||||
duration?: number;
|
duration?: number;
|
||||||
|
steps?: StepProps[];
|
||||||
driverSchedule: ScheduleItemProps[];
|
driverSchedule: ScheduleItemProps[];
|
||||||
passengerSchedule: ScheduleItemProps[];
|
passengerSchedule: ScheduleItemProps[];
|
||||||
|
journeys?: JourneyProps[];
|
||||||
spacetimeDetourRatio: SpacetimeDetourRatio;
|
spacetimeDetourRatio: SpacetimeDetourRatio;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Target } from './candidate.types';
|
||||||
import { CarpoolPathCreatorException } from './match.errors';
|
import { CarpoolPathCreatorException } from './match.errors';
|
||||||
import { Actor } from './value-objects/actor.value-object';
|
import { Actor } from './value-objects/actor.value-object';
|
||||||
import { Point } from './value-objects/point.value-object';
|
import { Point } from './value-objects/point.value-object';
|
||||||
import { WayStep } from './value-objects/waystep.value-object';
|
import { CarpoolStep } from './value-objects/carpool-step.value-object';
|
||||||
|
|
||||||
export class CarpoolPathCreator {
|
export class CarpoolPathCreator {
|
||||||
private PRECISION = 5;
|
private PRECISION = 5;
|
||||||
|
@ -23,29 +23,35 @@ export class CarpoolPathCreator {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a path (a list of waysteps) between driver waypoints
|
* Creates a path (a list of carpoolSteps) between driver waypoints
|
||||||
and passenger waypoints respecting the order
|
and passenger waypoints respecting the order
|
||||||
of the driver waypoints
|
of the driver waypoints
|
||||||
Inspired by :
|
Inspired by :
|
||||||
https://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment
|
https://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment
|
||||||
*/
|
*/
|
||||||
public carpoolPath = (): WayStep[] =>
|
public carpoolPath = (): CarpoolStep[] =>
|
||||||
this._consolidate(
|
this._consolidate(
|
||||||
this._mixedWaysteps(this._driverWaysteps(), this._passengerWaysteps()),
|
this._mixedCarpoolSteps(
|
||||||
|
this._driverCarpoolSteps(),
|
||||||
|
this._passengerCarpoolSteps(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
private _mixedWaysteps = (
|
private _mixedCarpoolSteps = (
|
||||||
driverWaysteps: WayStep[],
|
driverCarpoolSteps: CarpoolStep[],
|
||||||
passengerWaysteps: WayStep[],
|
passengerCarpoolSteps: CarpoolStep[],
|
||||||
): WayStep[] =>
|
): CarpoolStep[] =>
|
||||||
driverWaysteps.length == 2
|
driverCarpoolSteps.length == 2
|
||||||
? this._simpleMixedWaysteps(driverWaysteps, passengerWaysteps)
|
? this._simpleMixedCarpoolSteps(driverCarpoolSteps, passengerCarpoolSteps)
|
||||||
: this._complexMixedWaysteps(driverWaysteps, passengerWaysteps);
|
: this._complexMixedCarpoolSteps(
|
||||||
|
driverCarpoolSteps,
|
||||||
|
passengerCarpoolSteps,
|
||||||
|
);
|
||||||
|
|
||||||
private _driverWaysteps = (): WayStep[] =>
|
private _driverCarpoolSteps = (): CarpoolStep[] =>
|
||||||
this.driverWaypoints.map(
|
this.driverWaypoints.map(
|
||||||
(waypoint: Point, index: number) =>
|
(waypoint: Point, index: number) =>
|
||||||
new WayStep({
|
new CarpoolStep({
|
||||||
point: new Point({
|
point: new Point({
|
||||||
lon: waypoint.lon,
|
lon: waypoint.lon,
|
||||||
lat: waypoint.lat,
|
lat: waypoint.lat,
|
||||||
|
@ -60,13 +66,13 @@ export class CarpoolPathCreator {
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the passenger waysteps with original passenger waypoints, adding driver waypoints that are the same
|
* Creates the passenger carpoolSteps with original passenger waypoints, adding driver waypoints that are the same
|
||||||
*/
|
*/
|
||||||
private _passengerWaysteps = (): WayStep[] => {
|
private _passengerCarpoolSteps = (): CarpoolStep[] => {
|
||||||
const waysteps: WayStep[] = [];
|
const carpoolSteps: CarpoolStep[] = [];
|
||||||
this.passengerWaypoints.forEach(
|
this.passengerWaypoints.forEach(
|
||||||
(passengerWaypoint: Point, index: number) => {
|
(passengerWaypoint: Point, index: number) => {
|
||||||
const waystep: WayStep = new WayStep({
|
const carpoolStep: CarpoolStep = new CarpoolStep({
|
||||||
point: new Point({
|
point: new Point({
|
||||||
lon: passengerWaypoint.lon,
|
lon: passengerWaypoint.lon,
|
||||||
lat: passengerWaypoint.lat,
|
lat: passengerWaypoint.lat,
|
||||||
|
@ -83,73 +89,78 @@ export class CarpoolPathCreator {
|
||||||
passengerWaypoint.isSame(driverWaypoint),
|
passengerWaypoint.isSame(driverWaypoint),
|
||||||
).length == 0
|
).length == 0
|
||||||
) {
|
) {
|
||||||
waystep.actors.push(
|
carpoolStep.actors.push(
|
||||||
new Actor({
|
new Actor({
|
||||||
role: Role.DRIVER,
|
role: Role.DRIVER,
|
||||||
target: Target.NEUTRAL,
|
target: Target.NEUTRAL,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
waysteps.push(waystep);
|
carpoolSteps.push(carpoolStep);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return waysteps;
|
return carpoolSteps;
|
||||||
};
|
};
|
||||||
|
|
||||||
private _simpleMixedWaysteps = (
|
private _simpleMixedCarpoolSteps = (
|
||||||
driverWaysteps: WayStep[],
|
driverCarpoolSteps: CarpoolStep[],
|
||||||
passengerWaysteps: WayStep[],
|
passengerCarpoolSteps: CarpoolStep[],
|
||||||
): WayStep[] => [driverWaysteps[0], ...passengerWaysteps, driverWaysteps[1]];
|
): CarpoolStep[] => [
|
||||||
|
driverCarpoolSteps[0],
|
||||||
|
...passengerCarpoolSteps,
|
||||||
|
driverCarpoolSteps[1],
|
||||||
|
];
|
||||||
|
|
||||||
private _complexMixedWaysteps = (
|
private _complexMixedCarpoolSteps = (
|
||||||
driverWaysteps: WayStep[],
|
driverCarpoolSteps: CarpoolStep[],
|
||||||
passengerWaysteps: WayStep[],
|
passengerCarpoolSteps: CarpoolStep[],
|
||||||
): WayStep[] => {
|
): CarpoolStep[] => {
|
||||||
let mixedWaysteps: WayStep[] = [...driverWaysteps];
|
let mixedCarpoolSteps: CarpoolStep[] = [...driverCarpoolSteps];
|
||||||
const originInsertIndex: number = this._insertIndex(
|
const originInsertIndex: number = this._insertIndex(
|
||||||
passengerWaysteps[0],
|
passengerCarpoolSteps[0],
|
||||||
driverWaysteps,
|
driverCarpoolSteps,
|
||||||
);
|
);
|
||||||
mixedWaysteps = [
|
mixedCarpoolSteps = [
|
||||||
...mixedWaysteps.slice(0, originInsertIndex),
|
...mixedCarpoolSteps.slice(0, originInsertIndex),
|
||||||
passengerWaysteps[0],
|
passengerCarpoolSteps[0],
|
||||||
...mixedWaysteps.slice(originInsertIndex),
|
...mixedCarpoolSteps.slice(originInsertIndex),
|
||||||
];
|
];
|
||||||
const destinationInsertIndex: number =
|
const destinationInsertIndex: number =
|
||||||
this._insertIndex(
|
this._insertIndex(
|
||||||
passengerWaysteps[passengerWaysteps.length - 1],
|
passengerCarpoolSteps[passengerCarpoolSteps.length - 1],
|
||||||
driverWaysteps,
|
driverCarpoolSteps,
|
||||||
) + 1;
|
) + 1;
|
||||||
mixedWaysteps = [
|
mixedCarpoolSteps = [
|
||||||
...mixedWaysteps.slice(0, destinationInsertIndex),
|
...mixedCarpoolSteps.slice(0, destinationInsertIndex),
|
||||||
passengerWaysteps[passengerWaysteps.length - 1],
|
passengerCarpoolSteps[passengerCarpoolSteps.length - 1],
|
||||||
...mixedWaysteps.slice(destinationInsertIndex),
|
...mixedCarpoolSteps.slice(destinationInsertIndex),
|
||||||
];
|
];
|
||||||
return mixedWaysteps;
|
return mixedCarpoolSteps;
|
||||||
};
|
};
|
||||||
|
|
||||||
private _insertIndex = (
|
private _insertIndex = (
|
||||||
targetWaystep: WayStep,
|
targetCarpoolStep: CarpoolStep,
|
||||||
waysteps: WayStep[],
|
carpoolSteps: CarpoolStep[],
|
||||||
): number =>
|
): number =>
|
||||||
this._closestSegmentIndex(targetWaystep, this._segments(waysteps)) + 1;
|
this._closestSegmentIndex(targetCarpoolStep, this._segments(carpoolSteps)) +
|
||||||
|
1;
|
||||||
|
|
||||||
private _segments = (waysteps: WayStep[]): WayStep[][] => {
|
private _segments = (carpoolSteps: CarpoolStep[]): CarpoolStep[][] => {
|
||||||
const segments: WayStep[][] = [];
|
const segments: CarpoolStep[][] = [];
|
||||||
waysteps.forEach((waystep: WayStep, index: number) => {
|
carpoolSteps.forEach((carpoolStep: CarpoolStep, index: number) => {
|
||||||
if (index < waysteps.length - 1)
|
if (index < carpoolSteps.length - 1)
|
||||||
segments.push([waystep, waysteps[index + 1]]);
|
segments.push([carpoolStep, carpoolSteps[index + 1]]);
|
||||||
});
|
});
|
||||||
return segments;
|
return segments;
|
||||||
};
|
};
|
||||||
|
|
||||||
private _closestSegmentIndex = (
|
private _closestSegmentIndex = (
|
||||||
waystep: WayStep,
|
carpoolStep: CarpoolStep,
|
||||||
segments: WayStep[][],
|
segments: CarpoolStep[][],
|
||||||
): number => {
|
): number => {
|
||||||
const distances: Map<number, number> = new Map();
|
const distances: Map<number, number> = new Map();
|
||||||
segments.forEach((segment: WayStep[], index: number) => {
|
segments.forEach((segment: CarpoolStep[], index: number) => {
|
||||||
distances.set(index, this._distanceToSegment(waystep, segment));
|
distances.set(index, this._distanceToSegment(carpoolStep, segment));
|
||||||
});
|
});
|
||||||
const sortedDistances: Map<number, number> = new Map(
|
const sortedDistances: Map<number, number> = new Map(
|
||||||
[...distances.entries()].sort((a, b) => a[1] - b[1]),
|
[...distances.entries()].sort((a, b) => a[1] - b[1]),
|
||||||
|
@ -158,30 +169,33 @@ export class CarpoolPathCreator {
|
||||||
return closestSegmentIndex;
|
return closestSegmentIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
private _distanceToSegment = (waystep: WayStep, segment: WayStep[]): number =>
|
private _distanceToSegment = (
|
||||||
|
carpoolStep: CarpoolStep,
|
||||||
|
segment: CarpoolStep[],
|
||||||
|
): number =>
|
||||||
parseFloat(
|
parseFloat(
|
||||||
Math.sqrt(this._distanceToSegmentSquared(waystep, segment)).toFixed(
|
Math.sqrt(this._distanceToSegmentSquared(carpoolStep, segment)).toFixed(
|
||||||
this.PRECISION,
|
this.PRECISION,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
private _distanceToSegmentSquared = (
|
private _distanceToSegmentSquared = (
|
||||||
waystep: WayStep,
|
carpoolStep: CarpoolStep,
|
||||||
segment: WayStep[],
|
segment: CarpoolStep[],
|
||||||
): number => {
|
): number => {
|
||||||
const length2: number = this._distanceSquared(
|
const length2: number = this._distanceSquared(
|
||||||
segment[0].point,
|
segment[0].point,
|
||||||
segment[1].point,
|
segment[1].point,
|
||||||
);
|
);
|
||||||
if (length2 == 0)
|
if (length2 == 0)
|
||||||
return this._distanceSquared(waystep.point, segment[0].point);
|
return this._distanceSquared(carpoolStep.point, segment[0].point);
|
||||||
const length: number = Math.max(
|
const length: number = Math.max(
|
||||||
0,
|
0,
|
||||||
Math.min(
|
Math.min(
|
||||||
1,
|
1,
|
||||||
((waystep.point.lon - segment[0].point.lon) *
|
((carpoolStep.point.lon - segment[0].point.lon) *
|
||||||
(segment[1].point.lon - segment[0].point.lon) +
|
(segment[1].point.lon - segment[0].point.lon) +
|
||||||
(waystep.point.lat - segment[0].point.lat) *
|
(carpoolStep.point.lat - segment[0].point.lat) *
|
||||||
(segment[1].point.lat - segment[0].point.lat)) /
|
(segment[1].point.lat - segment[0].point.lat)) /
|
||||||
length2,
|
length2,
|
||||||
),
|
),
|
||||||
|
@ -194,7 +208,7 @@ export class CarpoolPathCreator {
|
||||||
segment[0].point.lat +
|
segment[0].point.lat +
|
||||||
length * (segment[1].point.lat - segment[0].point.lat),
|
length * (segment[1].point.lat - segment[0].point.lat),
|
||||||
});
|
});
|
||||||
return this._distanceSquared(waystep.point, newPoint);
|
return this._distanceSquared(carpoolStep.point, newPoint);
|
||||||
};
|
};
|
||||||
|
|
||||||
private _distanceSquared = (point1: Point, point2: Point): number =>
|
private _distanceSquared = (point1: Point, point2: Point): number =>
|
||||||
|
@ -213,29 +227,31 @@ export class CarpoolPathCreator {
|
||||||
: Target.INTERMEDIATE;
|
: Target.INTERMEDIATE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Consolidate waysteps by removing duplicate actors (eg. driver with neutral and start or finish target)
|
* Consolidate carpoolSteps by removing duplicate actors (eg. driver with neutral and start or finish target)
|
||||||
*/
|
*/
|
||||||
private _consolidate = (waysteps: WayStep[]): WayStep[] => {
|
private _consolidate = (carpoolSteps: CarpoolStep[]): CarpoolStep[] => {
|
||||||
const uniquePoints: Point[] = [];
|
const uniquePoints: Point[] = [];
|
||||||
waysteps.forEach((waystep: WayStep) => {
|
carpoolSteps.forEach((carpoolStep: CarpoolStep) => {
|
||||||
if (
|
if (
|
||||||
uniquePoints.find((point: Point) => point.isSame(waystep.point)) ===
|
uniquePoints.find((point: Point) => point.isSame(carpoolStep.point)) ===
|
||||||
undefined
|
undefined
|
||||||
)
|
)
|
||||||
uniquePoints.push(
|
uniquePoints.push(
|
||||||
new Point({
|
new Point({
|
||||||
lon: waystep.point.lon,
|
lon: carpoolStep.point.lon,
|
||||||
lat: waystep.point.lat,
|
lat: carpoolStep.point.lat,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
return uniquePoints.map(
|
return uniquePoints.map(
|
||||||
(point: Point) =>
|
(point: Point) =>
|
||||||
new WayStep({
|
new CarpoolStep({
|
||||||
point,
|
point,
|
||||||
actors: waysteps
|
actors: carpoolSteps
|
||||||
.filter((waystep: WayStep) => waystep.point.isSame(point))
|
.filter((carpoolStep: CarpoolStep) =>
|
||||||
.map((waystep: WayStep) => waystep.actors)
|
carpoolStep.point.isSame(point),
|
||||||
|
)
|
||||||
|
.map((carpoolStep: CarpoolStep) => carpoolStep.actors)
|
||||||
.flat(),
|
.flat(),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Route } from '@modules/geography/core/domain/route.types';
|
|
||||||
import { Role } from './ad.types';
|
import { Role } from './ad.types';
|
||||||
import { Point } from './value-objects/point.value-object';
|
import { Point } from './value-objects/point.value-object';
|
||||||
import { PathCreatorException } from './match.errors';
|
import { PathCreatorException } from './match.errors';
|
||||||
|
import { Route } from '../application/types/route.type';
|
||||||
|
|
||||||
export class PathCreator {
|
export class PathCreator {
|
||||||
constructor(
|
constructor(
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
import { ArgumentInvalidException, ValueObject } from '@mobicoop/ddd-library';
|
||||||
|
import { Role } from '../ad.types';
|
||||||
|
import { Target } from '../candidate.types';
|
||||||
|
import { ActorProps } from './actor.value-object';
|
||||||
|
|
||||||
|
/** Note:
|
||||||
|
* Value Objects with multiple properties can contain
|
||||||
|
* other Value Objects inside if needed.
|
||||||
|
* */
|
||||||
|
|
||||||
|
export interface ActorTimeProps extends ActorProps {
|
||||||
|
time: string;
|
||||||
|
minTime: string;
|
||||||
|
maxTime: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ActorTime extends ValueObject<ActorTimeProps> {
|
||||||
|
get time(): string {
|
||||||
|
return this.props.time;
|
||||||
|
}
|
||||||
|
|
||||||
|
get minTime(): string {
|
||||||
|
return this.props.minTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
get maxTime(): string {
|
||||||
|
return this.props.maxTime;
|
||||||
|
}
|
||||||
|
get role(): Role {
|
||||||
|
return this.props.role;
|
||||||
|
}
|
||||||
|
|
||||||
|
get target(): Target {
|
||||||
|
return this.props.target;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected validate(props: ActorTimeProps): void {
|
||||||
|
this._validateTime(props.time, 'time');
|
||||||
|
this._validateTime(props.minTime, 'minTime');
|
||||||
|
this._validateTime(props.maxTime, 'maxTime');
|
||||||
|
}
|
||||||
|
|
||||||
|
private _validateTime(time: string, property: string): void {
|
||||||
|
if (time.split(':').length != 2)
|
||||||
|
throw new ArgumentInvalidException(`${property} is invalid`);
|
||||||
|
if (parseInt(time.split(':')[0]) < 0 || parseInt(time.split(':')[0]) > 23)
|
||||||
|
throw new ArgumentInvalidException(`${property} is invalid`);
|
||||||
|
if (parseInt(time.split(':')[1]) < 0 || parseInt(time.split(':')[1]) > 59)
|
||||||
|
throw new ArgumentInvalidException(`${property} is invalid`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,12 +11,12 @@ import { Point } from './point.value-object';
|
||||||
* other Value Objects inside if needed.
|
* other Value Objects inside if needed.
|
||||||
* */
|
* */
|
||||||
|
|
||||||
export interface WayStepProps {
|
export interface CarpoolStepProps {
|
||||||
point: Point;
|
point: Point;
|
||||||
actors: Actor[];
|
actors: Actor[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WayStep extends ValueObject<WayStepProps> {
|
export class CarpoolStep extends ValueObject<CarpoolStepProps> {
|
||||||
get point(): Point {
|
get point(): Point {
|
||||||
return this.props.point;
|
return this.props.point;
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ export class WayStep extends ValueObject<WayStepProps> {
|
||||||
return this.props.actors;
|
return this.props.actors;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected validate(props: WayStepProps): void {
|
protected validate(props: CarpoolStepProps): void {
|
||||||
if (props.actors.length <= 0)
|
if (props.actors.length <= 0)
|
||||||
throw new ArgumentOutOfRangeException('at least one actor is required');
|
throw new ArgumentOutOfRangeException('at least one actor is required');
|
||||||
if (
|
if (
|
||||||
|
@ -33,7 +33,7 @@ export class WayStep extends ValueObject<WayStepProps> {
|
||||||
1
|
1
|
||||||
)
|
)
|
||||||
throw new ArgumentOutOfRangeException(
|
throw new ArgumentOutOfRangeException(
|
||||||
'a waystep can contain only one driver',
|
'a carpoolStep can contain only one driver',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
import {
|
||||||
|
ArgumentOutOfRangeException,
|
||||||
|
ValueObject,
|
||||||
|
} from '@mobicoop/ddd-library';
|
||||||
|
import { ScheduleItemProps } from './schedule-item.value-object';
|
||||||
|
import { ActorTime } from './actor-time.value-object';
|
||||||
|
|
||||||
|
/** Note:
|
||||||
|
* Value Objects with multiple properties can contain
|
||||||
|
* other Value Objects inside if needed.
|
||||||
|
* */
|
||||||
|
|
||||||
|
export interface JourneyProps extends ScheduleItemProps {
|
||||||
|
firstDate: Date;
|
||||||
|
lastDate: Date;
|
||||||
|
actorTimes: ActorTime[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Journey extends ValueObject<JourneyProps> {
|
||||||
|
get firstDate(): Date {
|
||||||
|
return this.props.firstDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
get lastDate(): Date {
|
||||||
|
return this.props.lastDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
get actorTimes(): ActorTime[] {
|
||||||
|
return this.props.actorTimes;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected validate(props: JourneyProps): void {
|
||||||
|
if (props.firstDate > props.lastDate)
|
||||||
|
throw new ArgumentOutOfRangeException(
|
||||||
|
'firstDate must be before lastDate',
|
||||||
|
);
|
||||||
|
if (props.actorTimes.length <= 0)
|
||||||
|
throw new ArgumentOutOfRangeException(
|
||||||
|
'at least one actorTime is required',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,7 +28,6 @@ export class ScheduleItem extends ValueObject<ScheduleItemProps> {
|
||||||
return this.props.margin;
|
return this.props.margin;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
protected validate(props: ScheduleItemProps): void {
|
protected validate(props: ScheduleItemProps): void {
|
||||||
if (props.day < 0 || props.day > 6)
|
if (props.day < 0 || props.day > 6)
|
||||||
throw new ArgumentOutOfRangeException('day must be between 0 and 6');
|
throw new ArgumentOutOfRangeException('day must be between 0 and 6');
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
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 StepProps extends PointProps {
|
||||||
|
duration: number;
|
||||||
|
distance?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Step extends ValueObject<StepProps> {
|
||||||
|
get duration(): number {
|
||||||
|
return this.props.duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
get distance(): number | undefined {
|
||||||
|
return this.props.distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
get lon(): number {
|
||||||
|
return this.props.lon;
|
||||||
|
}
|
||||||
|
|
||||||
|
get lat(): number {
|
||||||
|
return this.props.lat;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected validate(props: StepProps): void {
|
||||||
|
if (props.duration < 0)
|
||||||
|
throw new ArgumentInvalidException(
|
||||||
|
'duration must be greater than or equal to 0',
|
||||||
|
);
|
||||||
|
if (props.distance !== undefined && props.distance < 0)
|
||||||
|
throw new ArgumentInvalidException(
|
||||||
|
'distance must be greater than or equal to 0',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
import { CarpoolPathCreator } from '@modules/ad/core/domain/carpool-path-creator.service';
|
import { CarpoolPathCreator } from '@modules/ad/core/domain/carpool-path-creator.service';
|
||||||
import { CarpoolPathCreatorException } from '@modules/ad/core/domain/match.errors';
|
import { CarpoolPathCreatorException } from '@modules/ad/core/domain/match.errors';
|
||||||
import { Point } from '@modules/ad/core/domain/value-objects/point.value-object';
|
import { Point } from '@modules/ad/core/domain/value-objects/point.value-object';
|
||||||
import { WayStep } from '@modules/ad/core/domain/value-objects/waystep.value-object';
|
import { CarpoolStep } from '@modules/ad/core/domain/value-objects/carpool-step.value-object';
|
||||||
|
|
||||||
const waypoint1: Point = new Point({
|
const waypoint1: Point = new Point({
|
||||||
lat: 0,
|
lat: 0,
|
||||||
|
@ -34,71 +34,71 @@ describe('Carpool Path Creator Service', () => {
|
||||||
[waypoint1, waypoint6],
|
[waypoint1, waypoint6],
|
||||||
[waypoint2, waypoint5],
|
[waypoint2, waypoint5],
|
||||||
);
|
);
|
||||||
const waysteps: WayStep[] = carpoolPathCreator.carpoolPath();
|
const carpoolSteps: CarpoolStep[] = carpoolPathCreator.carpoolPath();
|
||||||
expect(waysteps).toHaveLength(4);
|
expect(carpoolSteps).toHaveLength(4);
|
||||||
expect(waysteps[0].actors.length).toBe(1);
|
expect(carpoolSteps[0].actors.length).toBe(1);
|
||||||
});
|
});
|
||||||
it('should create a simple carpool path with same destination for driver and passenger', () => {
|
it('should create a simple carpool path with same destination for driver and passenger', () => {
|
||||||
const carpoolPathCreator: CarpoolPathCreator = new CarpoolPathCreator(
|
const carpoolPathCreator: CarpoolPathCreator = new CarpoolPathCreator(
|
||||||
[waypoint1, waypoint6],
|
[waypoint1, waypoint6],
|
||||||
[waypoint2, waypoint6],
|
[waypoint2, waypoint6],
|
||||||
);
|
);
|
||||||
const waysteps: WayStep[] = carpoolPathCreator.carpoolPath();
|
const carpoolSteps: CarpoolStep[] = carpoolPathCreator.carpoolPath();
|
||||||
expect(waysteps).toHaveLength(3);
|
expect(carpoolSteps).toHaveLength(3);
|
||||||
expect(waysteps[0].actors.length).toBe(1);
|
expect(carpoolSteps[0].actors.length).toBe(1);
|
||||||
expect(waysteps[1].actors.length).toBe(2);
|
expect(carpoolSteps[1].actors.length).toBe(2);
|
||||||
expect(waysteps[2].actors.length).toBe(2);
|
expect(carpoolSteps[2].actors.length).toBe(2);
|
||||||
});
|
});
|
||||||
it('should create a simple carpool path with same waypoints for driver and passenger', () => {
|
it('should create a simple carpool path with same waypoints for driver and passenger', () => {
|
||||||
const carpoolPathCreator: CarpoolPathCreator = new CarpoolPathCreator(
|
const carpoolPathCreator: CarpoolPathCreator = new CarpoolPathCreator(
|
||||||
[waypoint1, waypoint6],
|
[waypoint1, waypoint6],
|
||||||
[waypoint1, waypoint6],
|
[waypoint1, waypoint6],
|
||||||
);
|
);
|
||||||
const waysteps: WayStep[] = carpoolPathCreator.carpoolPath();
|
const carpoolSteps: CarpoolStep[] = carpoolPathCreator.carpoolPath();
|
||||||
expect(waysteps).toHaveLength(2);
|
expect(carpoolSteps).toHaveLength(2);
|
||||||
expect(waysteps[0].actors.length).toBe(2);
|
expect(carpoolSteps[0].actors.length).toBe(2);
|
||||||
expect(waysteps[1].actors.length).toBe(2);
|
expect(carpoolSteps[1].actors.length).toBe(2);
|
||||||
});
|
});
|
||||||
it('should create a complex carpool path with 3 driver waypoints', () => {
|
it('should create a complex carpool path with 3 driver waypoints', () => {
|
||||||
const carpoolPathCreator: CarpoolPathCreator = new CarpoolPathCreator(
|
const carpoolPathCreator: CarpoolPathCreator = new CarpoolPathCreator(
|
||||||
[waypoint1, waypoint3, waypoint6],
|
[waypoint1, waypoint3, waypoint6],
|
||||||
[waypoint2, waypoint5],
|
[waypoint2, waypoint5],
|
||||||
);
|
);
|
||||||
const waysteps: WayStep[] = carpoolPathCreator.carpoolPath();
|
const carpoolSteps: CarpoolStep[] = carpoolPathCreator.carpoolPath();
|
||||||
expect(waysteps).toHaveLength(5);
|
expect(carpoolSteps).toHaveLength(5);
|
||||||
expect(waysteps[0].actors.length).toBe(1);
|
expect(carpoolSteps[0].actors.length).toBe(1);
|
||||||
expect(waysteps[1].actors.length).toBe(2);
|
expect(carpoolSteps[1].actors.length).toBe(2);
|
||||||
expect(waysteps[2].actors.length).toBe(1);
|
expect(carpoolSteps[2].actors.length).toBe(1);
|
||||||
expect(waysteps[3].actors.length).toBe(2);
|
expect(carpoolSteps[3].actors.length).toBe(2);
|
||||||
expect(waysteps[4].actors.length).toBe(1);
|
expect(carpoolSteps[4].actors.length).toBe(1);
|
||||||
});
|
});
|
||||||
it('should create a complex carpool path with 4 driver waypoints', () => {
|
it('should create a complex carpool path with 4 driver waypoints', () => {
|
||||||
const carpoolPathCreator: CarpoolPathCreator = new CarpoolPathCreator(
|
const carpoolPathCreator: CarpoolPathCreator = new CarpoolPathCreator(
|
||||||
[waypoint1, waypoint3, waypoint4, waypoint6],
|
[waypoint1, waypoint3, waypoint4, waypoint6],
|
||||||
[waypoint2, waypoint5],
|
[waypoint2, waypoint5],
|
||||||
);
|
);
|
||||||
const waysteps: WayStep[] = carpoolPathCreator.carpoolPath();
|
const carpoolSteps: CarpoolStep[] = carpoolPathCreator.carpoolPath();
|
||||||
expect(waysteps).toHaveLength(6);
|
expect(carpoolSteps).toHaveLength(6);
|
||||||
expect(waysteps[0].actors.length).toBe(1);
|
expect(carpoolSteps[0].actors.length).toBe(1);
|
||||||
expect(waysteps[1].actors.length).toBe(2);
|
expect(carpoolSteps[1].actors.length).toBe(2);
|
||||||
expect(waysteps[2].actors.length).toBe(1);
|
expect(carpoolSteps[2].actors.length).toBe(1);
|
||||||
expect(waysteps[3].actors.length).toBe(1);
|
expect(carpoolSteps[3].actors.length).toBe(1);
|
||||||
expect(waysteps[4].actors.length).toBe(2);
|
expect(carpoolSteps[4].actors.length).toBe(2);
|
||||||
expect(waysteps[5].actors.length).toBe(1);
|
expect(carpoolSteps[5].actors.length).toBe(1);
|
||||||
});
|
});
|
||||||
it('should create a alternate complex carpool path with 4 driver waypoints', () => {
|
it('should create a alternate complex carpool path with 4 driver waypoints', () => {
|
||||||
const carpoolPathCreator: CarpoolPathCreator = new CarpoolPathCreator(
|
const carpoolPathCreator: CarpoolPathCreator = new CarpoolPathCreator(
|
||||||
[waypoint1, waypoint2, waypoint5, waypoint6],
|
[waypoint1, waypoint2, waypoint5, waypoint6],
|
||||||
[waypoint3, waypoint4],
|
[waypoint3, waypoint4],
|
||||||
);
|
);
|
||||||
const waysteps: WayStep[] = carpoolPathCreator.carpoolPath();
|
const carpoolSteps: CarpoolStep[] = carpoolPathCreator.carpoolPath();
|
||||||
expect(waysteps).toHaveLength(6);
|
expect(carpoolSteps).toHaveLength(6);
|
||||||
expect(waysteps[0].actors.length).toBe(1);
|
expect(carpoolSteps[0].actors.length).toBe(1);
|
||||||
expect(waysteps[1].actors.length).toBe(1);
|
expect(carpoolSteps[1].actors.length).toBe(1);
|
||||||
expect(waysteps[2].actors.length).toBe(2);
|
expect(carpoolSteps[2].actors.length).toBe(2);
|
||||||
expect(waysteps[3].actors.length).toBe(2);
|
expect(carpoolSteps[3].actors.length).toBe(2);
|
||||||
expect(waysteps[4].actors.length).toBe(1);
|
expect(carpoolSteps[4].actors.length).toBe(1);
|
||||||
expect(waysteps[5].actors.length).toBe(1);
|
expect(carpoolSteps[5].actors.length).toBe(1);
|
||||||
});
|
});
|
||||||
it('should throw an exception if less than 2 driver waypoints are given', () => {
|
it('should throw an exception if less than 2 driver waypoints are given', () => {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -3,11 +3,11 @@ import { Role } from '@modules/ad/core/domain/ad.types';
|
||||||
import { Target } from '@modules/ad/core/domain/candidate.types';
|
import { Target } from '@modules/ad/core/domain/candidate.types';
|
||||||
import { Actor } from '@modules/ad/core/domain/value-objects/actor.value-object';
|
import { Actor } from '@modules/ad/core/domain/value-objects/actor.value-object';
|
||||||
import { Point } from '@modules/ad/core/domain/value-objects/point.value-object';
|
import { Point } from '@modules/ad/core/domain/value-objects/point.value-object';
|
||||||
import { WayStep } from '@modules/ad/core/domain/value-objects/waystep.value-object';
|
import { CarpoolStep } from '@modules/ad/core/domain/value-objects/carpool-step.value-object';
|
||||||
|
|
||||||
describe('WayStep value object', () => {
|
describe('CarpoolStep value object', () => {
|
||||||
it('should create a waystep value object', () => {
|
it('should create a carpoolStep value object', () => {
|
||||||
const wayStepVO = new WayStep({
|
const carpoolStepVO = new CarpoolStep({
|
||||||
point: new Point({
|
point: new Point({
|
||||||
lat: 48.689445,
|
lat: 48.689445,
|
||||||
lon: 6.17651,
|
lon: 6.17651,
|
||||||
|
@ -23,13 +23,13 @@ describe('WayStep value object', () => {
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
expect(wayStepVO.point.lon).toBe(6.17651);
|
expect(carpoolStepVO.point.lon).toBe(6.17651);
|
||||||
expect(wayStepVO.point.lat).toBe(48.689445);
|
expect(carpoolStepVO.point.lat).toBe(48.689445);
|
||||||
expect(wayStepVO.actors).toHaveLength(2);
|
expect(carpoolStepVO.actors).toHaveLength(2);
|
||||||
});
|
});
|
||||||
it('should throw an exception if actors is empty', () => {
|
it('should throw an exception if actors is empty', () => {
|
||||||
try {
|
try {
|
||||||
new WayStep({
|
new CarpoolStep({
|
||||||
point: new Point({
|
point: new Point({
|
||||||
lat: 48.689445,
|
lat: 48.689445,
|
||||||
lon: 6.17651,
|
lon: 6.17651,
|
||||||
|
@ -42,7 +42,7 @@ describe('WayStep value object', () => {
|
||||||
});
|
});
|
||||||
it('should throw an exception if actors contains more than one driver', () => {
|
it('should throw an exception if actors contains more than one driver', () => {
|
||||||
try {
|
try {
|
||||||
new WayStep({
|
new CarpoolStep({
|
||||||
point: new Point({
|
point: new Point({
|
||||||
lat: 48.689445,
|
lat: 48.689445,
|
||||||
lon: 6.17651,
|
lon: 6.17651,
|
|
@ -0,0 +1,151 @@
|
||||||
|
import { JourneyCompleter } from '@modules/ad/core/application/queries/match/completer/journey.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';
|
||||||
|
import { Frequency, Role } from '@modules/ad/core/domain/ad.types';
|
||||||
|
import { CandidateEntity } from '@modules/ad/core/domain/candidate.entity';
|
||||||
|
import { Target } from '@modules/ad/core/domain/candidate.types';
|
||||||
|
import { Actor } from '@modules/ad/core/domain/value-objects/actor.value-object';
|
||||||
|
import { Point } from '@modules/ad/core/domain/value-objects/point.value-object';
|
||||||
|
|
||||||
|
const originWaypoint: Waypoint = {
|
||||||
|
position: 0,
|
||||||
|
lat: 48.689445,
|
||||||
|
lon: 6.17651,
|
||||||
|
houseNumber: '5',
|
||||||
|
street: 'Avenue Foch',
|
||||||
|
locality: 'Nancy',
|
||||||
|
postalCode: '54000',
|
||||||
|
country: 'France',
|
||||||
|
};
|
||||||
|
const destinationWaypoint: Waypoint = {
|
||||||
|
position: 1,
|
||||||
|
lat: 48.8566,
|
||||||
|
lon: 2.3522,
|
||||||
|
locality: 'Paris',
|
||||||
|
postalCode: '75000',
|
||||||
|
country: 'France',
|
||||||
|
};
|
||||||
|
|
||||||
|
const matchQuery = new MatchQuery(
|
||||||
|
{
|
||||||
|
algorithmType: AlgorithmType.PASSENGER_ORIENTED,
|
||||||
|
driver: true,
|
||||||
|
passenger: true,
|
||||||
|
frequency: Frequency.PUNCTUAL,
|
||||||
|
fromDate: '2023-08-28',
|
||||||
|
toDate: '2023-08-28',
|
||||||
|
schedule: [
|
||||||
|
{
|
||||||
|
time: '07:05',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
strict: false,
|
||||||
|
waypoints: [originWaypoint, destinationWaypoint],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
getBasic: jest.fn().mockImplementation(() => ({
|
||||||
|
distance: 350101,
|
||||||
|
duration: 14422,
|
||||||
|
fwdAzimuth: 273,
|
||||||
|
backAzimuth: 93,
|
||||||
|
distanceAzimuth: 336544,
|
||||||
|
points: [],
|
||||||
|
})),
|
||||||
|
getDetailed: jest.fn().mockImplementation(() => ({
|
||||||
|
distance: 350102,
|
||||||
|
duration: 14423,
|
||||||
|
fwdAzimuth: 273,
|
||||||
|
backAzimuth: 93,
|
||||||
|
distanceAzimuth: 336544,
|
||||||
|
points: [],
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const candidate: CandidateEntity = CandidateEntity.create({
|
||||||
|
id: 'cc260669-1c6d-441f-80a5-19cd59afb777',
|
||||||
|
role: Role.DRIVER,
|
||||||
|
driverWaypoints: [
|
||||||
|
{
|
||||||
|
lat: 48.678454,
|
||||||
|
lon: 6.189745,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lat: 48.84877,
|
||||||
|
lon: 2.398457,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
passengerWaypoints: [
|
||||||
|
{
|
||||||
|
lat: 48.689445,
|
||||||
|
lon: 6.17651,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lat: 48.8566,
|
||||||
|
lon: 2.3522,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
driverDistance: 350145,
|
||||||
|
driverDuration: 13548,
|
||||||
|
driverSchedule: [
|
||||||
|
{
|
||||||
|
day: 0,
|
||||||
|
time: '07:00',
|
||||||
|
margin: 900,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
passengerSchedule: [
|
||||||
|
{
|
||||||
|
day: 0,
|
||||||
|
time: '07:10',
|
||||||
|
margin: 900,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
spacetimeDetourRatio: {
|
||||||
|
maxDistanceDetourRatio: 0.3,
|
||||||
|
maxDurationDetourRatio: 0.3,
|
||||||
|
},
|
||||||
|
}).setCarpoolPath([
|
||||||
|
{
|
||||||
|
point: new Point({
|
||||||
|
lat: 48.689445,
|
||||||
|
lon: 6.17651,
|
||||||
|
}),
|
||||||
|
actors: [
|
||||||
|
new Actor({
|
||||||
|
role: Role.DRIVER,
|
||||||
|
target: Target.START,
|
||||||
|
}),
|
||||||
|
new Actor({
|
||||||
|
role: Role.PASSENGER,
|
||||||
|
target: Target.START,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
point: new Point({
|
||||||
|
lat: 48.8566,
|
||||||
|
lon: 2.3522,
|
||||||
|
}),
|
||||||
|
actors: [
|
||||||
|
new Actor({
|
||||||
|
role: Role.DRIVER,
|
||||||
|
target: Target.FINISH,
|
||||||
|
}),
|
||||||
|
new Actor({
|
||||||
|
role: Role.PASSENGER,
|
||||||
|
target: Target.FINISH,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
describe('Journey completer', () => {
|
||||||
|
it('should complete candidates with their journey', async () => {
|
||||||
|
const journeyCompleter: JourneyCompleter = new JourneyCompleter(matchQuery);
|
||||||
|
const completedCandidates: CandidateEntity[] =
|
||||||
|
await journeyCompleter.complete([candidate]);
|
||||||
|
expect(completedCandidates.length).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
|
@ -56,12 +56,13 @@ const matchQuery = new MatchQuery(
|
||||||
points: [],
|
points: [],
|
||||||
})),
|
})),
|
||||||
getDetailed: jest.fn().mockImplementation(() => ({
|
getDetailed: jest.fn().mockImplementation(() => ({
|
||||||
distance: 350102,
|
distance: 350101,
|
||||||
duration: 14423,
|
duration: 14422,
|
||||||
fwdAzimuth: 273,
|
fwdAzimuth: 273,
|
||||||
backAzimuth: 93,
|
backAzimuth: 93,
|
||||||
distanceAzimuth: 336544,
|
distanceAzimuth: 336544,
|
||||||
points: [],
|
points: [],
|
||||||
|
steps: [jest.fn(), jest.fn(), jest.fn(), jest.fn()],
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -163,6 +164,6 @@ describe('Route completer', () => {
|
||||||
const completedCandidates: CandidateEntity[] =
|
const completedCandidates: CandidateEntity[] =
|
||||||
await routeCompleter.complete([candidate]);
|
await routeCompleter.complete([candidate]);
|
||||||
expect(completedCandidates.length).toBe(1);
|
expect(completedCandidates.length).toBe(1);
|
||||||
expect(completedCandidates[0].getProps().distance).toBe(350102);
|
expect(completedCandidates[0].getProps().steps).toHaveLength(4);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,9 +3,9 @@ import {
|
||||||
AD_GET_DETAILED_ROUTE_CONTROLLER,
|
AD_GET_DETAILED_ROUTE_CONTROLLER,
|
||||||
} from '@modules/ad/ad.di-tokens';
|
} from '@modules/ad/ad.di-tokens';
|
||||||
import { Point } from '@modules/ad/core/application/types/point.type';
|
import { Point } from '@modules/ad/core/application/types/point.type';
|
||||||
|
import { Route } from '@modules/ad/core/application/types/route.type';
|
||||||
import { RouteProvider } from '@modules/ad/infrastructure/route-provider';
|
import { RouteProvider } from '@modules/ad/infrastructure/route-provider';
|
||||||
import { GetRouteControllerPort } from '@modules/geography/core/application/ports/get-route-controller.port';
|
import { GetRouteControllerPort } from '@modules/geography/core/application/ports/get-route-controller.port';
|
||||||
import { Route } from '@modules/geography/core/domain/route.types';
|
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
|
||||||
const originPoint: Point = {
|
const originPoint: Point = {
|
||||||
|
|
Loading…
Reference in New Issue