geography entity with tests

This commit is contained in:
sbriat 2023-04-20 15:52:01 +02:00
parent ed99b442eb
commit 6dd4837c89
18 changed files with 458 additions and 121 deletions

View File

@ -6,7 +6,7 @@ import { IDefaultParams } from '../../domain/types/default-params.type';
export class DefaultParamsProvider {
constructor(private readonly configService: ConfigService) {}
getParams(): IDefaultParams {
getParams = (): IDefaultParams => {
return {
DEFAULT_IDENTIFIER: parseInt(
this.configService.get('DEFAULT_IDENTIFIER'),
@ -33,5 +33,5 @@ export class DefaultParamsProvider {
georouterUrl: this.configService.get('GEOROUTER_URL'),
},
};
}
};
}

View File

@ -10,12 +10,12 @@ export class MatcherGeodesic implements IGeodesic {
this._geod = Geodesic.WGS84;
}
inverse(
inverse = (
lon1: number,
lat1: number,
lon2: number,
lat2: number,
): { azimuth: number; distance: number } {
): { azimuth: number; distance: number } => {
const { azi2: azimuth, s12: distance } = this._geod.Inverse(
lat1,
lon1,
@ -23,5 +23,5 @@ export class MatcherGeodesic implements IGeodesic {
lon2,
);
return { azimuth, distance };
}
};
}

View File

@ -12,12 +12,12 @@ export class GeorouterCreator implements ICreateGeorouter {
private readonly geodesic: MatcherGeodesic,
) {}
create(type: string, url: string): IGeorouter {
create = (type: string, url: string): IGeorouter => {
switch (type) {
case 'graphhopper':
return new GraphhopperGeorouter(url, this.httpService, this.geodesic);
default:
throw new Error('Unknown geocoder');
}
}
};
}

View File

@ -27,50 +27,50 @@ export class GraphhopperGeorouter implements IGeorouter {
this._geodesic = geodesic;
}
async route(
route = async (
paths: Array<Path>,
settings: GeorouterSettings,
): Promise<Array<NamedRoute>> {
): Promise<Array<NamedRoute>> => {
this._setDefaultUrlArgs();
this._setWithTime(settings.withTime);
this._setWithPoints(settings.withPoints);
this._setWithDistance(settings.withDistance);
this._paths = paths;
return await this._getRoutes();
}
};
_setDefaultUrlArgs(): void {
_setDefaultUrlArgs = (): void => {
this._urlArgs = [
'vehicle=car',
'weighting=fastest',
'points_encoded=false',
];
}
};
_setWithTime(withTime: boolean): void {
_setWithTime = (withTime: boolean): void => {
this._withTime = withTime;
if (withTime) {
this._urlArgs.push('details=time');
}
}
};
_setWithPoints(withPoints: boolean): void {
_setWithPoints = (withPoints: boolean): void => {
this._withPoints = withPoints;
if (!withPoints) {
this._urlArgs.push('calc_points=false');
}
}
};
_setWithDistance(withDistance: boolean): void {
_setWithDistance = (withDistance: boolean): void => {
this._withDistance = withDistance;
if (withDistance) {
this._urlArgs.push('instructions=true');
} else {
this._urlArgs.push('instructions=false');
}
}
};
async _getRoutes(): Promise<Array<NamedRoute>> {
_getRoutes = async (): Promise<Array<NamedRoute>> => {
const routes = Promise.all(
this._paths.map(async (path) => {
const url: string = [
@ -95,20 +95,25 @@ export class GraphhopperGeorouter implements IGeorouter {
}),
);
return routes;
}
};
_getUrl(): string {
_getUrl = (): string => {
return [this._url, this._urlArgs.join('&')].join('');
}
};
_createRoute(response: AxiosResponse<GraphhopperResponse>): Route {
_createRoute = (response: AxiosResponse<GraphhopperResponse>): Route => {
const route = new Route(this._geodesic);
if (response.data.paths && response.data.paths[0]) {
const shortestPath = response.data.paths[0];
route.distance = shortestPath.distance ?? 0;
route.duration = shortestPath.time ? shortestPath.time / 1000 : 0;
if (shortestPath.points && shortestPath.points.coordinates) {
route.setPoints(shortestPath.points.coordinates);
route.setPoints(
shortestPath.points.coordinates.map((coordinate) => ({
lon: coordinate[0],
lat: coordinate[1],
})),
);
if (
shortestPath.details &&
shortestPath.details.time &&
@ -130,14 +135,14 @@ export class GraphhopperGeorouter implements IGeorouter {
}
}
return route;
}
};
_generateSpacetimePoints(
_generateSpacetimePoints = (
points: Array<Array<number>>,
snappedWaypoints: Array<Array<number>>,
durations: Array<Array<number>>,
instructions: Array<GraphhopperInstruction>,
): Array<SpacetimePoint> {
): Array<SpacetimePoint> => {
const indices = this._getIndices(points, snappedWaypoints);
const times = this._getTimes(durations, indices);
const distances = this._getDistances(instructions, indices);
@ -149,12 +154,12 @@ export class GraphhopperGeorouter implements IGeorouter {
distances.find((distance) => distance.index == index)?.distance,
),
);
}
};
_getIndices(
_getIndices = (
points: Array<Array<number>>,
snappedWaypoints: Array<Array<number>>,
): Array<number> {
): Array<number> => {
const indices = snappedWaypoints.map((waypoint) =>
points.findIndex(
(point) => point[0] == waypoint[0] && point[1] == waypoint[1],
@ -199,12 +204,12 @@ export class GraphhopperGeorouter implements IGeorouter {
indices[missedWaypoint.originIndex] = missedWaypoint.nearest;
}
return indices;
}
};
_getTimes(
_getTimes = (
durations: Array<Array<number>>,
indices: Array<number>,
): Array<{ index: number; duration: number }> {
): Array<{ index: number; duration: number }> => {
const times: Array<{ index: number; duration: number }> = [];
let duration = 0;
for (const [origin, destination, stepDuration] of durations) {
@ -249,12 +254,12 @@ export class GraphhopperGeorouter implements IGeorouter {
duration += stepDuration;
}
return times;
}
};
_getDistances(
_getDistances = (
instructions: Array<GraphhopperInstruction>,
indices: Array<number>,
): Array<{ index: number; distance: number }> {
): Array<{ index: number; distance: number }> => {
let distance = 0;
const distances: Array<{ index: number; distance: number }> = [
{
@ -276,7 +281,7 @@ export class GraphhopperGeorouter implements IGeorouter {
}
}
return distances;
}
};
}
type GraphhopperResponse = {

View File

@ -12,7 +12,7 @@ export class Messager extends MessageBroker {
super(configService.get<string>('RMQ_EXCHANGE'));
}
publish(routingKey: string, message: string): void {
publish = (routingKey: string, message: string): void => {
this._amqpConnection.publish(this.exchange, routingKey, message);
}
};
}

View File

@ -6,4 +6,10 @@ export class Actor {
person: Person;
role: Role;
step: Step;
constructor(person: Person, role: Role, step: Step) {
this.person = person;
this.role = role;
this.step = step;
}
}

View File

@ -3,32 +3,129 @@ import { IRequestGeography } from '../interfaces/geography-request.interface';
import { PointType } from '../types/geography.enum';
import { Point } from '../types/point.type';
import { find } from 'geo-tz';
import { Waypoint } from '../types/waypoint';
import { Route } from './route';
import { Role } from '../types/role.enum';
import { IGeorouter } from '../interfaces/georouter.interface';
import { Waypoint } from './waypoint';
import { Actor } from './actor';
import { Person } from './person';
import { Step } from '../types/step.enum';
import { Path } from '../types/path.type';
export class Geography {
_geographyRequest: IRequestGeography;
waypoints: Array<Waypoint>;
_person: Person;
_points: Array<Point>;
originType: PointType;
destinationType: PointType;
timezones: Array<string>;
driverRoute: Route;
passengerRoute: Route;
constructor(geographyRequest: IRequestGeography, defaultTimezone: string) {
constructor(
geographyRequest: IRequestGeography,
defaultTimezone: string,
person: Person,
) {
this._geographyRequest = geographyRequest;
this.waypoints = [];
this.originType = PointType.OTHER;
this.destinationType = PointType.OTHER;
this._person = person;
this._points = [];
this.originType = undefined;
this.destinationType = undefined;
this.timezones = [defaultTimezone];
}
init() {
init = (): void => {
this._validateWaypoints();
this._setTimezones();
}
this._setPointTypes();
};
_validateWaypoints() {
createRoutes = async (
roles: Array<Role>,
georouter: IGeorouter,
): Promise<void> => {
let driverWaypoints: Array<Waypoint> = [];
let passengerWaypoints: Array<Waypoint> = [];
const paths: Array<Path> = [];
if (roles.includes(Role.DRIVER) && roles.includes(Role.PASSENGER)) {
if (this._points.length == 2) {
// 2 points => same route for driver and passenger
const commonPath: Path = {
key: RouteKey.COMMON,
points: this._points,
};
driverWaypoints = this._createWaypoints(commonPath.points, Role.DRIVER);
passengerWaypoints = this._createWaypoints(
commonPath.points,
Role.PASSENGER,
);
paths.push(commonPath);
} else {
const driverPath: Path = {
key: RouteKey.DRIVER,
points: this._points,
};
driverWaypoints = this._createWaypoints(driverPath.points, Role.DRIVER);
const passengerPath: Path = {
key: RouteKey.PASSENGER,
points: [this._points[0], this._points[this._points.length - 1]],
};
passengerWaypoints = this._createWaypoints(
passengerPath.points,
Role.PASSENGER,
);
paths.push(driverPath, passengerPath);
}
} else if (roles.includes(Role.DRIVER)) {
const driverPath: Path = {
key: RouteKey.DRIVER,
points: this._points,
};
driverWaypoints = this._createWaypoints(driverPath.points, Role.DRIVER);
paths.push(driverPath);
} else if (roles.includes(Role.PASSENGER)) {
const passengerPath: Path = {
key: RouteKey.PASSENGER,
points: [this._points[0], this._points[this._points.length - 1]],
};
passengerWaypoints = this._createWaypoints(
passengerPath.points,
Role.PASSENGER,
);
paths.push(passengerPath);
}
const routes = await georouter.route(paths, {
withDistance: false,
withPoints: true,
withTime: false,
});
if (routes.some((route) => route.key == RouteKey.COMMON)) {
this.driverRoute = routes.find(
(route) => route.key == RouteKey.COMMON,
).route;
this.passengerRoute = routes.find(
(route) => route.key == RouteKey.COMMON,
).route;
this.driverRoute.setWaypoints(driverWaypoints);
this.passengerRoute.setWaypoints(passengerWaypoints);
} else {
if (routes.some((route) => route.key == RouteKey.DRIVER)) {
this.driverRoute = routes.find(
(route) => route.key == RouteKey.DRIVER,
).route;
this.driverRoute.setWaypoints(driverWaypoints);
}
if (routes.some((route) => route.key == RouteKey.PASSENGER)) {
this.passengerRoute = routes.find(
(route) => route.key == RouteKey.PASSENGER,
).route;
this.passengerRoute.setWaypoints(passengerWaypoints);
}
}
};
_validateWaypoints = (): void => {
if (this._geographyRequest.waypoints.length < 2) {
throw new MatcherException(3, 'At least 2 waypoints are required');
}
@ -39,19 +136,25 @@ export class Geography {
`Waypoint { Lon: ${point.lon}, Lat: ${point.lat} } is not valid`,
);
}
this.waypoints.push({
point,
actors: [],
this._points.push(point);
});
});
}
};
_setTimezones() {
_setTimezones = (): void => {
this.timezones = find(
this._geographyRequest.waypoints[0].lat,
this._geographyRequest.waypoints[0].lon,
);
}
};
_setPointTypes = (): void => {
this.originType =
this._geographyRequest.waypoints[0].type ?? PointType.OTHER;
this.destinationType =
this._geographyRequest.waypoints[
this._geographyRequest.waypoints.length - 1
].type ?? PointType.OTHER;
};
_isValidPoint = (point: Point): boolean =>
this._isValidLongitude(point.lon) && this._isValidLatitude(point.lat);
@ -61,4 +164,24 @@ export class Geography {
_isValidLatitude = (latitude: number): boolean =>
latitude >= -90 && latitude <= 90;
_createWaypoints = (points: Array<Point>, role: Role): Array<Waypoint> => {
return points.map((point, index) => {
const waypoint = new Waypoint(point);
if (index == 0) {
waypoint.addActor(new Actor(this._person, role, Step.START));
} else if (index == points.length - 1) {
waypoint.addActor(new Actor(this._person, role, Step.FINISH));
} else {
waypoint.addActor(new Actor(this._person, role, Step.INTERMEDIATE));
}
return waypoint;
});
};
}
export enum RouteKey {
COMMON = 'common',
DRIVER = 'driver',
PASSENGER = 'passenger',
}

View File

@ -1,6 +1,6 @@
import { Route } from './route';
export class NamedRoute {
export type NamedRoute = {
key: string;
route: Route;
}
};

View File

@ -17,7 +17,7 @@ export class Person {
this._defaultMarginDuration = defaultMarginDuration;
}
init() {
init = (): void => {
this.setIdentifier(
this._personRequest.identifier ?? this._defaultIdentifier,
);
@ -30,13 +30,13 @@ export class Person {
this._defaultMarginDuration,
this._defaultMarginDuration,
]);
}
};
setIdentifier(identifier: number) {
setIdentifier = (identifier: number): void => {
this.identifier = identifier;
}
};
setMarginDurations(marginDurations: Array<number>) {
setMarginDurations = (marginDurations: Array<number>): void => {
this.marginDurations = marginDurations;
}
};
}

View File

@ -1,4 +1,5 @@
import { IGeodesic } from '../interfaces/geodesic.interface';
import { Point } from '../types/point.type';
import { SpacetimePoint } from './spacetime-point';
import { Waypoint } from './waypoint';
@ -9,7 +10,7 @@ export class Route {
backAzimuth: number;
distanceAzimuth: number;
waypoints: Array<Waypoint>;
points: Array<Array<number>>;
points: Array<Point>;
spacetimePoints: Array<SpacetimePoint>;
_geodesic: IGeodesic;
@ -25,31 +26,31 @@ export class Route {
this._geodesic = geodesic;
}
setWaypoints(waypoints: Array<Waypoint>): void {
setWaypoints = (waypoints: Array<Waypoint>): void => {
this.waypoints = waypoints;
this._setAzimuth(waypoints.map((waypoint) => waypoint.point));
}
};
setPoints(points: Array<Array<number>>): void {
setPoints = (points: Array<Point>): void => {
this.points = points;
this._setAzimuth(points);
}
};
setSpacetimePoints(spacetimePoints: Array<SpacetimePoint>): void {
setSpacetimePoints = (spacetimePoints: Array<SpacetimePoint>): void => {
this.spacetimePoints = spacetimePoints;
}
};
_setAzimuth(points: Array<Array<number>>): void {
_setAzimuth = (points: Array<Point>): void => {
const inverse = this._geodesic.inverse(
points[0][0],
points[0][1],
points[points.length - 1][0],
points[points.length - 1][1],
points[0].lon,
points[0].lat,
points[points.length - 1].lon,
points[points.length - 1].lat,
);
this.fwdAzimuth =
inverse.azimuth >= 0 ? inverse.azimuth : 360 - Math.abs(inverse.azimuth);
this.backAzimuth =
this.fwdAzimuth > 180 ? this.fwdAzimuth - 180 : this.fwdAzimuth + 180;
this.distanceAzimuth = inverse.distance;
}
};
}

View File

@ -34,31 +34,31 @@ export class Time {
};
}
init() {
init = (): void => {
this._validateBaseDate();
this._validatePunctualRequest();
this._validateRecurrentRequest();
this._setPunctualRequest();
this._setRecurrentRequest();
this._setMargindurations();
}
};
_validateBaseDate() {
_validateBaseDate = (): void => {
if (!this._timeRequest.departure && !this._timeRequest.fromDate) {
throw new MatcherException(3, 'departure or fromDate is required');
}
}
};
_validatePunctualRequest() {
_validatePunctualRequest = (): void => {
if (this._timeRequest.departure) {
this.fromDate = this.toDate = new Date(this._timeRequest.departure);
if (!this._isDate(this.fromDate)) {
throw new MatcherException(3, 'Wrong departure date');
}
}
}
};
_validateRecurrentRequest() {
_validateRecurrentRequest = (): void => {
if (this._timeRequest.fromDate) {
this.fromDate = new Date(this._timeRequest.fromDate);
if (!this._isDate(this.fromDate)) {
@ -77,9 +77,9 @@ export class Time {
if (this._timeRequest.fromDate) {
this._validateSchedule();
}
}
};
_validateSchedule() {
_validateSchedule = (): void => {
if (!this._timeRequest.schedule) {
throw new MatcherException(3, 'Schedule is required');
}
@ -96,17 +96,17 @@ export class Time {
throw new MatcherException(3, `Wrong time for ${day} in schedule`);
}
});
}
};
_setPunctualRequest() {
_setPunctualRequest = (): void => {
if (this._timeRequest.departure) {
this.frequency = TimingFrequency.FREQUENCY_PUNCTUAL;
this.schedule[TimingDays[this.fromDate.getDay()]] =
this.fromDate.getHours() + ':' + this.fromDate.getMinutes();
}
}
};
_setRecurrentRequest() {
_setRecurrentRequest = (): void => {
if (this._timeRequest.fromDate) {
this.frequency = TimingFrequency.FREQUENCY_RECURRENT;
if (!this.toDate) {
@ -117,15 +117,15 @@ export class Time {
}
this._setSchedule();
}
}
};
_setSchedule() {
_setSchedule = (): void => {
Object.keys(this._timeRequest.schedule).map((day) => {
this.schedule[day] = this._timeRequest.schedule[day];
});
}
};
_setMargindurations() {
_setMargindurations = (): void => {
if (this._timeRequest.marginDuration) {
const duration = Math.abs(this._timeRequest.marginDuration);
this.marginDurations = {
@ -155,7 +155,7 @@ export class Time {
);
});
}
}
};
_isDate = (date: Date): boolean => {
return date instanceof Date && isFinite(+date);

View File

@ -1,6 +1,14 @@
import { Point } from '../types/point.type';
import { Actor } from './actor';
export class Waypoint {
point: Array<number>;
point: Point;
actors: Array<Actor>;
constructor(point: Point) {
this.point = point;
this.actors = [];
}
addActor = (actor: Actor) => this.actors.push(actor);
}

View File

@ -1,4 +1,7 @@
import { PointType } from './geography.enum';
export type Point = {
lon: number;
lat: number;
type?: PointType;
};

View File

@ -15,7 +15,7 @@ export class MatchUseCase {
@InjectMapper() private readonly _mapper: Mapper,
) {}
async execute(matchQuery: MatchQuery): Promise<ICollection<Match>> {
execute = async (matchQuery: MatchQuery): Promise<ICollection<Match>> => {
try {
// const paths = [];
// for (let i = 0; i < 1; i++) {
@ -73,5 +73,5 @@ export class MatchUseCase {
);
throw error;
}
}
};
}

View File

@ -11,7 +11,7 @@ export class MatchProfile extends AutomapperProfile {
}
override get profile() {
return (mapper) => {
return (mapper: Mapper) => {
createMap(mapper, Match, MatchPresenter);
};
}

View File

@ -39,60 +39,65 @@ export class MatchQuery {
this._setExclusions();
}
_setPerson() {
createRoutes = (): void => {
this.geography.createRoutes(this.roles, this.algorithmSettings.georouter);
};
_setPerson = (): void => {
this.person = new Person(
this._matchRequest,
this._defaultParams.DEFAULT_IDENTIFIER,
this._defaultParams.MARGIN_DURATION,
);
this.person.init();
}
};
_setRoles() {
_setRoles = (): void => {
this.roles = [];
if (this._matchRequest.driver) this.roles.push(Role.DRIVER);
if (this._matchRequest.passenger) this.roles.push(Role.PASSENGER);
if (this.roles.length == 0) this.roles.push(Role.PASSENGER);
}
};
_setTime() {
_setTime = (): void => {
this.time = new Time(
this._matchRequest,
this._defaultParams.MARGIN_DURATION,
this._defaultParams.VALIDITY_DURATION,
);
this.time.init();
}
};
_setGeography() {
_setGeography = (): void => {
this.geography = new Geography(
this._matchRequest,
this._defaultParams.DEFAULT_TIMEZONE,
this.person,
);
this.geography.init();
}
};
_setRequirement() {
_setRequirement = (): void => {
this.requirement = new Requirement(
this._matchRequest,
this._defaultParams.DEFAULT_SEATS,
);
}
};
_setAlgorithmSettings() {
_setAlgorithmSettings = (): void => {
this.algorithmSettings = new AlgorithmSettings(
this._matchRequest,
this._defaultParams.DEFAULT_ALGORITHM_SETTINGS,
this.time.frequency,
this._georouterCreator,
);
}
};
_setExclusions() {
_setExclusions = (): void => {
this.exclusions = [];
if (this._matchRequest.identifier)
this.exclusions.push(this._matchRequest.identifier);
if (this._matchRequest.exclusions)
this.exclusions.push(...this._matchRequest.exclusions);
}
};
}

View File

@ -1,4 +1,66 @@
import { Geography } from '../../../domain/entities/geography';
import { Person } from '../../../domain/entities/person';
import { Geography, RouteKey } from '../../../domain/entities/geography';
import { Role } from '../../../domain/types/role.enum';
import { NamedRoute } from '../../../domain/entities/named-route';
import { Route } from '../../../domain/entities/route';
import { IGeodesic } from '../../../domain/interfaces/geodesic.interface';
import { PointType } from '../../../domain/types/geography.enum';
const person: Person = new Person(
{
identifier: 1,
},
0,
900,
);
const mockGeodesic: IGeodesic = {
inverse: jest.fn().mockImplementation(() => ({
azimuth: 45,
distance: 50000,
})),
};
const mockGeorouter = {
route: jest
.fn()
.mockImplementationOnce(() => {
return [
<NamedRoute>{
key: RouteKey.COMMON,
route: new Route(mockGeodesic),
},
];
})
.mockImplementationOnce(() => {
return [
<NamedRoute>{
key: RouteKey.DRIVER,
route: new Route(mockGeodesic),
},
<NamedRoute>{
key: RouteKey.PASSENGER,
route: new Route(mockGeodesic),
},
];
})
.mockImplementationOnce(() => {
return [
<NamedRoute>{
key: RouteKey.DRIVER,
route: new Route(mockGeodesic),
},
];
})
.mockImplementationOnce(() => {
return [
<NamedRoute>{
key: RouteKey.PASSENGER,
route: new Route(mockGeodesic),
},
];
}),
};
describe('Geography entity', () => {
it('should be defined', () => {
@ -16,29 +78,35 @@ describe('Geography entity', () => {
],
},
'Europe/Paris',
person,
);
expect(geography).toBeDefined();
});
describe('init', () => {
it('should initialize a geography request', () => {
it('should initialize a geography request with point types', () => {
const geography = new Geography(
{
waypoints: [
{
lat: 49.440041,
lon: 1.093912,
type: PointType.LOCALITY,
},
{
lat: 50.630992,
lon: 3.045432,
type: PointType.LOCALITY,
},
],
},
'Europe/Paris',
person,
);
geography.init();
expect(geography.waypoints.length).toBe(2);
expect(geography._points.length).toBe(2);
expect(geography.originType).toBe(PointType.LOCALITY);
expect(geography.destinationType).toBe(PointType.LOCALITY);
});
it('should throw an exception if waypoints are empty', () => {
const geography = new Geography(
@ -46,6 +114,7 @@ describe('Geography entity', () => {
waypoints: [],
},
'Europe/Paris',
person,
);
expect(() => geography.init()).toThrow();
});
@ -60,6 +129,7 @@ describe('Geography entity', () => {
],
},
'Europe/Paris',
person,
);
expect(() => geography.init()).toThrow();
});
@ -78,6 +148,7 @@ describe('Geography entity', () => {
],
},
'Europe/Paris',
person,
);
expect(() => geography.init()).toThrow();
});
@ -96,8 +167,113 @@ describe('Geography entity', () => {
],
},
'Europe/Paris',
person,
);
expect(() => geography.init()).toThrow();
});
});
describe('create route', () => {
it('should create routes as driver and passenger', async () => {
const geography = new Geography(
{
waypoints: [
{
lat: 49.440041,
lon: 1.093912,
},
{
lat: 50.630992,
lon: 3.045432,
},
],
},
'Europe/Paris',
person,
);
geography.init();
await geography.createRoutes(
[Role.DRIVER, Role.PASSENGER],
mockGeorouter,
);
expect(geography.driverRoute.waypoints.length).toBe(2);
expect(geography.passengerRoute.waypoints.length).toBe(2);
});
it('should create routes as driver and passenger with 3 waypoints', async () => {
const geography = new Geography(
{
waypoints: [
{
lat: 49.440041,
lon: 1.093912,
},
{
lat: 49.781215,
lon: 2.198475,
},
{
lat: 50.630992,
lon: 3.045432,
},
],
},
'Europe/Paris',
person,
);
geography.init();
await geography.createRoutes(
[Role.DRIVER, Role.PASSENGER],
mockGeorouter,
);
expect(geography.driverRoute.waypoints.length).toBe(3);
expect(geography.passengerRoute.waypoints.length).toBe(2);
});
it('should create routes as driver', async () => {
const geography = new Geography(
{
waypoints: [
{
lat: 49.440041,
lon: 1.093912,
},
{
lat: 50.630992,
lon: 3.045432,
},
],
},
'Europe/Paris',
person,
);
geography.init();
await geography.createRoutes([Role.DRIVER], mockGeorouter);
expect(geography.driverRoute.waypoints.length).toBe(2);
expect(geography.passengerRoute).toBeUndefined();
});
it('should create routes as passenger', async () => {
const geography = new Geography(
{
waypoints: [
{
lat: 49.440041,
lon: 1.093912,
},
{
lat: 50.630992,
lon: 3.045432,
},
],
},
'Europe/Paris',
person,
);
geography.init();
await geography.createRoutes([Role.PASSENGER], mockGeorouter);
expect(geography.passengerRoute.waypoints.length).toBe(2);
expect(geography.driverRoute).toBeUndefined();
});
});
});

View File

@ -24,10 +24,14 @@ describe('Route entity', () => {
});
it('should set waypoints and geodesic values for a route', () => {
const route = new Route(mockGeodesic);
const waypoint1: Waypoint = new Waypoint();
waypoint1.point = [0, 0];
const waypoint2: Waypoint = new Waypoint();
waypoint2.point = [10, 10];
const waypoint1: Waypoint = new Waypoint({
lon: 0,
lat: 0,
});
const waypoint2: Waypoint = new Waypoint({
lon: 10,
lat: 10,
});
route.setWaypoints([waypoint1, waypoint2]);
expect(route.waypoints.length).toBe(2);
expect(route.fwdAzimuth).toBe(45);
@ -37,8 +41,14 @@ describe('Route entity', () => {
it('should set points and geodesic values for a route', () => {
const route = new Route(mockGeodesic);
route.setPoints([
[10, 10],
[20, 20],
{
lon: 10,
lat: 10,
},
{
lon: 20,
lat: 20,
},
]);
expect(route.points.length).toBe(2);
expect(route.fwdAzimuth).toBe(315);