extract carpool informations from geography module
This commit is contained in:
parent
57fe8d417f
commit
d1a314f011
|
@ -4,7 +4,7 @@ import { Selector } from '../algorithm.abstract';
|
||||||
import { AdReadModel } from '@modules/ad/infrastructure/ad.repository';
|
import { AdReadModel } from '@modules/ad/infrastructure/ad.repository';
|
||||||
import { ScheduleItem } from '../match.query';
|
import { ScheduleItem } from '../match.query';
|
||||||
import { Waypoint } from '../../../types/waypoint.type';
|
import { Waypoint } from '../../../types/waypoint.type';
|
||||||
import { Coordinates } from '../../../types/coordinates.type';
|
import { Point } from '../../../types/point.type';
|
||||||
|
|
||||||
export class PassengerOrientedSelector extends Selector {
|
export class PassengerOrientedSelector extends Selector {
|
||||||
select = async (): Promise<Candidate[]> => {
|
select = async (): Promise<Candidate[]> => {
|
||||||
|
@ -238,7 +238,7 @@ export class PassengerOrientedSelector extends Selector {
|
||||||
${this.query.remoteness}`;
|
${this.query.remoteness}`;
|
||||||
case Role.DRIVER:
|
case Role.DRIVER:
|
||||||
const lineStringPoints: string[] = [];
|
const lineStringPoints: string[] = [];
|
||||||
this.query.carpoolRoute?.points.forEach((point: Coordinates) =>
|
this.query.carpoolRoute?.points.forEach((point: Point) =>
|
||||||
lineStringPoints.push(
|
lineStringPoints.push(
|
||||||
`public.st_makepoint(${point.lon},${point.lat})`,
|
`public.st_makepoint(${point.lon},${point.lat})`,
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Coordinates } from './coordinates.type';
|
import { Point } from './point.type';
|
||||||
|
|
||||||
export type Address = {
|
export type Address = {
|
||||||
name?: string;
|
name?: string;
|
||||||
|
@ -7,4 +7,4 @@ export type Address = {
|
||||||
locality?: string;
|
locality?: string;
|
||||||
postalCode?: string;
|
postalCode?: string;
|
||||||
country?: string;
|
country?: string;
|
||||||
} & Coordinates;
|
} & Point;
|
||||||
|
|
|
@ -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
|
* A carpool route is a route with distance and duration as driver and / or passenger
|
||||||
|
@ -10,5 +10,5 @@ export type CarpoolRoute = {
|
||||||
passengerDuration?: number;
|
passengerDuration?: number;
|
||||||
fwdAzimuth: number;
|
fwdAzimuth: number;
|
||||||
backAzimuth: number;
|
backAzimuth: number;
|
||||||
points: Coordinates[];
|
points: Point[];
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export type Coordinates = {
|
export type Point = {
|
||||||
lon: number;
|
lon: number;
|
||||||
lat: number;
|
lat: number;
|
||||||
};
|
};
|
|
@ -5,6 +5,7 @@ import { Waypoint } from '../core/application/types/waypoint.type';
|
||||||
import { Role } from '../core/domain/ad.types';
|
import { Role } from '../core/domain/ad.types';
|
||||||
import { GetBasicRouteControllerPort } from '@modules/geography/core/application/ports/get-basic-route-controller.port';
|
import { GetBasicRouteControllerPort } from '@modules/geography/core/application/ports/get-basic-route-controller.port';
|
||||||
import { AD_GET_BASIC_ROUTE_CONTROLLER } from '../ad.di-tokens';
|
import { AD_GET_BASIC_ROUTE_CONTROLLER } from '../ad.di-tokens';
|
||||||
|
import { Route } from '@modules/geography/core/domain/route.types';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CarpoolRouteProvider implements CarpoolRouteProviderPort {
|
export class CarpoolRouteProvider implements CarpoolRouteProviderPort {
|
||||||
|
@ -16,9 +17,116 @@ export class CarpoolRouteProvider implements CarpoolRouteProviderPort {
|
||||||
getBasic = async (
|
getBasic = async (
|
||||||
roles: Role[],
|
roles: Role[],
|
||||||
waypoints: Waypoint[],
|
waypoints: Waypoint[],
|
||||||
): Promise<CarpoolRoute> =>
|
): Promise<CarpoolRoute> => {
|
||||||
await this.getBasicRouteController.get({
|
const paths: Path[] = this.getPaths(roles, waypoints);
|
||||||
roles,
|
const typeRoutes: TypeRoute[] = await Promise.all(
|
||||||
waypoints,
|
paths.map(
|
||||||
});
|
async (path: Path) =>
|
||||||
|
<TypeRoute>{
|
||||||
|
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',
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,133 @@
|
||||||
import { AD_GET_BASIC_ROUTE_CONTROLLER } from '@modules/ad/ad.di-tokens';
|
import { AD_GET_BASIC_ROUTE_CONTROLLER } from '@modules/ad/ad.di-tokens';
|
||||||
import { CarpoolRoute } from '@modules/ad/core/application/types/carpool-route.type';
|
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 { Role } from '@modules/ad/core/domain/ad.types';
|
||||||
import { CarpoolRouteProvider } from '@modules/ad/infrastructure/carpool-route-provider';
|
import { CarpoolRouteProvider } from '@modules/ad/infrastructure/carpool-route-provider';
|
||||||
import { GetBasicRouteControllerPort } from '@modules/geography/core/application/ports/get-basic-route-controller.port';
|
import { GetBasicRouteControllerPort } from '@modules/geography/core/application/ports/get-basic-route-controller.port';
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
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 = {
|
const mockGetBasicRouteController: GetBasicRouteControllerPort = {
|
||||||
get: jest.fn().mockImplementation(() => ({
|
get: jest
|
||||||
driverDistance: 23000,
|
.fn()
|
||||||
driverDuration: 900,
|
.mockImplementationOnce(() => ({
|
||||||
passengerDistance: 23000,
|
distance: 350101,
|
||||||
passengerDuration: 900,
|
duration: 14422,
|
||||||
fwdAzimuth: 283,
|
fwdAzimuth: 273,
|
||||||
backAzimuth: 93,
|
backAzimuth: 93,
|
||||||
distanceAzimuth: 19840,
|
distanceAzimuth: 336544,
|
||||||
points: [
|
points: [
|
||||||
{
|
{
|
||||||
lon: 6.1765103,
|
lon: 6.1765102,
|
||||||
lat: 48.689446,
|
lat: 48.689445,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
lon: 2.3523,
|
lon: 4.984578,
|
||||||
lat: 48.8567,
|
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', () => {
|
describe('Carpool route provider', () => {
|
||||||
|
@ -49,22 +152,79 @@ describe('Carpool route provider', () => {
|
||||||
expect(carpoolRouteProvider).toBeDefined();
|
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(
|
const carpoolRoute: CarpoolRoute = await carpoolRouteProvider.getBasic(
|
||||||
[Role.DRIVER],
|
[Role.DRIVER],
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
position: 0,
|
position: 0,
|
||||||
lat: 48.689445,
|
...originPoint,
|
||||||
lon: 6.1765102,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
position: 1,
|
position: 1,
|
||||||
lat: 48.8566,
|
...destinationPoint,
|
||||||
lon: 2.3522,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Coordinates } from '../../domain/route.types';
|
import { Point } from '../../domain/route.types';
|
||||||
|
|
||||||
export interface DirectionEncoderPort {
|
export interface DirectionEncoderPort {
|
||||||
encode(coordinates: Coordinates[]): string;
|
encode(coordinates: Point[]): string;
|
||||||
decode(direction: string): Coordinates[];
|
decode(direction: string): Point[];
|
||||||
}
|
}
|
||||||
|
|
|
@ -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';
|
import { GeorouterSettings } from '../types/georouter-settings.type';
|
||||||
|
|
||||||
export interface GeorouterPort {
|
export interface GeorouterPort {
|
||||||
routes(paths: Path[], settings: GeorouterSettings): Promise<Route[]>;
|
route(waypoints: Waypoint[], settings: GeorouterSettings): Promise<Route>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ export class GetRouteQueryHandler implements IQueryHandler {
|
||||||
|
|
||||||
execute = async (query: GetRouteQuery): Promise<RouteEntity> =>
|
execute = async (query: GetRouteQuery): Promise<RouteEntity> =>
|
||||||
await RouteEntity.create({
|
await RouteEntity.create({
|
||||||
roles: query.roles,
|
|
||||||
waypoints: query.waypoints,
|
waypoints: query.waypoints,
|
||||||
georouter: this.georouter,
|
georouter: this.georouter,
|
||||||
georouterSettings: query.georouterSettings,
|
georouterSettings: query.georouterSettings,
|
||||||
|
|
|
@ -1,19 +1,13 @@
|
||||||
import { QueryBase } from '@mobicoop/ddd-library';
|
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';
|
import { GeorouterSettings } from '../../types/georouter-settings.type';
|
||||||
|
|
||||||
export class GetRouteQuery extends QueryBase {
|
export class GetRouteQuery extends QueryBase {
|
||||||
readonly roles: Role[];
|
|
||||||
readonly waypoints: Waypoint[];
|
readonly waypoints: Waypoint[];
|
||||||
readonly georouterSettings: GeorouterSettings;
|
readonly georouterSettings: GeorouterSettings;
|
||||||
|
|
||||||
constructor(
|
constructor(waypoints: Waypoint[], georouterSettings: GeorouterSettings) {
|
||||||
roles: Role[],
|
|
||||||
waypoints: Waypoint[],
|
|
||||||
georouterSettings: GeorouterSettings,
|
|
||||||
) {
|
|
||||||
super();
|
super();
|
||||||
this.roles = roles;
|
|
||||||
this.waypoints = waypoints;
|
this.waypoints = waypoints;
|
||||||
this.georouterSettings = georouterSettings;
|
this.georouterSettings = georouterSettings;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,5 @@
|
||||||
import { AggregateRoot, AggregateID } from '@mobicoop/ddd-library';
|
import { AggregateRoot, AggregateID } from '@mobicoop/ddd-library';
|
||||||
import {
|
import { CreateRouteProps, RouteProps, Route } from './route.types';
|
||||||
CreateRouteProps,
|
|
||||||
Path,
|
|
||||||
Role,
|
|
||||||
RouteProps,
|
|
||||||
PathType,
|
|
||||||
Route,
|
|
||||||
} from './route.types';
|
|
||||||
import { WaypointProps } from './value-objects/waypoint.value-object';
|
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
import { RouteNotFoundException } from './route.errors';
|
import { RouteNotFoundException } from './route.errors';
|
||||||
|
|
||||||
|
@ -15,43 +7,18 @@ export class RouteEntity extends AggregateRoot<RouteProps> {
|
||||||
protected readonly _id: AggregateID;
|
protected readonly _id: AggregateID;
|
||||||
|
|
||||||
static create = async (create: CreateRouteProps): Promise<RouteEntity> => {
|
static create = async (create: CreateRouteProps): Promise<RouteEntity> => {
|
||||||
const routes: Route[] = await create.georouter.routes(
|
const route: Route = await create.georouter.route(
|
||||||
this.getPaths(create.roles, create.waypoints),
|
create.waypoints,
|
||||||
create.georouterSettings,
|
create.georouterSettings,
|
||||||
);
|
);
|
||||||
if (!routes || routes.length == 0) throw new RouteNotFoundException();
|
if (!route) 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;
|
|
||||||
}
|
|
||||||
const routeProps: RouteProps = {
|
const routeProps: RouteProps = {
|
||||||
driverDistance: driverRoute?.distance,
|
distance: route.distance,
|
||||||
driverDuration: driverRoute?.duration,
|
duration: route.duration,
|
||||||
passengerDistance: passengerRoute?.distance,
|
fwdAzimuth: route.fwdAzimuth,
|
||||||
passengerDuration: passengerRoute?.duration,
|
backAzimuth: route.backAzimuth,
|
||||||
fwdAzimuth: baseRoute.fwdAzimuth,
|
distanceAzimuth: route.distanceAzimuth,
|
||||||
backAzimuth: baseRoute.backAzimuth,
|
points: route.points,
|
||||||
distanceAzimuth: baseRoute.distanceAzimuth,
|
|
||||||
waypoints: create.waypoints,
|
|
||||||
points: baseRoute.points,
|
|
||||||
};
|
};
|
||||||
return new RouteEntity({
|
return new RouteEntity({
|
||||||
id: v4(),
|
id: v4(),
|
||||||
|
@ -63,46 +30,46 @@ export class RouteEntity extends AggregateRoot<RouteProps> {
|
||||||
// entity business rules validation to protect it's invariant before saving entity to a database
|
// entity business rules validation to protect it's invariant before saving entity to a database
|
||||||
}
|
}
|
||||||
|
|
||||||
private static getPaths = (
|
// private static getPaths = (
|
||||||
roles: Role[],
|
// roles: Role[],
|
||||||
waypoints: WaypointProps[],
|
// waypoints: WaypointProps[],
|
||||||
): Path[] => {
|
// ): Path[] => {
|
||||||
const paths: Path[] = [];
|
// const paths: Path[] = [];
|
||||||
if (roles.includes(Role.DRIVER) && roles.includes(Role.PASSENGER)) {
|
// if (roles.includes(Role.DRIVER) && roles.includes(Role.PASSENGER)) {
|
||||||
if (waypoints.length == 2) {
|
// if (waypoints.length == 2) {
|
||||||
// 2 points => same route for driver and passenger
|
// // 2 points => same route for driver and passenger
|
||||||
paths.push(this.createGenericPath(waypoints));
|
// paths.push(this.createGenericPath(waypoints));
|
||||||
} else {
|
// } else {
|
||||||
paths.push(
|
// paths.push(
|
||||||
this.createDriverPath(waypoints),
|
// this.createDriverPath(waypoints),
|
||||||
this.createPassengerPath(waypoints),
|
// this.createPassengerPath(waypoints),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
} else if (roles.includes(Role.DRIVER)) {
|
// } else if (roles.includes(Role.DRIVER)) {
|
||||||
paths.push(this.createDriverPath(waypoints));
|
// paths.push(this.createDriverPath(waypoints));
|
||||||
} else if (roles.includes(Role.PASSENGER)) {
|
// } else if (roles.includes(Role.PASSENGER)) {
|
||||||
paths.push(this.createPassengerPath(waypoints));
|
// paths.push(this.createPassengerPath(waypoints));
|
||||||
}
|
// }
|
||||||
return paths;
|
// return paths;
|
||||||
};
|
// };
|
||||||
|
|
||||||
private static createGenericPath = (waypoints: WaypointProps[]): Path =>
|
// private static createGenericPath = (waypoints: WaypointProps[]): Path =>
|
||||||
this.createPath(waypoints, PathType.GENERIC);
|
// this.createPath(waypoints, PathType.GENERIC);
|
||||||
|
|
||||||
private static createDriverPath = (waypoints: WaypointProps[]): Path =>
|
// private static createDriverPath = (waypoints: WaypointProps[]): Path =>
|
||||||
this.createPath(waypoints, PathType.DRIVER);
|
// this.createPath(waypoints, PathType.DRIVER);
|
||||||
|
|
||||||
private static createPassengerPath = (waypoints: WaypointProps[]): Path =>
|
// private static createPassengerPath = (waypoints: WaypointProps[]): Path =>
|
||||||
this.createPath(
|
// this.createPath(
|
||||||
[waypoints[0], waypoints[waypoints.length - 1]],
|
// [waypoints[0], waypoints[waypoints.length - 1]],
|
||||||
PathType.PASSENGER,
|
// PathType.PASSENGER,
|
||||||
);
|
// );
|
||||||
|
|
||||||
private static createPath = (
|
// private static createPath = (
|
||||||
points: WaypointProps[],
|
// points: WaypointProps[],
|
||||||
type: PathType,
|
// type: PathType,
|
||||||
): Path => ({
|
// ): Path => ({
|
||||||
type,
|
// type,
|
||||||
points,
|
// points,
|
||||||
});
|
// });
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,67 +1,49 @@
|
||||||
import { GeorouterPort } from '../application/ports/georouter.port';
|
import { GeorouterPort } from '../application/ports/georouter.port';
|
||||||
import { GeorouterSettings } from '../application/types/georouter-settings.type';
|
import { GeorouterSettings } from '../application/types/georouter-settings.type';
|
||||||
import { CoordinatesProps } from './value-objects/coordinates.value-object';
|
import { PointProps } from './value-objects/point.value-object';
|
||||||
import { SpacetimePointProps } from './value-objects/spacetime-point.value-object';
|
|
||||||
import { WaypointProps } from './value-objects/waypoint.value-object';
|
import { WaypointProps } from './value-objects/waypoint.value-object';
|
||||||
|
|
||||||
// All properties that a Route has
|
// All properties that a Route has
|
||||||
export interface RouteProps {
|
export interface RouteProps {
|
||||||
driverDistance?: number;
|
distance: number;
|
||||||
driverDuration?: number;
|
duration: number;
|
||||||
passengerDistance?: number;
|
|
||||||
passengerDuration?: number;
|
|
||||||
fwdAzimuth: number;
|
fwdAzimuth: number;
|
||||||
backAzimuth: number;
|
backAzimuth: number;
|
||||||
distanceAzimuth: number;
|
distanceAzimuth: number;
|
||||||
waypoints: WaypointProps[];
|
points: PointProps[];
|
||||||
points: SpacetimePointProps[] | CoordinatesProps[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Properties that are needed for a Route creation
|
// Properties that are needed for a Route creation
|
||||||
export interface CreateRouteProps {
|
export interface CreateRouteProps {
|
||||||
roles: Role[];
|
|
||||||
waypoints: WaypointProps[];
|
waypoints: WaypointProps[];
|
||||||
georouter: GeorouterPort;
|
georouter: GeorouterPort;
|
||||||
georouterSettings: GeorouterSettings;
|
georouterSettings: GeorouterSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Route = {
|
export type Route = {
|
||||||
type: PathType;
|
|
||||||
distance: number;
|
distance: number;
|
||||||
duration: number;
|
duration: number;
|
||||||
fwdAzimuth: number;
|
fwdAzimuth: number;
|
||||||
backAzimuth: number;
|
backAzimuth: number;
|
||||||
distanceAzimuth: number;
|
distanceAzimuth: number;
|
||||||
points: Coordinates[];
|
points: Point[];
|
||||||
spacetimeWaypoints: SpacetimePoint[];
|
steps: Step[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Path = {
|
export type Point = {
|
||||||
type: PathType;
|
|
||||||
points: Coordinates[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Coordinates = {
|
|
||||||
lon: number;
|
lon: number;
|
||||||
lat: number;
|
lat: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Waypoint = Coordinates & {
|
export type Waypoint = Point & {
|
||||||
position: number;
|
position: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SpacetimePoint = Coordinates & {
|
export type Spacetime = {
|
||||||
duration: number;
|
duration: number;
|
||||||
distance?: number;
|
distance?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum Role {
|
export type Step = Point & Spacetime;
|
||||||
DRIVER = 'DRIVER',
|
|
||||||
PASSENGER = 'PASSENGER',
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum PathType {
|
export type Waystep = Waypoint & Spacetime;
|
||||||
GENERIC = 'generic',
|
|
||||||
DRIVER = 'driver',
|
|
||||||
PASSENGER = 'passenger',
|
|
||||||
}
|
|
||||||
|
|
|
@ -8,12 +8,12 @@ import {
|
||||||
* other Value Objects inside if needed.
|
* other Value Objects inside if needed.
|
||||||
* */
|
* */
|
||||||
|
|
||||||
export interface CoordinatesProps {
|
export interface PointProps {
|
||||||
lon: number;
|
lon: number;
|
||||||
lat: number;
|
lat: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Coordinates extends ValueObject<CoordinatesProps> {
|
export class Point extends ValueObject<PointProps> {
|
||||||
get lon(): number {
|
get lon(): number {
|
||||||
return this.props.lon;
|
return this.props.lon;
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ export class Coordinates extends ValueObject<CoordinatesProps> {
|
||||||
return this.props.lat;
|
return this.props.lat;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected validate(props: CoordinatesProps): void {
|
protected validate(props: PointProps): void {
|
||||||
if (props.lon > 180 || props.lon < -180)
|
if (props.lon > 180 || props.lon < -180)
|
||||||
throw new ArgumentOutOfRangeException('lon must be between -180 and 180');
|
throw new ArgumentOutOfRangeException('lon must be between -180 and 180');
|
||||||
if (props.lat > 90 || props.lat < -90)
|
if (props.lat > 90 || props.lat < -90)
|
|
@ -9,14 +9,14 @@ import {
|
||||||
* other Value Objects inside if needed.
|
* other Value Objects inside if needed.
|
||||||
* */
|
* */
|
||||||
|
|
||||||
export interface SpacetimePointProps {
|
export interface StepProps {
|
||||||
lon: number;
|
lon: number;
|
||||||
lat: number;
|
lat: number;
|
||||||
duration: number;
|
duration: number;
|
||||||
distance: number;
|
distance: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SpacetimePoint extends ValueObject<SpacetimePointProps> {
|
export class Step extends ValueObject<StepProps> {
|
||||||
get lon(): number {
|
get lon(): number {
|
||||||
return this.props.lon;
|
return this.props.lon;
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ export class SpacetimePoint extends ValueObject<SpacetimePointProps> {
|
||||||
return this.props.distance;
|
return this.props.distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected validate(props: SpacetimePointProps): void {
|
protected validate(props: StepProps): void {
|
||||||
if (props.duration < 0)
|
if (props.duration < 0)
|
||||||
throw new ArgumentInvalidException(
|
throw new ArgumentInvalidException(
|
||||||
'duration must be greater than or equal to 0',
|
'duration must be greater than or equal to 0',
|
|
@ -2,12 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { HttpService } from '@nestjs/axios';
|
import { HttpService } from '@nestjs/axios';
|
||||||
import { GeorouterPort } from '../core/application/ports/georouter.port';
|
import { GeorouterPort } from '../core/application/ports/georouter.port';
|
||||||
import { GeorouterSettings } from '../core/application/types/georouter-settings.type';
|
import { GeorouterSettings } from '../core/application/types/georouter-settings.type';
|
||||||
import {
|
import { Route, Step, Waypoint } from '../core/domain/route.types';
|
||||||
Path,
|
|
||||||
PathType,
|
|
||||||
Route,
|
|
||||||
SpacetimePoint,
|
|
||||||
} from '../core/domain/route.types';
|
|
||||||
import { DefaultParamsProviderPort } from '../core/application/ports/default-params-provider.port';
|
import { DefaultParamsProviderPort } from '../core/application/ports/default-params-provider.port';
|
||||||
import { GEODESIC, PARAMS_PROVIDER } from '../geography.di-tokens';
|
import { GEODESIC, PARAMS_PROVIDER } from '../geography.di-tokens';
|
||||||
import { catchError, lastValueFrom, map } from 'rxjs';
|
import { catchError, lastValueFrom, map } from 'rxjs';
|
||||||
|
@ -35,13 +30,13 @@ export class GraphhopperGeorouter implements GeorouterPort {
|
||||||
].join('');
|
].join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
routes = async (
|
route = async (
|
||||||
paths: Path[],
|
waypoints: Waypoint[],
|
||||||
settings: GeorouterSettings,
|
settings: GeorouterSettings,
|
||||||
): Promise<Route[]> => {
|
): Promise<Route> => {
|
||||||
this._setDefaultUrlArgs();
|
this._setDefaultUrlArgs();
|
||||||
this._setSettings(settings);
|
this._setSettings(settings);
|
||||||
return this._getRoutes(paths);
|
return this._getRoute(waypoints);
|
||||||
};
|
};
|
||||||
|
|
||||||
private _setDefaultUrlArgs = (): void => {
|
private _setDefaultUrlArgs = (): void => {
|
||||||
|
@ -62,46 +57,39 @@ export class GraphhopperGeorouter implements GeorouterPort {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private _getRoutes = async (paths: Path[]): Promise<Route[]> => {
|
private _getRoute = async (waypoints: Waypoint[]): Promise<Route> => {
|
||||||
const routes = Promise.all(
|
const url: string = [
|
||||||
paths.map(async (path) => {
|
this.getUrl(),
|
||||||
const url: string = [
|
'&point=',
|
||||||
this.getUrl(),
|
waypoints
|
||||||
'&point=',
|
.map((waypoint: Waypoint) => [waypoint.lat, waypoint.lon].join('%2C'))
|
||||||
path.points
|
.join('&point='),
|
||||||
.map((point) => [point.lat, point.lon].join('%2C'))
|
].join('');
|
||||||
.join('&point='),
|
return await lastValueFrom(
|
||||||
].join('');
|
this.httpService.get(url).pipe(
|
||||||
return await lastValueFrom(
|
map((response) => {
|
||||||
this.httpService.get(url).pipe(
|
if (response.data) return this.createRoute(response);
|
||||||
map((response) => {
|
throw new Error();
|
||||||
if (response.data) return this.createRoute(response, path.type);
|
}),
|
||||||
throw new Error();
|
catchError((error: AxiosError) => {
|
||||||
}),
|
if (error.code == AxiosError.ERR_BAD_REQUEST) {
|
||||||
catchError((error: AxiosError) => {
|
throw new RouteNotFoundException(
|
||||||
if (error.code == AxiosError.ERR_BAD_REQUEST) {
|
error,
|
||||||
throw new RouteNotFoundException(
|
'No route found for given coordinates',
|
||||||
error,
|
);
|
||||||
'No route found for given coordinates',
|
}
|
||||||
);
|
throw new GeorouterUnavailableException(error);
|
||||||
}
|
}),
|
||||||
throw new GeorouterUnavailableException(error);
|
),
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
return routes;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private getUrl = (): string => [this.url, this.urlArgs.join('&')].join('');
|
private getUrl = (): string => [this.url, this.urlArgs.join('&')].join('');
|
||||||
|
|
||||||
private createRoute = (
|
private createRoute = (
|
||||||
response: AxiosResponse<GraphhopperResponse>,
|
response: AxiosResponse<GraphhopperResponse>,
|
||||||
type: PathType,
|
|
||||||
): Route => {
|
): Route => {
|
||||||
const route = {} as Route;
|
const route = {} as Route;
|
||||||
route.type = type;
|
|
||||||
if (response.data.paths && response.data.paths[0]) {
|
if (response.data.paths && response.data.paths[0]) {
|
||||||
const shortestPath = response.data.paths[0];
|
const shortestPath = response.data.paths[0];
|
||||||
route.distance = shortestPath.distance ?? 0;
|
route.distance = shortestPath.distance ?? 0;
|
||||||
|
@ -135,7 +123,7 @@ export class GraphhopperGeorouter implements GeorouterPort {
|
||||||
let instructions: GraphhopperInstruction[] = [];
|
let instructions: GraphhopperInstruction[] = [];
|
||||||
if (shortestPath.instructions)
|
if (shortestPath.instructions)
|
||||||
instructions = shortestPath.instructions;
|
instructions = shortestPath.instructions;
|
||||||
route.spacetimeWaypoints = this.generateSpacetimePoints(
|
route.steps = this.generateSteps(
|
||||||
shortestPath.points.coordinates,
|
shortestPath.points.coordinates,
|
||||||
shortestPath.snapped_waypoints.coordinates,
|
shortestPath.snapped_waypoints.coordinates,
|
||||||
shortestPath.details.time,
|
shortestPath.details.time,
|
||||||
|
@ -147,12 +135,12 @@ export class GraphhopperGeorouter implements GeorouterPort {
|
||||||
return route;
|
return route;
|
||||||
};
|
};
|
||||||
|
|
||||||
private generateSpacetimePoints = (
|
private generateSteps = (
|
||||||
points: [[number, number]],
|
points: [[number, number]],
|
||||||
snappedWaypoints: [[number, number]],
|
snappedWaypoints: [[number, number]],
|
||||||
durations: [[number, number, number]],
|
durations: [[number, number, number]],
|
||||||
instructions: GraphhopperInstruction[],
|
instructions: GraphhopperInstruction[],
|
||||||
): SpacetimePoint[] => {
|
): Step[] => {
|
||||||
const indices = this.getIndices(points, snappedWaypoints);
|
const indices = this.getIndices(points, snappedWaypoints);
|
||||||
const times = this.getTimes(durations, indices);
|
const times = this.getTimes(durations, indices);
|
||||||
const distances = this.getDistances(instructions, indices);
|
const distances = this.getDistances(instructions, indices);
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
import { DirectionEncoderPort } from '../core/application/ports/direction-encoder.port';
|
import { DirectionEncoderPort } from '../core/application/ports/direction-encoder.port';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { Coordinates } from '../core/domain/route.types';
|
import { Point } from '../core/domain/route.types';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PostgresDirectionEncoder implements DirectionEncoderPort {
|
export class PostgresDirectionEncoder implements DirectionEncoderPort {
|
||||||
encode = (coordinates: Coordinates[]): string =>
|
encode = (coordinates: Point[]): string =>
|
||||||
[
|
[
|
||||||
"'LINESTRING(",
|
"'LINESTRING(",
|
||||||
coordinates.map((point) => [point.lon, point.lat].join(' ')).join(),
|
coordinates.map((point) => [point.lon, point.lat].join(' ')).join(),
|
||||||
")'",
|
")'",
|
||||||
].join('');
|
].join('');
|
||||||
decode = (direction: string): Coordinates[] =>
|
decode = (direction: string): Point[] =>
|
||||||
direction
|
direction
|
||||||
.split('(')[1]
|
.split('(')[1]
|
||||||
.split(')')[0]
|
.split(')')[0]
|
||||||
|
|
|
@ -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 = {
|
export type GetRouteRequestDto = {
|
||||||
roles: Role[];
|
|
||||||
waypoints: Waypoint[];
|
waypoints: Waypoint[];
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,7 +16,7 @@ export class GetBasicRouteController implements GetBasicRouteControllerPort {
|
||||||
|
|
||||||
async get(data: GetRouteRequestDto): Promise<RouteResponseDto> {
|
async get(data: GetRouteRequestDto): Promise<RouteResponseDto> {
|
||||||
const route: RouteEntity = await this.queryBus.execute(
|
const route: RouteEntity = await this.queryBus.execute(
|
||||||
new GetRouteQuery(data.roles, data.waypoints, {
|
new GetRouteQuery(data.waypoints, {
|
||||||
detailedDistance: false,
|
detailedDistance: false,
|
||||||
detailedDuration: false,
|
detailedDuration: false,
|
||||||
points: true,
|
points: true,
|
||||||
|
|
|
@ -1,15 +1,10 @@
|
||||||
import {
|
import { Point, Step } from '@modules/geography/core/domain/route.types';
|
||||||
Coordinates,
|
|
||||||
SpacetimePoint,
|
|
||||||
} from '@modules/geography/core/domain/route.types';
|
|
||||||
|
|
||||||
export class RouteResponseDto {
|
export class RouteResponseDto {
|
||||||
driverDistance?: number;
|
distance?: number;
|
||||||
driverDuration?: number;
|
duration?: number;
|
||||||
passengerDistance?: number;
|
|
||||||
passengerDuration?: number;
|
|
||||||
fwdAzimuth: number;
|
fwdAzimuth: number;
|
||||||
backAzimuth: number;
|
backAzimuth: number;
|
||||||
distanceAzimuth: number;
|
distanceAzimuth: number;
|
||||||
points: SpacetimePoint[] | Coordinates[];
|
points: Step[] | Point[];
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,17 +16,11 @@ export class RouteMapper
|
||||||
{
|
{
|
||||||
toResponse = (entity: RouteEntity): RouteResponseDto => {
|
toResponse = (entity: RouteEntity): RouteResponseDto => {
|
||||||
const response = new RouteResponseDto();
|
const response = new RouteResponseDto();
|
||||||
response.driverDistance = entity.getProps().driverDistance
|
response.distance = entity.getProps().distance
|
||||||
? Math.round(entity.getProps().driverDistance as number)
|
? Math.round(entity.getProps().distance as number)
|
||||||
: undefined;
|
: undefined;
|
||||||
response.driverDuration = entity.getProps().driverDuration
|
response.duration = entity.getProps().duration
|
||||||
? Math.round(entity.getProps().driverDuration as number)
|
? Math.round(entity.getProps().duration 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)
|
|
||||||
: undefined;
|
: undefined;
|
||||||
response.fwdAzimuth = Math.round(entity.getProps().fwdAzimuth);
|
response.fwdAzimuth = Math.round(entity.getProps().fwdAzimuth);
|
||||||
response.backAzimuth = Math.round(entity.getProps().backAzimuth);
|
response.backAzimuth = Math.round(entity.getProps().backAzimuth);
|
||||||
|
|
|
@ -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 { 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 { GetRouteQueryHandler } from '@modules/geography/core/application/queries/get-route/get-route.query-handler';
|
||||||
import { RouteEntity } from '@modules/geography/core/domain/route.entity';
|
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 { GEOROUTER } from '@modules/geography/geography.di-tokens';
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ const destinationWaypoint: Waypoint = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockGeorouter: GeorouterPort = {
|
const mockGeorouter: GeorouterPort = {
|
||||||
routes: jest.fn(),
|
route: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('Get route query handler', () => {
|
describe('Get route query handler', () => {
|
||||||
|
@ -44,9 +44,8 @@ describe('Get route query handler', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('execution', () => {
|
describe('execution', () => {
|
||||||
it('should get a route for a driver only', async () => {
|
it('should get a route', async () => {
|
||||||
const getRoutequery = new GetRouteQuery(
|
const getRoutequery = new GetRouteQuery(
|
||||||
[Role.DRIVER],
|
|
||||||
[originWaypoint, destinationWaypoint],
|
[originWaypoint, destinationWaypoint],
|
||||||
{
|
{
|
||||||
detailedDistance: false,
|
detailedDistance: false,
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
import { ArgumentOutOfRangeException } from '@mobicoop/ddd-library';
|
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', () => {
|
describe('Point value object', () => {
|
||||||
it('should create a waypoint value object', () => {
|
it('should create a point value object', () => {
|
||||||
const coordinatesVO = new Coordinates({
|
const pointVO = new Point({
|
||||||
lat: 48.689445,
|
lat: 48.689445,
|
||||||
lon: 6.17651,
|
lon: 6.17651,
|
||||||
});
|
});
|
||||||
expect(coordinatesVO.lat).toBe(48.689445);
|
expect(pointVO.lat).toBe(48.689445);
|
||||||
expect(coordinatesVO.lon).toBe(6.17651);
|
expect(pointVO.lon).toBe(6.17651);
|
||||||
});
|
});
|
||||||
it('should throw an exception if longitude is invalid', () => {
|
it('should throw an exception if longitude is invalid', () => {
|
||||||
try {
|
try {
|
||||||
new Coordinates({
|
new Point({
|
||||||
lat: 48.689445,
|
lat: 48.689445,
|
||||||
lon: 186.17651,
|
lon: 186.17651,
|
||||||
});
|
});
|
||||||
|
@ -20,7 +20,7 @@ describe('Waypoint value object', () => {
|
||||||
expect(e).toBeInstanceOf(ArgumentOutOfRangeException);
|
expect(e).toBeInstanceOf(ArgumentOutOfRangeException);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
new Coordinates({
|
new Point({
|
||||||
lat: 48.689445,
|
lat: 48.689445,
|
||||||
lon: -186.17651,
|
lon: -186.17651,
|
||||||
});
|
});
|
||||||
|
@ -30,7 +30,7 @@ describe('Waypoint value object', () => {
|
||||||
});
|
});
|
||||||
it('should throw an exception if latitude is invalid', () => {
|
it('should throw an exception if latitude is invalid', () => {
|
||||||
try {
|
try {
|
||||||
new Coordinates({
|
new Point({
|
||||||
lat: 148.689445,
|
lat: 148.689445,
|
||||||
lon: 6.17651,
|
lon: 6.17651,
|
||||||
});
|
});
|
||||||
|
@ -38,7 +38,7 @@ describe('Waypoint value object', () => {
|
||||||
expect(e).toBeInstanceOf(ArgumentOutOfRangeException);
|
expect(e).toBeInstanceOf(ArgumentOutOfRangeException);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
new Coordinates({
|
new Point({
|
||||||
lat: -148.689445,
|
lat: -148.689445,
|
||||||
lon: 6.17651,
|
lon: 6.17651,
|
||||||
});
|
});
|
|
@ -2,205 +2,56 @@ import { GeorouterPort } from '@modules/geography/core/application/ports/georout
|
||||||
import { RouteEntity } from '@modules/geography/core/domain/route.entity';
|
import { RouteEntity } from '@modules/geography/core/domain/route.entity';
|
||||||
import { RouteNotFoundException } from '@modules/geography/core/domain/route.errors';
|
import { RouteNotFoundException } from '@modules/geography/core/domain/route.errors';
|
||||||
import {
|
import {
|
||||||
Coordinates,
|
Point,
|
||||||
CreateRouteProps,
|
CreateRouteProps,
|
||||||
PathType,
|
|
||||||
Role,
|
|
||||||
} from '@modules/geography/core/domain/route.types';
|
} from '@modules/geography/core/domain/route.types';
|
||||||
|
|
||||||
const originCoordinates: Coordinates = {
|
const originPoint: Point = {
|
||||||
lat: 48.689445,
|
lat: 48.689445,
|
||||||
lon: 6.17651,
|
lon: 6.17651,
|
||||||
};
|
};
|
||||||
const destinationCoordinates: Coordinates = {
|
const destinationPoint: Point = {
|
||||||
lat: 48.8566,
|
lat: 48.8566,
|
||||||
lon: 2.3522,
|
lon: 2.3522,
|
||||||
};
|
};
|
||||||
const additionalCoordinates: Coordinates = {
|
|
||||||
lon: 48.7566,
|
|
||||||
lat: 4.4498,
|
|
||||||
};
|
|
||||||
|
|
||||||
const mockGeorouter: GeorouterPort = {
|
const mockGeorouter: GeorouterPort = {
|
||||||
routes: jest
|
route: jest
|
||||||
.fn()
|
.fn()
|
||||||
.mockImplementationOnce(() => [
|
.mockImplementationOnce(() => ({
|
||||||
{
|
distance: 350101,
|
||||||
type: PathType.DRIVER,
|
duration: 14422,
|
||||||
distance: 350101,
|
fwdAzimuth: 273,
|
||||||
duration: 14422,
|
backAzimuth: 93,
|
||||||
fwdAzimuth: 273,
|
distanceAzimuth: 336544,
|
||||||
backAzimuth: 93,
|
points: [
|
||||||
distanceAzimuth: 336544,
|
{
|
||||||
points: [
|
lon: 6.1765102,
|
||||||
{
|
lat: 48.689445,
|
||||||
lon: 6.1765102,
|
},
|
||||||
lat: 48.689445,
|
{
|
||||||
},
|
lon: 4.984578,
|
||||||
{
|
lat: 48.725687,
|
||||||
lon: 4.984578,
|
},
|
||||||
lat: 48.725687,
|
{
|
||||||
},
|
lon: 2.3522,
|
||||||
{
|
lat: 48.8566,
|
||||||
lon: 2.3522,
|
},
|
||||||
lat: 48.8566,
|
],
|
||||||
},
|
steps: [],
|
||||||
],
|
}))
|
||||||
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(() => []),
|
.mockImplementationOnce(() => []),
|
||||||
};
|
};
|
||||||
|
|
||||||
const createDriverRouteProps: CreateRouteProps = {
|
const createRouteProps: CreateRouteProps = {
|
||||||
roles: [Role.DRIVER],
|
|
||||||
waypoints: [
|
waypoints: [
|
||||||
{
|
{
|
||||||
position: 0,
|
position: 0,
|
||||||
...originCoordinates,
|
...originPoint,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
position: 1,
|
position: 1,
|
||||||
...destinationCoordinates,
|
...destinationPoint,
|
||||||
},
|
|
||||||
],
|
|
||||||
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,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
georouter: mockGeorouter,
|
georouter: mockGeorouter,
|
||||||
|
@ -212,43 +63,15 @@ const createComplexDriverAndPassengerRouteProps: CreateRouteProps = {
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('Route entity create', () => {
|
describe('Route entity create', () => {
|
||||||
it('should create a new entity for a driver only', async () => {
|
it('should create a new entity', async () => {
|
||||||
const route: RouteEntity = await RouteEntity.create(createDriverRouteProps);
|
const route: RouteEntity = await RouteEntity.create(createRouteProps);
|
||||||
expect(route.id.length).toBe(36);
|
expect(route.id.length).toBe(36);
|
||||||
expect(route.getProps().driverDuration).toBe(14422);
|
expect(route.getProps().duration).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);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an exception if route is not found', async () => {
|
it('should throw an exception if route is not found', async () => {
|
||||||
try {
|
try {
|
||||||
await RouteEntity.create(createDriverRouteProps);
|
await RouteEntity.create(createRouteProps);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
expect(e).toBeInstanceOf(RouteNotFoundException);
|
expect(e).toBeInstanceOf(RouteNotFoundException);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,24 +2,24 @@ import {
|
||||||
ArgumentInvalidException,
|
ArgumentInvalidException,
|
||||||
ArgumentOutOfRangeException,
|
ArgumentOutOfRangeException,
|
||||||
} from '@mobicoop/ddd-library';
|
} 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', () => {
|
describe('Step value object', () => {
|
||||||
it('should create a timepoint value object', () => {
|
it('should create a step value object', () => {
|
||||||
const timepointVO = new SpacetimePoint({
|
const stepVO = new Step({
|
||||||
lat: 48.689445,
|
lat: 48.689445,
|
||||||
lon: 6.17651,
|
lon: 6.17651,
|
||||||
duration: 150,
|
duration: 150,
|
||||||
distance: 12000,
|
distance: 12000,
|
||||||
});
|
});
|
||||||
expect(timepointVO.duration).toBe(150);
|
expect(stepVO.duration).toBe(150);
|
||||||
expect(timepointVO.distance).toBe(12000);
|
expect(stepVO.distance).toBe(12000);
|
||||||
expect(timepointVO.lat).toBe(48.689445);
|
expect(stepVO.lat).toBe(48.689445);
|
||||||
expect(timepointVO.lon).toBe(6.17651);
|
expect(stepVO.lon).toBe(6.17651);
|
||||||
});
|
});
|
||||||
it('should throw an exception if longitude is invalid', () => {
|
it('should throw an exception if longitude is invalid', () => {
|
||||||
try {
|
try {
|
||||||
new SpacetimePoint({
|
new Step({
|
||||||
lat: 48.689445,
|
lat: 48.689445,
|
||||||
lon: 186.17651,
|
lon: 186.17651,
|
||||||
duration: 150,
|
duration: 150,
|
||||||
|
@ -29,7 +29,7 @@ describe('Timepoint value object', () => {
|
||||||
expect(e).toBeInstanceOf(ArgumentOutOfRangeException);
|
expect(e).toBeInstanceOf(ArgumentOutOfRangeException);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
new SpacetimePoint({
|
new Step({
|
||||||
lon: 48.689445,
|
lon: 48.689445,
|
||||||
lat: -186.17651,
|
lat: -186.17651,
|
||||||
duration: 150,
|
duration: 150,
|
||||||
|
@ -41,7 +41,7 @@ describe('Timepoint value object', () => {
|
||||||
});
|
});
|
||||||
it('should throw an exception if latitude is invalid', () => {
|
it('should throw an exception if latitude is invalid', () => {
|
||||||
try {
|
try {
|
||||||
new SpacetimePoint({
|
new Step({
|
||||||
lat: 248.689445,
|
lat: 248.689445,
|
||||||
lon: 6.17651,
|
lon: 6.17651,
|
||||||
duration: 150,
|
duration: 150,
|
||||||
|
@ -51,7 +51,7 @@ describe('Timepoint value object', () => {
|
||||||
expect(e).toBeInstanceOf(ArgumentOutOfRangeException);
|
expect(e).toBeInstanceOf(ArgumentOutOfRangeException);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
new SpacetimePoint({
|
new Step({
|
||||||
lon: -148.689445,
|
lon: -148.689445,
|
||||||
lat: 6.17651,
|
lat: 6.17651,
|
||||||
duration: 150,
|
duration: 150,
|
||||||
|
@ -63,7 +63,7 @@ describe('Timepoint value object', () => {
|
||||||
});
|
});
|
||||||
it('should throw an exception if distance is invalid', () => {
|
it('should throw an exception if distance is invalid', () => {
|
||||||
try {
|
try {
|
||||||
new SpacetimePoint({
|
new Step({
|
||||||
lat: 48.689445,
|
lat: 48.689445,
|
||||||
lon: 6.17651,
|
lon: 6.17651,
|
||||||
duration: 150,
|
duration: 150,
|
||||||
|
@ -75,7 +75,7 @@ describe('Timepoint value object', () => {
|
||||||
});
|
});
|
||||||
it('should throw an exception if duration is invalid', () => {
|
it('should throw an exception if duration is invalid', () => {
|
||||||
try {
|
try {
|
||||||
new SpacetimePoint({
|
new Step({
|
||||||
lat: 48.689445,
|
lat: 48.689445,
|
||||||
lon: 6.17651,
|
lon: 6.17651,
|
||||||
duration: -150,
|
duration: -150,
|
|
@ -4,7 +4,7 @@ import {
|
||||||
GeorouterUnavailableException,
|
GeorouterUnavailableException,
|
||||||
RouteNotFoundException,
|
RouteNotFoundException,
|
||||||
} from '@modules/geography/core/domain/route.errors';
|
} 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 {
|
import {
|
||||||
GEODESIC,
|
GEODESIC,
|
||||||
PARAMS_PROVIDER,
|
PARAMS_PROVIDER,
|
||||||
|
@ -294,20 +294,17 @@ describe('Graphhopper Georouter', () => {
|
||||||
|
|
||||||
it('should fail if route is not found', async () => {
|
it('should fail if route is not found', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
graphhopperGeorouter.routes(
|
graphhopperGeorouter.route(
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
type: PathType.DRIVER,
|
position: 0,
|
||||||
points: [
|
lon: 0,
|
||||||
{
|
lat: 0,
|
||||||
lon: 0,
|
},
|
||||||
lat: 0,
|
{
|
||||||
},
|
position: 1,
|
||||||
{
|
lon: 1,
|
||||||
lon: 1,
|
lat: 1,
|
||||||
lat: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
|
@ -321,20 +318,17 @@ describe('Graphhopper Georouter', () => {
|
||||||
|
|
||||||
it('should fail if georouter is unavailable', async () => {
|
it('should fail if georouter is unavailable', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
graphhopperGeorouter.routes(
|
graphhopperGeorouter.route(
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
type: PathType.DRIVER,
|
position: 0,
|
||||||
points: [
|
lon: 0,
|
||||||
{
|
lat: 0,
|
||||||
lon: 0,
|
},
|
||||||
lat: 0,
|
{
|
||||||
},
|
position: 1,
|
||||||
{
|
lon: 1,
|
||||||
lon: 1,
|
lat: 1,
|
||||||
lat: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
|
@ -347,20 +341,17 @@ describe('Graphhopper Georouter', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create a basic route', async () => {
|
it('should create a basic route', async () => {
|
||||||
const routes: Route[] = await graphhopperGeorouter.routes(
|
const route: Route = await graphhopperGeorouter.route(
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
type: PathType.DRIVER,
|
position: 0,
|
||||||
points: [
|
lon: 0,
|
||||||
{
|
lat: 0,
|
||||||
lon: 0,
|
},
|
||||||
lat: 0,
|
{
|
||||||
},
|
position: 1,
|
||||||
{
|
lon: 10,
|
||||||
lon: 10,
|
lat: 10,
|
||||||
lat: 10,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
|
@ -369,25 +360,21 @@ describe('Graphhopper Georouter', () => {
|
||||||
points: false,
|
points: false,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
expect(routes).toHaveLength(1);
|
expect(route.distance).toBe(50000);
|
||||||
expect(routes[0].distance).toBe(50000);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create one route with points', async () => {
|
it('should create a route with points', async () => {
|
||||||
const routes = await graphhopperGeorouter.routes(
|
const route: Route = await graphhopperGeorouter.route(
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
type: PathType.DRIVER,
|
position: 0,
|
||||||
points: [
|
lon: 0,
|
||||||
{
|
lat: 0,
|
||||||
lat: 0,
|
},
|
||||||
lon: 0,
|
{
|
||||||
},
|
position: 1,
|
||||||
{
|
lon: 10,
|
||||||
lat: 10,
|
lat: 10,
|
||||||
lon: 10,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
|
@ -396,29 +383,25 @@ describe('Graphhopper Georouter', () => {
|
||||||
points: true,
|
points: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
expect(routes).toHaveLength(1);
|
expect(route.distance).toBe(50000);
|
||||||
expect(routes[0].distance).toBe(50000);
|
expect(route.duration).toBe(1800);
|
||||||
expect(routes[0].duration).toBe(1800);
|
expect(route.fwdAzimuth).toBe(45);
|
||||||
expect(routes[0].fwdAzimuth).toBe(45);
|
expect(route.backAzimuth).toBe(225);
|
||||||
expect(routes[0].backAzimuth).toBe(225);
|
expect(route.points).toHaveLength(11);
|
||||||
expect(routes[0].points).toHaveLength(11);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create one route with points and time', async () => {
|
it('should create a route with points and time', async () => {
|
||||||
const routes = await graphhopperGeorouter.routes(
|
const route: Route = await graphhopperGeorouter.route(
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
type: PathType.DRIVER,
|
position: 0,
|
||||||
points: [
|
lon: 0,
|
||||||
{
|
lat: 0,
|
||||||
lat: 0,
|
},
|
||||||
lon: 0,
|
{
|
||||||
},
|
position: 1,
|
||||||
{
|
lon: 10,
|
||||||
lat: 10,
|
lat: 10,
|
||||||
lon: 10,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
|
@ -427,31 +410,28 @@ describe('Graphhopper Georouter', () => {
|
||||||
points: true,
|
points: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
expect(routes).toHaveLength(1);
|
expect(route.steps).toHaveLength(2);
|
||||||
expect(routes[0].spacetimeWaypoints).toHaveLength(2);
|
expect(route.steps[1].duration).toBe(1800);
|
||||||
expect(routes[0].spacetimeWaypoints[1].duration).toBe(1800);
|
expect(route.steps[1].distance).toBeUndefined();
|
||||||
expect(routes[0].spacetimeWaypoints[1].distance).toBeUndefined();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create one route with points and missed waypoints extrapolations', async () => {
|
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,
|
position: 0,
|
||||||
points: [
|
lon: 0,
|
||||||
{
|
lat: 0,
|
||||||
lat: 0,
|
},
|
||||||
lon: 0,
|
{
|
||||||
},
|
position: 1,
|
||||||
{
|
lon: 5,
|
||||||
lat: 5,
|
lat: 5,
|
||||||
lon: 5,
|
},
|
||||||
},
|
{
|
||||||
{
|
position: 2,
|
||||||
lat: 10,
|
lon: 10,
|
||||||
lon: 10,
|
lat: 10,
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
|
@ -460,30 +440,26 @@ describe('Graphhopper Georouter', () => {
|
||||||
points: true,
|
points: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
expect(routes).toHaveLength(1);
|
expect(route.steps).toHaveLength(3);
|
||||||
expect(routes[0].spacetimeWaypoints).toHaveLength(3);
|
expect(route.distance).toBe(50000);
|
||||||
expect(routes[0].distance).toBe(50000);
|
expect(route.duration).toBe(1800);
|
||||||
expect(routes[0].duration).toBe(1800);
|
expect(route.fwdAzimuth).toBe(45);
|
||||||
expect(routes[0].fwdAzimuth).toBe(45);
|
expect(route.backAzimuth).toBe(225);
|
||||||
expect(routes[0].backAzimuth).toBe(225);
|
expect(route.points.length).toBe(9);
|
||||||
expect(routes[0].points.length).toBe(9);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create one route with points, time and distance', async () => {
|
it('should create a route with points, time and distance', async () => {
|
||||||
const routes = await graphhopperGeorouter.routes(
|
const route: Route = await graphhopperGeorouter.route(
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
type: PathType.DRIVER,
|
position: 0,
|
||||||
points: [
|
lon: 0,
|
||||||
{
|
lat: 0,
|
||||||
lat: 0,
|
},
|
||||||
lon: 0,
|
{
|
||||||
},
|
position: 1,
|
||||||
{
|
lon: 10,
|
||||||
lat: 10,
|
lat: 10,
|
||||||
lon: 10,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
|
@ -492,9 +468,8 @@ describe('Graphhopper Georouter', () => {
|
||||||
points: true,
|
points: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
expect(routes).toHaveLength(1);
|
expect(route.steps.length).toBe(3);
|
||||||
expect(routes[0].spacetimeWaypoints.length).toBe(3);
|
expect(route.steps[1].duration).toBe(990);
|
||||||
expect(routes[0].spacetimeWaypoints[1].duration).toBe(990);
|
expect(route.steps[1].distance).toBe(25000);
|
||||||
expect(routes[0].spacetimeWaypoints[1].distance).toBe(25000);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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';
|
import { PostgresDirectionEncoder } from '@modules/geography/infrastructure/postgres-direction-encoder';
|
||||||
|
|
||||||
describe('Postgres direction encoder', () => {
|
describe('Postgres direction encoder', () => {
|
||||||
|
@ -7,10 +7,10 @@ describe('Postgres direction encoder', () => {
|
||||||
new PostgresDirectionEncoder();
|
new PostgresDirectionEncoder();
|
||||||
expect(postgresDirectionEncoder).toBeDefined();
|
expect(postgresDirectionEncoder).toBeDefined();
|
||||||
});
|
});
|
||||||
it('should encode coordinates to a postgres direction', () => {
|
it('should encode points to a postgres direction', () => {
|
||||||
const postgresDirectionEncoder: PostgresDirectionEncoder =
|
const postgresDirectionEncoder: PostgresDirectionEncoder =
|
||||||
new PostgresDirectionEncoder();
|
new PostgresDirectionEncoder();
|
||||||
const coordinates: Coordinates[] = [
|
const points: Point[] = [
|
||||||
{
|
{
|
||||||
lon: 6,
|
lon: 6,
|
||||||
lat: 47,
|
lat: 47,
|
||||||
|
@ -24,18 +24,17 @@ describe('Postgres direction encoder', () => {
|
||||||
lat: 47.2,
|
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)'");
|
expect(direction).toBe("'LINESTRING(6 47,6.1 47.1,6.2 47.2)'");
|
||||||
});
|
});
|
||||||
it('should decode a postgres direction to coordinates', () => {
|
it('should decode a postgres direction to coordinates', () => {
|
||||||
const postgresDirectionEncoder: PostgresDirectionEncoder =
|
const postgresDirectionEncoder: PostgresDirectionEncoder =
|
||||||
new PostgresDirectionEncoder();
|
new PostgresDirectionEncoder();
|
||||||
const direction = "'LINESTRING(6 47,6.1 47.1,6.2 47.2)'";
|
const direction = "'LINESTRING(6 47,6.1 47.1,6.2 47.2)'";
|
||||||
const coordinates: Coordinates[] =
|
const points: Point[] = postgresDirectionEncoder.decode(direction);
|
||||||
postgresDirectionEncoder.decode(direction);
|
expect(points.length).toBe(3);
|
||||||
expect(coordinates.length).toBe(3);
|
expect(points[0].lat).toBe(47);
|
||||||
expect(coordinates[0].lat).toBe(47);
|
expect(points[1].lon).toBe(6.1);
|
||||||
expect(coordinates[1].lon).toBe(6.1);
|
expect(points[2].lat).toBe(47.2);
|
||||||
expect(coordinates[2].lat).toBe(47.2);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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 { GetBasicRouteController } from '@modules/geography/interface/controllers/get-basic-route.controller';
|
||||||
import { RouteMapper } from '@modules/geography/route.mapper';
|
import { RouteMapper } from '@modules/geography/route.mapper';
|
||||||
import { QueryBus } from '@nestjs/cqrs';
|
import { QueryBus } from '@nestjs/cqrs';
|
||||||
|
@ -48,7 +47,6 @@ describe('Get Basic Route Controller', () => {
|
||||||
it('should get a route', async () => {
|
it('should get a route', async () => {
|
||||||
jest.spyOn(mockQueryBus, 'execute');
|
jest.spyOn(mockQueryBus, 'execute');
|
||||||
await getBasicRouteController.get({
|
await getBasicRouteController.get({
|
||||||
roles: [Role.DRIVER],
|
|
||||||
waypoints: [
|
waypoints: [
|
||||||
{
|
{
|
||||||
position: 0,
|
position: 0,
|
||||||
|
|
|
@ -23,28 +23,23 @@ describe('Route Mapper', () => {
|
||||||
createdAt: now,
|
createdAt: now,
|
||||||
updatedAt: now,
|
updatedAt: now,
|
||||||
props: {
|
props: {
|
||||||
driverDistance: 23000,
|
distance: 23000,
|
||||||
driverDuration: 900,
|
duration: 900,
|
||||||
passengerDistance: 23000,
|
|
||||||
passengerDuration: 900,
|
|
||||||
fwdAzimuth: 283,
|
fwdAzimuth: 283,
|
||||||
backAzimuth: 93,
|
backAzimuth: 93,
|
||||||
distanceAzimuth: 19840,
|
distanceAzimuth: 19840,
|
||||||
points: [],
|
points: [
|
||||||
waypoints: [
|
|
||||||
{
|
{
|
||||||
position: 0,
|
|
||||||
lon: 6.1765103,
|
lon: 6.1765103,
|
||||||
lat: 48.689446,
|
lat: 48.689446,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
position: 1,
|
|
||||||
lon: 2.3523,
|
lon: 2.3523,
|
||||||
lat: 48.8567,
|
lat: 48.8567,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(routeMapper.toResponse(routeEntity).driverDistance).toBe(23000);
|
expect(routeMapper.toResponse(routeEntity).distance).toBe(23000);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue