geography entity with tests
This commit is contained in:
parent
ed99b442eb
commit
6dd4837c89
|
@ -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'),
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Route } from './route';
|
||||
|
||||
export class NamedRoute {
|
||||
export type NamedRoute = {
|
||||
key: string;
|
||||
route: Route;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import { PointType } from './geography.enum';
|
||||
|
||||
export type Point = {
|
||||
lon: number;
|
||||
lat: number;
|
||||
type?: PointType;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ export class MatchProfile extends AutomapperProfile {
|
|||
}
|
||||
|
||||
override get profile() {
|
||||
return (mapper) => {
|
||||
return (mapper: Mapper) => {
|
||||
createMap(mapper, Match, MatchPresenter);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue