diff --git a/src/modules/ad/core/application/queries/match/selector/passenger-oriented.selector.ts b/src/modules/ad/core/application/queries/match/selector/passenger-oriented.selector.ts index fe9e67b..5c828ea 100644 --- a/src/modules/ad/core/application/queries/match/selector/passenger-oriented.selector.ts +++ b/src/modules/ad/core/application/queries/match/selector/passenger-oriented.selector.ts @@ -4,7 +4,7 @@ import { Selector } from '../algorithm.abstract'; import { AdReadModel } from '@modules/ad/infrastructure/ad.repository'; import { ScheduleItem } from '../match.query'; import { Waypoint } from '../../../types/waypoint.type'; -import { Coordinates } from '../../../types/coordinates.type'; +import { Point } from '../../../types/point.type'; export class PassengerOrientedSelector extends Selector { select = async (): Promise => { @@ -238,7 +238,7 @@ export class PassengerOrientedSelector extends Selector { ${this.query.remoteness}`; case Role.DRIVER: const lineStringPoints: string[] = []; - this.query.carpoolRoute?.points.forEach((point: Coordinates) => + this.query.carpoolRoute?.points.forEach((point: Point) => lineStringPoints.push( `public.st_makepoint(${point.lon},${point.lat})`, ), diff --git a/src/modules/ad/core/application/types/address.type.ts b/src/modules/ad/core/application/types/address.type.ts index e37174a..af02842 100644 --- a/src/modules/ad/core/application/types/address.type.ts +++ b/src/modules/ad/core/application/types/address.type.ts @@ -1,4 +1,4 @@ -import { Coordinates } from './coordinates.type'; +import { Point } from './point.type'; export type Address = { name?: string; @@ -7,4 +7,4 @@ export type Address = { locality?: string; postalCode?: string; country?: string; -} & Coordinates; +} & Point; diff --git a/src/modules/ad/core/application/types/carpool-route.type.ts b/src/modules/ad/core/application/types/carpool-route.type.ts index 7f07fba..b219701 100644 --- a/src/modules/ad/core/application/types/carpool-route.type.ts +++ b/src/modules/ad/core/application/types/carpool-route.type.ts @@ -1,4 +1,4 @@ -import { Coordinates } from './coordinates.type'; +import { Point } from './point.type'; /** * A carpool route is a route with distance and duration as driver and / or passenger @@ -10,5 +10,5 @@ export type CarpoolRoute = { passengerDuration?: number; fwdAzimuth: number; backAzimuth: number; - points: Coordinates[]; + points: Point[]; }; diff --git a/src/modules/ad/core/application/types/coordinates.type.ts b/src/modules/ad/core/application/types/point.type.ts similarity index 54% rename from src/modules/ad/core/application/types/coordinates.type.ts rename to src/modules/ad/core/application/types/point.type.ts index 8e149ed..9bb160e 100644 --- a/src/modules/ad/core/application/types/coordinates.type.ts +++ b/src/modules/ad/core/application/types/point.type.ts @@ -1,4 +1,4 @@ -export type Coordinates = { +export type Point = { lon: number; lat: number; }; diff --git a/src/modules/ad/infrastructure/carpool-route-provider.ts b/src/modules/ad/infrastructure/carpool-route-provider.ts index 97bd5c9..5cdb4dd 100644 --- a/src/modules/ad/infrastructure/carpool-route-provider.ts +++ b/src/modules/ad/infrastructure/carpool-route-provider.ts @@ -5,6 +5,7 @@ import { Waypoint } from '../core/application/types/waypoint.type'; import { Role } from '../core/domain/ad.types'; 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 } from '@modules/geography/core/domain/route.types'; @Injectable() export class CarpoolRouteProvider implements CarpoolRouteProviderPort { @@ -16,9 +17,116 @@ export class CarpoolRouteProvider implements CarpoolRouteProviderPort { getBasic = async ( roles: Role[], waypoints: Waypoint[], - ): Promise => - await this.getBasicRouteController.get({ - roles, - waypoints, - }); + ): Promise => { + const paths: Path[] = this.getPaths(roles, waypoints); + const typeRoutes: TypeRoute[] = await Promise.all( + paths.map( + async (path: Path) => + { + type: path.type, + route: await this.getBasicRouteController.get({ + waypoints, + }), + }, + ), + ); + return this._toCarpoolRoute(typeRoutes); + }; + + private _toCarpoolRoute = (typeRoutes: TypeRoute[]): CarpoolRoute => { + let baseRoute: Route; + let driverRoute: Route | undefined; + let passengerRoute: Route | undefined; + if ( + typeRoutes.some( + (typeRoute: TypeRoute) => typeRoute.type == PathType.GENERIC, + ) + ) { + driverRoute = passengerRoute = typeRoutes.find( + (typeRoute: TypeRoute) => typeRoute.type == PathType.GENERIC, + )?.route; + } else { + driverRoute = typeRoutes.some( + (typeRoute: TypeRoute) => typeRoute.type == PathType.DRIVER, + ) + ? typeRoutes.find( + (typeRoute: TypeRoute) => typeRoute.type == PathType.DRIVER, + )?.route + : undefined; + passengerRoute = typeRoutes.some( + (typeRoute: TypeRoute) => typeRoute.type == PathType.PASSENGER, + ) + ? typeRoutes.find( + (typeRoute: TypeRoute) => typeRoute.type == PathType.PASSENGER, + )?.route + : undefined; + } + if (driverRoute) { + baseRoute = driverRoute; + } else { + baseRoute = passengerRoute as Route; + } + return { + driverDistance: driverRoute?.distance, + driverDuration: driverRoute?.duration, + passengerDistance: passengerRoute?.distance, + passengerDuration: passengerRoute?.duration, + fwdAzimuth: baseRoute.fwdAzimuth, + backAzimuth: baseRoute.backAzimuth, + points: baseRoute.points, + }; + }; + + private getPaths = (roles: Role[], waypoints: Waypoint[]): Path[] => { + const paths: Path[] = []; + if (roles.includes(Role.DRIVER) && roles.includes(Role.PASSENGER)) { + if (waypoints.length == 2) { + // 2 points => same route for driver and passenger + paths.push(this.createGenericPath(waypoints)); + } else { + paths.push( + this.createDriverPath(waypoints), + this.createPassengerPath(waypoints), + ); + } + } else if (roles.includes(Role.DRIVER)) { + paths.push(this.createDriverPath(waypoints)); + } else if (roles.includes(Role.PASSENGER)) { + paths.push(this.createPassengerPath(waypoints)); + } + return paths; + }; + + private createGenericPath = (waypoints: Waypoint[]): Path => + this.createPath(waypoints, PathType.GENERIC); + + private createDriverPath = (waypoints: Waypoint[]): Path => + this.createPath(waypoints, PathType.DRIVER); + + private createPassengerPath = (waypoints: Waypoint[]): Path => + this.createPath( + [waypoints[0], waypoints[waypoints.length - 1]], + PathType.PASSENGER, + ); + + private createPath = (waypoints: Waypoint[], type: PathType): Path => ({ + type, + waypoints, + }); +} + +type Path = { + type: PathType; + waypoints: Waypoint[]; +}; + +type TypeRoute = { + type: PathType; + route: Route; +}; + +enum PathType { + GENERIC = 'generic', + DRIVER = 'driver', + PASSENGER = 'passenger', } diff --git a/src/modules/ad/tests/unit/infrastructure/carpool-route-provider.spec.ts b/src/modules/ad/tests/unit/infrastructure/carpool-route-provider.spec.ts index fb35530..3e96571 100644 --- a/src/modules/ad/tests/unit/infrastructure/carpool-route-provider.spec.ts +++ b/src/modules/ad/tests/unit/infrastructure/carpool-route-provider.spec.ts @@ -1,30 +1,133 @@ import { AD_GET_BASIC_ROUTE_CONTROLLER } from '@modules/ad/ad.di-tokens'; import { CarpoolRoute } from '@modules/ad/core/application/types/carpool-route.type'; +import { Point } from '@modules/ad/core/application/types/point.type'; import { Role } from '@modules/ad/core/domain/ad.types'; import { CarpoolRouteProvider } from '@modules/ad/infrastructure/carpool-route-provider'; import { GetBasicRouteControllerPort } from '@modules/geography/core/application/ports/get-basic-route-controller.port'; import { Test, TestingModule } from '@nestjs/testing'; +const originPoint: Point = { + lat: 48.689445, + lon: 6.17651, +}; +const destinationPoint: Point = { + lat: 48.8566, + lon: 2.3522, +}; +const additionalPoint: Point = { + lon: 48.7566, + lat: 4.4498, +}; + const mockGetBasicRouteController: GetBasicRouteControllerPort = { - get: jest.fn().mockImplementation(() => ({ - driverDistance: 23000, - driverDuration: 900, - passengerDistance: 23000, - passengerDuration: 900, - fwdAzimuth: 283, - backAzimuth: 93, - distanceAzimuth: 19840, - points: [ - { - lon: 6.1765103, - lat: 48.689446, - }, - { - lon: 2.3523, - lat: 48.8567, - }, - ], - })), + get: jest + .fn() + .mockImplementationOnce(() => ({ + distance: 350101, + duration: 14422, + fwdAzimuth: 273, + backAzimuth: 93, + distanceAzimuth: 336544, + points: [ + { + lon: 6.1765102, + lat: 48.689445, + }, + { + lon: 4.984578, + lat: 48.725687, + }, + { + lon: 2.3522, + lat: 48.8566, + }, + ], + })) + .mockImplementationOnce(() => ({ + distance: 350102, + duration: 14423, + fwdAzimuth: 273, + backAzimuth: 93, + distanceAzimuth: 336545, + points: [ + { + lon: 6.1765103, + lat: 48.689446, + }, + { + lon: 4.984579, + lat: 48.725688, + }, + { + lon: 2.3523, + lat: 48.8567, + }, + ], + })) + .mockImplementationOnce(() => ({ + distance: 350100, + duration: 14421, + fwdAzimuth: 273, + backAzimuth: 93, + distanceAzimuth: 336543, + points: [ + { + lon: 6.1765101, + lat: 48.689444, + }, + { + lon: 4.984577, + lat: 48.725686, + }, + { + lon: 2.3521, + lat: 48.8565, + }, + ], + })) + .mockImplementationOnce(() => ({ + distance: 350107, + duration: 14427, + fwdAzimuth: 273, + backAzimuth: 93, + distanceAzimuth: 336548, + points: [ + { + lon: 6.1765101, + lat: 48.689444, + }, + { + lon: 4.984577, + lat: 48.725686, + }, + { + lon: 2.3521, + lat: 48.8565, + }, + ], + })) + .mockImplementationOnce(() => ({ + distance: 350108, + duration: 14428, + fwdAzimuth: 273, + backAzimuth: 93, + distanceAzimuth: 336548, + points: [ + { + lon: 6.1765101, + lat: 48.689444, + }, + { + lon: 4.984577, + lat: 48.725686, + }, + { + lon: 2.3521, + lat: 48.8565, + }, + ], + })) + .mockImplementationOnce(() => []), }; describe('Carpool route provider', () => { @@ -49,22 +152,79 @@ describe('Carpool route provider', () => { expect(carpoolRouteProvider).toBeDefined(); }); - it('should provide a carpool route', async () => { + it('should provide a carpool route for a driver only', async () => { const carpoolRoute: CarpoolRoute = await carpoolRouteProvider.getBasic( [Role.DRIVER], [ { position: 0, - lat: 48.689445, - lon: 6.1765102, + ...originPoint, }, { position: 1, - lat: 48.8566, - lon: 2.3522, + ...destinationPoint, }, ], ); - expect(carpoolRoute.driverDistance).toBe(23000); + expect(carpoolRoute.driverDistance).toBe(350101); + expect(carpoolRoute.passengerDuration).toBeUndefined(); + }); + + it('should provide a carpool route for a passenger only', async () => { + const carpoolRoute: CarpoolRoute = await carpoolRouteProvider.getBasic( + [Role.PASSENGER], + [ + { + position: 0, + ...originPoint, + }, + { + position: 1, + ...destinationPoint, + }, + ], + ); + expect(carpoolRoute.passengerDistance).toBe(350102); + expect(carpoolRoute.driverDuration).toBeUndefined(); + }); + + it('should provide a simple carpool route for a driver and passenger', async () => { + const carpoolRoute: CarpoolRoute = await carpoolRouteProvider.getBasic( + [Role.DRIVER, Role.PASSENGER], + [ + { + position: 0, + ...originPoint, + }, + { + position: 1, + ...destinationPoint, + }, + ], + ); + expect(carpoolRoute.driverDuration).toBe(14421); + expect(carpoolRoute.passengerDistance).toBe(350100); + }); + + it('should provide a complex carpool route for a driver and passenger', async () => { + const carpoolRoute: CarpoolRoute = await carpoolRouteProvider.getBasic( + [Role.DRIVER, Role.PASSENGER], + [ + { + position: 0, + ...originPoint, + }, + { + position: 1, + ...additionalPoint, + }, + { + position: 2, + ...destinationPoint, + }, + ], + ); + expect(carpoolRoute.driverDistance).toBe(350107); + expect(carpoolRoute.passengerDuration).toBe(14428); }); }); diff --git a/src/modules/geography/core/application/ports/direction-encoder.port.ts b/src/modules/geography/core/application/ports/direction-encoder.port.ts index 737456a..316de85 100644 --- a/src/modules/geography/core/application/ports/direction-encoder.port.ts +++ b/src/modules/geography/core/application/ports/direction-encoder.port.ts @@ -1,6 +1,6 @@ -import { Coordinates } from '../../domain/route.types'; +import { Point } from '../../domain/route.types'; export interface DirectionEncoderPort { - encode(coordinates: Coordinates[]): string; - decode(direction: string): Coordinates[]; + encode(coordinates: Point[]): string; + decode(direction: string): Point[]; } diff --git a/src/modules/geography/core/application/ports/georouter.port.ts b/src/modules/geography/core/application/ports/georouter.port.ts index 1990e99..8936ff0 100644 --- a/src/modules/geography/core/application/ports/georouter.port.ts +++ b/src/modules/geography/core/application/ports/georouter.port.ts @@ -1,6 +1,6 @@ -import { Path, Route } from '../../domain/route.types'; +import { Route, Waypoint } from '../../domain/route.types'; import { GeorouterSettings } from '../types/georouter-settings.type'; export interface GeorouterPort { - routes(paths: Path[], settings: GeorouterSettings): Promise; + route(waypoints: Waypoint[], settings: GeorouterSettings): Promise; } diff --git a/src/modules/geography/core/application/queries/get-route/get-route.query-handler.ts b/src/modules/geography/core/application/queries/get-route/get-route.query-handler.ts index 1a255aa..77d88ad 100644 --- a/src/modules/geography/core/application/queries/get-route/get-route.query-handler.ts +++ b/src/modules/geography/core/application/queries/get-route/get-route.query-handler.ts @@ -11,7 +11,6 @@ export class GetRouteQueryHandler implements IQueryHandler { execute = async (query: GetRouteQuery): Promise => await RouteEntity.create({ - roles: query.roles, waypoints: query.waypoints, georouter: this.georouter, georouterSettings: query.georouterSettings, diff --git a/src/modules/geography/core/application/queries/get-route/get-route.query.ts b/src/modules/geography/core/application/queries/get-route/get-route.query.ts index eef3ed1..0c936d9 100644 --- a/src/modules/geography/core/application/queries/get-route/get-route.query.ts +++ b/src/modules/geography/core/application/queries/get-route/get-route.query.ts @@ -1,19 +1,13 @@ import { QueryBase } from '@mobicoop/ddd-library'; -import { Role, Waypoint } from '@modules/geography/core/domain/route.types'; +import { Waypoint } from '@modules/geography/core/domain/route.types'; import { GeorouterSettings } from '../../types/georouter-settings.type'; export class GetRouteQuery extends QueryBase { - readonly roles: Role[]; readonly waypoints: Waypoint[]; readonly georouterSettings: GeorouterSettings; - constructor( - roles: Role[], - waypoints: Waypoint[], - georouterSettings: GeorouterSettings, - ) { + constructor(waypoints: Waypoint[], georouterSettings: GeorouterSettings) { super(); - this.roles = roles; this.waypoints = waypoints; this.georouterSettings = georouterSettings; } diff --git a/src/modules/geography/core/domain/route.entity.ts b/src/modules/geography/core/domain/route.entity.ts index 9bbee40..373d3aa 100644 --- a/src/modules/geography/core/domain/route.entity.ts +++ b/src/modules/geography/core/domain/route.entity.ts @@ -1,13 +1,5 @@ import { AggregateRoot, AggregateID } from '@mobicoop/ddd-library'; -import { - CreateRouteProps, - Path, - Role, - RouteProps, - PathType, - Route, -} from './route.types'; -import { WaypointProps } from './value-objects/waypoint.value-object'; +import { CreateRouteProps, RouteProps, Route } from './route.types'; import { v4 } from 'uuid'; import { RouteNotFoundException } from './route.errors'; @@ -15,43 +7,18 @@ export class RouteEntity extends AggregateRoot { protected readonly _id: AggregateID; static create = async (create: CreateRouteProps): Promise => { - const routes: Route[] = await create.georouter.routes( - this.getPaths(create.roles, create.waypoints), + const route: Route = await create.georouter.route( + create.waypoints, create.georouterSettings, ); - if (!routes || routes.length == 0) throw new RouteNotFoundException(); - let baseRoute: Route; - let driverRoute: Route | undefined; - let passengerRoute: Route | undefined; - if (routes.some((route: Route) => route.type == PathType.GENERIC)) { - driverRoute = passengerRoute = routes.find( - (route: Route) => route.type == PathType.GENERIC, - ); - } else { - driverRoute = routes.some((route: Route) => route.type == PathType.DRIVER) - ? routes.find((route: Route) => route.type == PathType.DRIVER) - : undefined; - passengerRoute = routes.some( - (route: Route) => route.type == PathType.PASSENGER, - ) - ? routes.find((route: Route) => route.type == PathType.PASSENGER) - : undefined; - } - if (driverRoute) { - baseRoute = driverRoute; - } else { - baseRoute = passengerRoute as Route; - } + if (!route) throw new RouteNotFoundException(); const routeProps: RouteProps = { - driverDistance: driverRoute?.distance, - driverDuration: driverRoute?.duration, - passengerDistance: passengerRoute?.distance, - passengerDuration: passengerRoute?.duration, - fwdAzimuth: baseRoute.fwdAzimuth, - backAzimuth: baseRoute.backAzimuth, - distanceAzimuth: baseRoute.distanceAzimuth, - waypoints: create.waypoints, - points: baseRoute.points, + distance: route.distance, + duration: route.duration, + fwdAzimuth: route.fwdAzimuth, + backAzimuth: route.backAzimuth, + distanceAzimuth: route.distanceAzimuth, + points: route.points, }; return new RouteEntity({ id: v4(), @@ -63,46 +30,46 @@ export class RouteEntity extends AggregateRoot { // entity business rules validation to protect it's invariant before saving entity to a database } - private static getPaths = ( - roles: Role[], - waypoints: WaypointProps[], - ): Path[] => { - const paths: Path[] = []; - if (roles.includes(Role.DRIVER) && roles.includes(Role.PASSENGER)) { - if (waypoints.length == 2) { - // 2 points => same route for driver and passenger - paths.push(this.createGenericPath(waypoints)); - } else { - paths.push( - this.createDriverPath(waypoints), - this.createPassengerPath(waypoints), - ); - } - } else if (roles.includes(Role.DRIVER)) { - paths.push(this.createDriverPath(waypoints)); - } else if (roles.includes(Role.PASSENGER)) { - paths.push(this.createPassengerPath(waypoints)); - } - return paths; - }; + // private static getPaths = ( + // roles: Role[], + // waypoints: WaypointProps[], + // ): Path[] => { + // const paths: Path[] = []; + // if (roles.includes(Role.DRIVER) && roles.includes(Role.PASSENGER)) { + // if (waypoints.length == 2) { + // // 2 points => same route for driver and passenger + // paths.push(this.createGenericPath(waypoints)); + // } else { + // paths.push( + // this.createDriverPath(waypoints), + // this.createPassengerPath(waypoints), + // ); + // } + // } else if (roles.includes(Role.DRIVER)) { + // paths.push(this.createDriverPath(waypoints)); + // } else if (roles.includes(Role.PASSENGER)) { + // paths.push(this.createPassengerPath(waypoints)); + // } + // return paths; + // }; - private static createGenericPath = (waypoints: WaypointProps[]): Path => - this.createPath(waypoints, PathType.GENERIC); + // private static createGenericPath = (waypoints: WaypointProps[]): Path => + // this.createPath(waypoints, PathType.GENERIC); - private static createDriverPath = (waypoints: WaypointProps[]): Path => - this.createPath(waypoints, PathType.DRIVER); + // private static createDriverPath = (waypoints: WaypointProps[]): Path => + // this.createPath(waypoints, PathType.DRIVER); - private static createPassengerPath = (waypoints: WaypointProps[]): Path => - this.createPath( - [waypoints[0], waypoints[waypoints.length - 1]], - PathType.PASSENGER, - ); + // private static createPassengerPath = (waypoints: WaypointProps[]): Path => + // this.createPath( + // [waypoints[0], waypoints[waypoints.length - 1]], + // PathType.PASSENGER, + // ); - private static createPath = ( - points: WaypointProps[], - type: PathType, - ): Path => ({ - type, - points, - }); + // private static createPath = ( + // points: WaypointProps[], + // type: PathType, + // ): Path => ({ + // type, + // points, + // }); } diff --git a/src/modules/geography/core/domain/route.types.ts b/src/modules/geography/core/domain/route.types.ts index 748c759..9340718 100644 --- a/src/modules/geography/core/domain/route.types.ts +++ b/src/modules/geography/core/domain/route.types.ts @@ -1,67 +1,49 @@ import { GeorouterPort } from '../application/ports/georouter.port'; import { GeorouterSettings } from '../application/types/georouter-settings.type'; -import { CoordinatesProps } from './value-objects/coordinates.value-object'; -import { SpacetimePointProps } from './value-objects/spacetime-point.value-object'; +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 { - driverDistance?: number; - driverDuration?: number; - passengerDistance?: number; - passengerDuration?: number; + distance: number; + duration: number; fwdAzimuth: number; backAzimuth: number; distanceAzimuth: number; - waypoints: WaypointProps[]; - points: SpacetimePointProps[] | CoordinatesProps[]; + points: PointProps[]; } // Properties that are needed for a Route creation export interface CreateRouteProps { - roles: Role[]; waypoints: WaypointProps[]; georouter: GeorouterPort; georouterSettings: GeorouterSettings; } export type Route = { - type: PathType; distance: number; duration: number; fwdAzimuth: number; backAzimuth: number; distanceAzimuth: number; - points: Coordinates[]; - spacetimeWaypoints: SpacetimePoint[]; + points: Point[]; + steps: Step[]; }; -export type Path = { - type: PathType; - points: Coordinates[]; -}; - -export type Coordinates = { +export type Point = { lon: number; lat: number; }; -export type Waypoint = Coordinates & { +export type Waypoint = Point & { position: number; }; -export type SpacetimePoint = Coordinates & { +export type Spacetime = { duration: number; distance?: number; }; -export enum Role { - DRIVER = 'DRIVER', - PASSENGER = 'PASSENGER', -} +export type Step = Point & Spacetime; -export enum PathType { - GENERIC = 'generic', - DRIVER = 'driver', - PASSENGER = 'passenger', -} +export type Waystep = Waypoint & Spacetime; diff --git a/src/modules/geography/core/domain/value-objects/coordinates.value-object.ts b/src/modules/geography/core/domain/value-objects/point.value-object.ts similarity index 79% rename from src/modules/geography/core/domain/value-objects/coordinates.value-object.ts rename to src/modules/geography/core/domain/value-objects/point.value-object.ts index 9bd81e7..2047ead 100644 --- a/src/modules/geography/core/domain/value-objects/coordinates.value-object.ts +++ b/src/modules/geography/core/domain/value-objects/point.value-object.ts @@ -8,12 +8,12 @@ import { * other Value Objects inside if needed. * */ -export interface CoordinatesProps { +export interface PointProps { lon: number; lat: number; } -export class Coordinates extends ValueObject { +export class Point extends ValueObject { get lon(): number { return this.props.lon; } @@ -22,7 +22,7 @@ export class Coordinates extends ValueObject { return this.props.lat; } - protected validate(props: CoordinatesProps): void { + protected validate(props: PointProps): void { if (props.lon > 180 || props.lon < -180) throw new ArgumentOutOfRangeException('lon must be between -180 and 180'); if (props.lat > 90 || props.lat < -90) diff --git a/src/modules/geography/core/domain/value-objects/spacetime-point.value-object.ts b/src/modules/geography/core/domain/value-objects/step.value-object.ts similarity index 86% rename from src/modules/geography/core/domain/value-objects/spacetime-point.value-object.ts rename to src/modules/geography/core/domain/value-objects/step.value-object.ts index c7bfbce..a5c180f 100644 --- a/src/modules/geography/core/domain/value-objects/spacetime-point.value-object.ts +++ b/src/modules/geography/core/domain/value-objects/step.value-object.ts @@ -9,14 +9,14 @@ import { * other Value Objects inside if needed. * */ -export interface SpacetimePointProps { +export interface StepProps { lon: number; lat: number; duration: number; distance: number; } -export class SpacetimePoint extends ValueObject { +export class Step extends ValueObject { get lon(): number { return this.props.lon; } @@ -33,7 +33,7 @@ export class SpacetimePoint extends ValueObject { return this.props.distance; } - protected validate(props: SpacetimePointProps): void { + protected validate(props: StepProps): void { if (props.duration < 0) throw new ArgumentInvalidException( 'duration must be greater than or equal to 0', diff --git a/src/modules/geography/infrastructure/graphhopper-georouter.ts b/src/modules/geography/infrastructure/graphhopper-georouter.ts index 37278d6..adfc60a 100644 --- a/src/modules/geography/infrastructure/graphhopper-georouter.ts +++ b/src/modules/geography/infrastructure/graphhopper-georouter.ts @@ -2,12 +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 { - Path, - PathType, - Route, - SpacetimePoint, -} from '../core/domain/route.types'; +import { Route, Step, Waypoint } 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'; @@ -35,13 +30,13 @@ export class GraphhopperGeorouter implements GeorouterPort { ].join(''); } - routes = async ( - paths: Path[], + route = async ( + waypoints: Waypoint[], settings: GeorouterSettings, - ): Promise => { + ): Promise => { this._setDefaultUrlArgs(); this._setSettings(settings); - return this._getRoutes(paths); + return this._getRoute(waypoints); }; private _setDefaultUrlArgs = (): void => { @@ -62,46 +57,39 @@ export class GraphhopperGeorouter implements GeorouterPort { } }; - private _getRoutes = async (paths: Path[]): Promise => { - const routes = Promise.all( - paths.map(async (path) => { - const url: string = [ - this.getUrl(), - '&point=', - path.points - .map((point) => [point.lat, point.lon].join('%2C')) - .join('&point='), - ].join(''); - return await lastValueFrom( - this.httpService.get(url).pipe( - map((response) => { - if (response.data) return this.createRoute(response, path.type); - throw new Error(); - }), - catchError((error: AxiosError) => { - if (error.code == AxiosError.ERR_BAD_REQUEST) { - throw new RouteNotFoundException( - error, - 'No route found for given coordinates', - ); - } - throw new GeorouterUnavailableException(error); - }), - ), - ); - }), + private _getRoute = async (waypoints: Waypoint[]): Promise => { + const url: string = [ + this.getUrl(), + '&point=', + waypoints + .map((waypoint: Waypoint) => [waypoint.lat, waypoint.lon].join('%2C')) + .join('&point='), + ].join(''); + return await lastValueFrom( + this.httpService.get(url).pipe( + map((response) => { + if (response.data) return this.createRoute(response); + throw new Error(); + }), + catchError((error: AxiosError) => { + if (error.code == AxiosError.ERR_BAD_REQUEST) { + throw new RouteNotFoundException( + error, + 'No route found for given coordinates', + ); + } + throw new GeorouterUnavailableException(error); + }), + ), ); - return routes; }; private getUrl = (): string => [this.url, this.urlArgs.join('&')].join(''); private createRoute = ( response: AxiosResponse, - type: PathType, ): Route => { const route = {} as Route; - route.type = type; if (response.data.paths && response.data.paths[0]) { const shortestPath = response.data.paths[0]; route.distance = shortestPath.distance ?? 0; @@ -135,7 +123,7 @@ export class GraphhopperGeorouter implements GeorouterPort { let instructions: GraphhopperInstruction[] = []; if (shortestPath.instructions) instructions = shortestPath.instructions; - route.spacetimeWaypoints = this.generateSpacetimePoints( + route.steps = this.generateSteps( shortestPath.points.coordinates, shortestPath.snapped_waypoints.coordinates, shortestPath.details.time, @@ -147,12 +135,12 @@ export class GraphhopperGeorouter implements GeorouterPort { return route; }; - private generateSpacetimePoints = ( + private generateSteps = ( points: [[number, number]], snappedWaypoints: [[number, number]], durations: [[number, number, number]], instructions: GraphhopperInstruction[], - ): SpacetimePoint[] => { + ): Step[] => { const indices = this.getIndices(points, snappedWaypoints); const times = this.getTimes(durations, indices); const distances = this.getDistances(instructions, indices); diff --git a/src/modules/geography/infrastructure/postgres-direction-encoder.ts b/src/modules/geography/infrastructure/postgres-direction-encoder.ts index d6cb0b6..05cae0b 100644 --- a/src/modules/geography/infrastructure/postgres-direction-encoder.ts +++ b/src/modules/geography/infrastructure/postgres-direction-encoder.ts @@ -1,16 +1,16 @@ import { DirectionEncoderPort } from '../core/application/ports/direction-encoder.port'; import { Injectable } from '@nestjs/common'; -import { Coordinates } from '../core/domain/route.types'; +import { Point } from '../core/domain/route.types'; @Injectable() export class PostgresDirectionEncoder implements DirectionEncoderPort { - encode = (coordinates: Coordinates[]): string => + encode = (coordinates: Point[]): string => [ "'LINESTRING(", coordinates.map((point) => [point.lon, point.lat].join(' ')).join(), ")'", ].join(''); - decode = (direction: string): Coordinates[] => + decode = (direction: string): Point[] => direction .split('(')[1] .split(')')[0] diff --git a/src/modules/geography/interface/controllers/dtos/get-route.request.dto.ts b/src/modules/geography/interface/controllers/dtos/get-route.request.dto.ts index 7790bba..1b01076 100644 --- a/src/modules/geography/interface/controllers/dtos/get-route.request.dto.ts +++ b/src/modules/geography/interface/controllers/dtos/get-route.request.dto.ts @@ -1,6 +1,5 @@ -import { Role, Waypoint } from '@modules/geography/core/domain/route.types'; +import { Waypoint } from '@modules/geography/core/domain/route.types'; export type GetRouteRequestDto = { - roles: Role[]; waypoints: Waypoint[]; }; diff --git a/src/modules/geography/interface/controllers/get-basic-route.controller.ts b/src/modules/geography/interface/controllers/get-basic-route.controller.ts index 3bdc42e..3c14b10 100644 --- a/src/modules/geography/interface/controllers/get-basic-route.controller.ts +++ b/src/modules/geography/interface/controllers/get-basic-route.controller.ts @@ -16,7 +16,7 @@ export class GetBasicRouteController implements GetBasicRouteControllerPort { async get(data: GetRouteRequestDto): Promise { const route: RouteEntity = await this.queryBus.execute( - new GetRouteQuery(data.roles, data.waypoints, { + new GetRouteQuery(data.waypoints, { detailedDistance: false, detailedDuration: false, points: true, diff --git a/src/modules/geography/interface/dtos/route.response.dto.ts b/src/modules/geography/interface/dtos/route.response.dto.ts index 714fb68..c157585 100644 --- a/src/modules/geography/interface/dtos/route.response.dto.ts +++ b/src/modules/geography/interface/dtos/route.response.dto.ts @@ -1,15 +1,10 @@ -import { - Coordinates, - SpacetimePoint, -} from '@modules/geography/core/domain/route.types'; +import { Point, Step } from '@modules/geography/core/domain/route.types'; export class RouteResponseDto { - driverDistance?: number; - driverDuration?: number; - passengerDistance?: number; - passengerDuration?: number; + distance?: number; + duration?: number; fwdAzimuth: number; backAzimuth: number; distanceAzimuth: number; - points: SpacetimePoint[] | Coordinates[]; + points: Step[] | Point[]; } diff --git a/src/modules/geography/route.mapper.ts b/src/modules/geography/route.mapper.ts index a714ff5..c7d6eef 100644 --- a/src/modules/geography/route.mapper.ts +++ b/src/modules/geography/route.mapper.ts @@ -16,17 +16,11 @@ export class RouteMapper { toResponse = (entity: RouteEntity): RouteResponseDto => { const response = new RouteResponseDto(); - response.driverDistance = entity.getProps().driverDistance - ? Math.round(entity.getProps().driverDistance as number) + response.distance = entity.getProps().distance + ? Math.round(entity.getProps().distance as number) : undefined; - response.driverDuration = entity.getProps().driverDuration - ? Math.round(entity.getProps().driverDuration as number) - : undefined; - response.passengerDistance = entity.getProps().passengerDistance - ? Math.round(entity.getProps().passengerDistance as number) - : undefined; - response.passengerDuration = entity.getProps().passengerDuration - ? Math.round(entity.getProps().passengerDuration as number) + response.duration = entity.getProps().duration + ? Math.round(entity.getProps().duration as number) : undefined; response.fwdAzimuth = Math.round(entity.getProps().fwdAzimuth); response.backAzimuth = Math.round(entity.getProps().backAzimuth); diff --git a/src/modules/geography/tests/unit/core/get-route.query-handler.spec.ts b/src/modules/geography/tests/unit/core/get-route.query-handler.spec.ts index 948ed0e..8008540 100644 --- a/src/modules/geography/tests/unit/core/get-route.query-handler.spec.ts +++ b/src/modules/geography/tests/unit/core/get-route.query-handler.spec.ts @@ -2,7 +2,7 @@ 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 { Role, Waypoint } from '@modules/geography/core/domain/route.types'; +import { Waypoint } from '@modules/geography/core/domain/route.types'; import { GEOROUTER } from '@modules/geography/geography.di-tokens'; import { Test, TestingModule } from '@nestjs/testing'; @@ -18,7 +18,7 @@ const destinationWaypoint: Waypoint = { }; const mockGeorouter: GeorouterPort = { - routes: jest.fn(), + route: jest.fn(), }; describe('Get route query handler', () => { @@ -44,9 +44,8 @@ describe('Get route query handler', () => { }); describe('execution', () => { - it('should get a route for a driver only', async () => { + it('should get a route', async () => { const getRoutequery = new GetRouteQuery( - [Role.DRIVER], [originWaypoint, destinationWaypoint], { detailedDistance: false, diff --git a/src/modules/geography/tests/unit/core/coordinates.value-object.spec.ts b/src/modules/geography/tests/unit/core/point.value-object.spec.ts similarity index 67% rename from src/modules/geography/tests/unit/core/coordinates.value-object.spec.ts rename to src/modules/geography/tests/unit/core/point.value-object.spec.ts index 4c7b1f7..8fc5573 100644 --- a/src/modules/geography/tests/unit/core/coordinates.value-object.spec.ts +++ b/src/modules/geography/tests/unit/core/point.value-object.spec.ts @@ -1,18 +1,18 @@ import { ArgumentOutOfRangeException } from '@mobicoop/ddd-library'; -import { Coordinates } from '@modules/geography/core/domain/value-objects/coordinates.value-object'; +import { Point } from '@modules/geography/core/domain/value-objects/point.value-object'; -describe('Waypoint value object', () => { - it('should create a waypoint value object', () => { - const coordinatesVO = new Coordinates({ +describe('Point value object', () => { + it('should create a point value object', () => { + const pointVO = new Point({ lat: 48.689445, lon: 6.17651, }); - expect(coordinatesVO.lat).toBe(48.689445); - expect(coordinatesVO.lon).toBe(6.17651); + expect(pointVO.lat).toBe(48.689445); + expect(pointVO.lon).toBe(6.17651); }); it('should throw an exception if longitude is invalid', () => { try { - new Coordinates({ + new Point({ lat: 48.689445, lon: 186.17651, }); @@ -20,7 +20,7 @@ describe('Waypoint value object', () => { expect(e).toBeInstanceOf(ArgumentOutOfRangeException); } try { - new Coordinates({ + new Point({ lat: 48.689445, lon: -186.17651, }); @@ -30,7 +30,7 @@ describe('Waypoint value object', () => { }); it('should throw an exception if latitude is invalid', () => { try { - new Coordinates({ + new Point({ lat: 148.689445, lon: 6.17651, }); @@ -38,7 +38,7 @@ describe('Waypoint value object', () => { expect(e).toBeInstanceOf(ArgumentOutOfRangeException); } try { - new Coordinates({ + new Point({ lat: -148.689445, lon: 6.17651, }); diff --git a/src/modules/geography/tests/unit/core/route.entity.spec.ts b/src/modules/geography/tests/unit/core/route.entity.spec.ts index 7cf810a..127cf36 100644 --- a/src/modules/geography/tests/unit/core/route.entity.spec.ts +++ b/src/modules/geography/tests/unit/core/route.entity.spec.ts @@ -2,205 +2,56 @@ import { GeorouterPort } from '@modules/geography/core/application/ports/georout import { RouteEntity } from '@modules/geography/core/domain/route.entity'; import { RouteNotFoundException } from '@modules/geography/core/domain/route.errors'; import { - Coordinates, + Point, CreateRouteProps, - PathType, - Role, } from '@modules/geography/core/domain/route.types'; -const originCoordinates: Coordinates = { +const originPoint: Point = { lat: 48.689445, lon: 6.17651, }; -const destinationCoordinates: Coordinates = { +const destinationPoint: Point = { lat: 48.8566, lon: 2.3522, }; -const additionalCoordinates: Coordinates = { - lon: 48.7566, - lat: 4.4498, -}; const mockGeorouter: GeorouterPort = { - routes: jest + route: jest .fn() - .mockImplementationOnce(() => [ - { - type: PathType.DRIVER, - distance: 350101, - duration: 14422, - fwdAzimuth: 273, - backAzimuth: 93, - distanceAzimuth: 336544, - points: [ - { - lon: 6.1765102, - lat: 48.689445, - }, - { - lon: 4.984578, - lat: 48.725687, - }, - { - lon: 2.3522, - lat: 48.8566, - }, - ], - spacetimePoints: [], - }, - ]) - .mockImplementationOnce(() => [ - { - type: PathType.PASSENGER, - distance: 350102, - duration: 14423, - fwdAzimuth: 273, - backAzimuth: 93, - distanceAzimuth: 336545, - points: [ - { - lon: 6.1765103, - lat: 48.689446, - }, - { - lon: 4.984579, - lat: 48.725688, - }, - { - lon: 2.3523, - lat: 48.8567, - }, - ], - spacetimePoints: [], - }, - ]) - .mockImplementationOnce(() => [ - { - type: PathType.GENERIC, - distance: 350100, - duration: 14421, - fwdAzimuth: 273, - backAzimuth: 93, - distanceAzimuth: 336543, - points: [ - { - lon: 6.1765101, - lat: 48.689444, - }, - { - lon: 4.984577, - lat: 48.725686, - }, - { - lon: 2.3521, - lat: 48.8565, - }, - ], - spacetimePoints: [], - }, - ]) - .mockImplementationOnce(() => [ - { - type: PathType.GENERIC, - distance: 350108, - duration: 14428, - fwdAzimuth: 273, - backAzimuth: 93, - distanceAzimuth: 336548, - points: [ - { - lon: 6.1765101, - lat: 48.689444, - }, - { - lon: 4.984577, - lat: 48.725686, - }, - { - lon: 2.3521, - lat: 48.8565, - }, - ], - spacetimePoints: [], - }, - ]) + .mockImplementationOnce(() => ({ + distance: 350101, + duration: 14422, + fwdAzimuth: 273, + backAzimuth: 93, + distanceAzimuth: 336544, + points: [ + { + lon: 6.1765102, + lat: 48.689445, + }, + { + lon: 4.984578, + lat: 48.725687, + }, + { + lon: 2.3522, + lat: 48.8566, + }, + ], + steps: [], + })) .mockImplementationOnce(() => []), }; -const createDriverRouteProps: CreateRouteProps = { - roles: [Role.DRIVER], +const createRouteProps: CreateRouteProps = { waypoints: [ { position: 0, - ...originCoordinates, + ...originPoint, }, { position: 1, - ...destinationCoordinates, - }, - ], - georouter: mockGeorouter, - georouterSettings: { - points: true, - detailedDistance: false, - detailedDuration: false, - }, -}; - -const createPassengerRouteProps: CreateRouteProps = { - roles: [Role.PASSENGER], - waypoints: [ - { - position: 0, - ...originCoordinates, - }, - { - position: 1, - ...destinationCoordinates, - }, - ], - georouter: mockGeorouter, - georouterSettings: { - points: true, - detailedDistance: false, - detailedDuration: false, - }, -}; - -const createSimpleDriverAndPassengerRouteProps: CreateRouteProps = { - roles: [Role.DRIVER, Role.PASSENGER], - waypoints: [ - { - position: 0, - ...originCoordinates, - }, - { - position: 1, - ...destinationCoordinates, - }, - ], - georouter: mockGeorouter, - georouterSettings: { - points: true, - detailedDistance: false, - detailedDuration: false, - }, -}; - -const createComplexDriverAndPassengerRouteProps: CreateRouteProps = { - roles: [Role.DRIVER, Role.PASSENGER], - waypoints: [ - { - position: 0, - ...originCoordinates, - }, - { - position: 1, - ...additionalCoordinates, - }, - { - position: 2, - ...destinationCoordinates, + ...destinationPoint, }, ], georouter: mockGeorouter, @@ -212,43 +63,15 @@ const createComplexDriverAndPassengerRouteProps: CreateRouteProps = { }; describe('Route entity create', () => { - it('should create a new entity for a driver only', async () => { - const route: RouteEntity = await RouteEntity.create(createDriverRouteProps); + it('should create a new entity', async () => { + const route: RouteEntity = await RouteEntity.create(createRouteProps); expect(route.id.length).toBe(36); - expect(route.getProps().driverDuration).toBe(14422); - expect(route.getProps().passengerDistance).toBeUndefined(); - }); - it('should create a new entity for a passenger only', async () => { - const route: RouteEntity = await RouteEntity.create( - createPassengerRouteProps, - ); - expect(route.id.length).toBe(36); - expect(route.getProps().passengerDuration).toBe(14423); - expect(route.getProps().driverDistance).toBeUndefined(); - }); - it('should create a new entity for a simple driver and passenger route', async () => { - const route: RouteEntity = await RouteEntity.create( - createSimpleDriverAndPassengerRouteProps, - ); - expect(route.id.length).toBe(36); - expect(route.getProps().driverDuration).toBe(14421); - expect(route.getProps().driverDistance).toBe(350100); - expect(route.getProps().passengerDuration).toBe(14421); - expect(route.getProps().passengerDistance).toBe(350100); - }); - it('should create a new entity for a complex driver and passenger route', async () => { - const route: RouteEntity = await RouteEntity.create( - createComplexDriverAndPassengerRouteProps, - ); - expect(route.id.length).toBe(36); - expect(route.getProps().driverDuration).toBe(14428); - expect(route.getProps().driverDistance).toBe(350108); - expect(route.getProps().passengerDuration).toBe(14428); - expect(route.getProps().passengerDistance).toBe(350108); + expect(route.getProps().duration).toBe(14422); }); + it('should throw an exception if route is not found', async () => { try { - await RouteEntity.create(createDriverRouteProps); + await RouteEntity.create(createRouteProps); } catch (e: any) { expect(e).toBeInstanceOf(RouteNotFoundException); } diff --git a/src/modules/geography/tests/unit/core/spacetime-point.value-object.spec.ts b/src/modules/geography/tests/unit/core/step.value-object.spec.ts similarity index 74% rename from src/modules/geography/tests/unit/core/spacetime-point.value-object.spec.ts rename to src/modules/geography/tests/unit/core/step.value-object.spec.ts index 405190c..6811c97 100644 --- a/src/modules/geography/tests/unit/core/spacetime-point.value-object.spec.ts +++ b/src/modules/geography/tests/unit/core/step.value-object.spec.ts @@ -2,24 +2,24 @@ import { ArgumentInvalidException, ArgumentOutOfRangeException, } from '@mobicoop/ddd-library'; -import { SpacetimePoint } from '@modules/geography/core/domain/value-objects/spacetime-point.value-object'; +import { Step } from '@modules/geography/core/domain/value-objects/step.value-object'; -describe('Timepoint value object', () => { - it('should create a timepoint value object', () => { - const timepointVO = new SpacetimePoint({ +describe('Step value object', () => { + it('should create a step value object', () => { + const stepVO = new Step({ lat: 48.689445, lon: 6.17651, duration: 150, distance: 12000, }); - expect(timepointVO.duration).toBe(150); - expect(timepointVO.distance).toBe(12000); - expect(timepointVO.lat).toBe(48.689445); - expect(timepointVO.lon).toBe(6.17651); + expect(stepVO.duration).toBe(150); + expect(stepVO.distance).toBe(12000); + expect(stepVO.lat).toBe(48.689445); + expect(stepVO.lon).toBe(6.17651); }); it('should throw an exception if longitude is invalid', () => { try { - new SpacetimePoint({ + new Step({ lat: 48.689445, lon: 186.17651, duration: 150, @@ -29,7 +29,7 @@ describe('Timepoint value object', () => { expect(e).toBeInstanceOf(ArgumentOutOfRangeException); } try { - new SpacetimePoint({ + new Step({ lon: 48.689445, lat: -186.17651, duration: 150, @@ -41,7 +41,7 @@ describe('Timepoint value object', () => { }); it('should throw an exception if latitude is invalid', () => { try { - new SpacetimePoint({ + new Step({ lat: 248.689445, lon: 6.17651, duration: 150, @@ -51,7 +51,7 @@ describe('Timepoint value object', () => { expect(e).toBeInstanceOf(ArgumentOutOfRangeException); } try { - new SpacetimePoint({ + new Step({ lon: -148.689445, lat: 6.17651, duration: 150, @@ -63,7 +63,7 @@ describe('Timepoint value object', () => { }); it('should throw an exception if distance is invalid', () => { try { - new SpacetimePoint({ + new Step({ lat: 48.689445, lon: 6.17651, duration: 150, @@ -75,7 +75,7 @@ describe('Timepoint value object', () => { }); it('should throw an exception if duration is invalid', () => { try { - new SpacetimePoint({ + new Step({ lat: 48.689445, lon: 6.17651, duration: -150, diff --git a/src/modules/geography/tests/unit/infrastructure/graphhopper-georouter.spec.ts b/src/modules/geography/tests/unit/infrastructure/graphhopper-georouter.spec.ts index c167240..1a40b5d 100644 --- a/src/modules/geography/tests/unit/infrastructure/graphhopper-georouter.spec.ts +++ b/src/modules/geography/tests/unit/infrastructure/graphhopper-georouter.spec.ts @@ -4,7 +4,7 @@ import { GeorouterUnavailableException, RouteNotFoundException, } from '@modules/geography/core/domain/route.errors'; -import { PathType, Route } from '@modules/geography/core/domain/route.types'; +import { Route } from '@modules/geography/core/domain/route.types'; import { GEODESIC, PARAMS_PROVIDER, @@ -294,20 +294,17 @@ describe('Graphhopper Georouter', () => { it('should fail if route is not found', async () => { await expect( - graphhopperGeorouter.routes( + graphhopperGeorouter.route( [ { - type: PathType.DRIVER, - points: [ - { - lon: 0, - lat: 0, - }, - { - lon: 1, - lat: 1, - }, - ], + position: 0, + lon: 0, + lat: 0, + }, + { + position: 1, + lon: 1, + lat: 1, }, ], { @@ -321,20 +318,17 @@ describe('Graphhopper Georouter', () => { it('should fail if georouter is unavailable', async () => { await expect( - graphhopperGeorouter.routes( + graphhopperGeorouter.route( [ { - type: PathType.DRIVER, - points: [ - { - lon: 0, - lat: 0, - }, - { - lon: 1, - lat: 1, - }, - ], + position: 0, + lon: 0, + lat: 0, + }, + { + position: 1, + lon: 1, + lat: 1, }, ], { @@ -347,20 +341,17 @@ describe('Graphhopper Georouter', () => { }); it('should create a basic route', async () => { - const routes: Route[] = await graphhopperGeorouter.routes( + const route: Route = await graphhopperGeorouter.route( [ { - type: PathType.DRIVER, - points: [ - { - lon: 0, - lat: 0, - }, - { - lon: 10, - lat: 10, - }, - ], + position: 0, + lon: 0, + lat: 0, + }, + { + position: 1, + lon: 10, + lat: 10, }, ], { @@ -369,25 +360,21 @@ describe('Graphhopper Georouter', () => { points: false, }, ); - expect(routes).toHaveLength(1); - expect(routes[0].distance).toBe(50000); + expect(route.distance).toBe(50000); }); - it('should create one route with points', async () => { - const routes = await graphhopperGeorouter.routes( + it('should create a route with points', async () => { + const route: Route = await graphhopperGeorouter.route( [ { - type: PathType.DRIVER, - points: [ - { - lat: 0, - lon: 0, - }, - { - lat: 10, - lon: 10, - }, - ], + position: 0, + lon: 0, + lat: 0, + }, + { + position: 1, + lon: 10, + lat: 10, }, ], { @@ -396,29 +383,25 @@ describe('Graphhopper Georouter', () => { points: true, }, ); - expect(routes).toHaveLength(1); - expect(routes[0].distance).toBe(50000); - expect(routes[0].duration).toBe(1800); - expect(routes[0].fwdAzimuth).toBe(45); - expect(routes[0].backAzimuth).toBe(225); - expect(routes[0].points).toHaveLength(11); + expect(route.distance).toBe(50000); + expect(route.duration).toBe(1800); + expect(route.fwdAzimuth).toBe(45); + expect(route.backAzimuth).toBe(225); + expect(route.points).toHaveLength(11); }); - it('should create one route with points and time', async () => { - const routes = await graphhopperGeorouter.routes( + it('should create a route with points and time', async () => { + const route: Route = await graphhopperGeorouter.route( [ { - type: PathType.DRIVER, - points: [ - { - lat: 0, - lon: 0, - }, - { - lat: 10, - lon: 10, - }, - ], + position: 0, + lon: 0, + lat: 0, + }, + { + position: 1, + lon: 10, + lat: 10, }, ], { @@ -427,31 +410,28 @@ describe('Graphhopper Georouter', () => { points: true, }, ); - expect(routes).toHaveLength(1); - expect(routes[0].spacetimeWaypoints).toHaveLength(2); - expect(routes[0].spacetimeWaypoints[1].duration).toBe(1800); - expect(routes[0].spacetimeWaypoints[1].distance).toBeUndefined(); + expect(route.steps).toHaveLength(2); + expect(route.steps[1].duration).toBe(1800); + expect(route.steps[1].distance).toBeUndefined(); }); it('should create one route with points and missed waypoints extrapolations', async () => { - const routes = await graphhopperGeorouter.routes( + const route: Route = await graphhopperGeorouter.route( [ { - type: PathType.DRIVER, - points: [ - { - lat: 0, - lon: 0, - }, - { - lat: 5, - lon: 5, - }, - { - lat: 10, - lon: 10, - }, - ], + position: 0, + lon: 0, + lat: 0, + }, + { + position: 1, + lon: 5, + lat: 5, + }, + { + position: 2, + lon: 10, + lat: 10, }, ], { @@ -460,30 +440,26 @@ describe('Graphhopper Georouter', () => { points: true, }, ); - expect(routes).toHaveLength(1); - expect(routes[0].spacetimeWaypoints).toHaveLength(3); - expect(routes[0].distance).toBe(50000); - expect(routes[0].duration).toBe(1800); - expect(routes[0].fwdAzimuth).toBe(45); - expect(routes[0].backAzimuth).toBe(225); - expect(routes[0].points.length).toBe(9); + expect(route.steps).toHaveLength(3); + expect(route.distance).toBe(50000); + expect(route.duration).toBe(1800); + expect(route.fwdAzimuth).toBe(45); + expect(route.backAzimuth).toBe(225); + expect(route.points.length).toBe(9); }); - it('should create one route with points, time and distance', async () => { - const routes = await graphhopperGeorouter.routes( + it('should create a route with points, time and distance', async () => { + const route: Route = await graphhopperGeorouter.route( [ { - type: PathType.DRIVER, - points: [ - { - lat: 0, - lon: 0, - }, - { - lat: 10, - lon: 10, - }, - ], + position: 0, + lon: 0, + lat: 0, + }, + { + position: 1, + lon: 10, + lat: 10, }, ], { @@ -492,9 +468,8 @@ describe('Graphhopper Georouter', () => { points: true, }, ); - expect(routes).toHaveLength(1); - expect(routes[0].spacetimeWaypoints.length).toBe(3); - expect(routes[0].spacetimeWaypoints[1].duration).toBe(990); - expect(routes[0].spacetimeWaypoints[1].distance).toBe(25000); + expect(route.steps.length).toBe(3); + expect(route.steps[1].duration).toBe(990); + expect(route.steps[1].distance).toBe(25000); }); }); diff --git a/src/modules/geography/tests/unit/infrastructure/postgres-direction-encoder.spec.ts b/src/modules/geography/tests/unit/infrastructure/postgres-direction-encoder.spec.ts index fd4cbab..8a2527c 100644 --- a/src/modules/geography/tests/unit/infrastructure/postgres-direction-encoder.spec.ts +++ b/src/modules/geography/tests/unit/infrastructure/postgres-direction-encoder.spec.ts @@ -1,4 +1,4 @@ -import { Coordinates } from '@modules/geography/core/domain/route.types'; +import { Point } from '@modules/geography/core/domain/route.types'; import { PostgresDirectionEncoder } from '@modules/geography/infrastructure/postgres-direction-encoder'; describe('Postgres direction encoder', () => { @@ -7,10 +7,10 @@ describe('Postgres direction encoder', () => { new PostgresDirectionEncoder(); expect(postgresDirectionEncoder).toBeDefined(); }); - it('should encode coordinates to a postgres direction', () => { + it('should encode points to a postgres direction', () => { const postgresDirectionEncoder: PostgresDirectionEncoder = new PostgresDirectionEncoder(); - const coordinates: Coordinates[] = [ + const points: Point[] = [ { lon: 6, lat: 47, @@ -24,18 +24,17 @@ describe('Postgres direction encoder', () => { lat: 47.2, }, ]; - const direction = postgresDirectionEncoder.encode(coordinates); + const direction = postgresDirectionEncoder.encode(points); expect(direction).toBe("'LINESTRING(6 47,6.1 47.1,6.2 47.2)'"); }); it('should decode a postgres direction to coordinates', () => { const postgresDirectionEncoder: PostgresDirectionEncoder = new PostgresDirectionEncoder(); const direction = "'LINESTRING(6 47,6.1 47.1,6.2 47.2)'"; - const coordinates: Coordinates[] = - postgresDirectionEncoder.decode(direction); - expect(coordinates.length).toBe(3); - expect(coordinates[0].lat).toBe(47); - expect(coordinates[1].lon).toBe(6.1); - expect(coordinates[2].lat).toBe(47.2); + const points: Point[] = postgresDirectionEncoder.decode(direction); + expect(points.length).toBe(3); + expect(points[0].lat).toBe(47); + expect(points[1].lon).toBe(6.1); + expect(points[2].lat).toBe(47.2); }); }); diff --git a/src/modules/geography/tests/unit/interface/get-basic-route.controller.spec.ts b/src/modules/geography/tests/unit/interface/get-basic-route.controller.spec.ts index 7e2de20..b4c4202 100644 --- a/src/modules/geography/tests/unit/interface/get-basic-route.controller.spec.ts +++ b/src/modules/geography/tests/unit/interface/get-basic-route.controller.spec.ts @@ -1,4 +1,3 @@ -import { Role } from '@modules/geography/core/domain/route.types'; import { GetBasicRouteController } from '@modules/geography/interface/controllers/get-basic-route.controller'; import { RouteMapper } from '@modules/geography/route.mapper'; import { QueryBus } from '@nestjs/cqrs'; @@ -48,7 +47,6 @@ describe('Get Basic Route Controller', () => { it('should get a route', async () => { jest.spyOn(mockQueryBus, 'execute'); await getBasicRouteController.get({ - roles: [Role.DRIVER], waypoints: [ { position: 0, diff --git a/src/modules/geography/tests/unit/route.mapper.spec.ts b/src/modules/geography/tests/unit/route.mapper.spec.ts index 7492a85..0d0ccf5 100644 --- a/src/modules/geography/tests/unit/route.mapper.spec.ts +++ b/src/modules/geography/tests/unit/route.mapper.spec.ts @@ -23,28 +23,23 @@ describe('Route Mapper', () => { createdAt: now, updatedAt: now, props: { - driverDistance: 23000, - driverDuration: 900, - passengerDistance: 23000, - passengerDuration: 900, + distance: 23000, + duration: 900, fwdAzimuth: 283, backAzimuth: 93, distanceAzimuth: 19840, - points: [], - waypoints: [ + points: [ { - position: 0, lon: 6.1765103, lat: 48.689446, }, { - position: 1, lon: 2.3523, lat: 48.8567, }, ], }, }); - expect(routeMapper.toResponse(routeEntity).driverDistance).toBe(23000); + expect(routeMapper.toResponse(routeEntity).distance).toBe(23000); }); });