tests and refactor for ad geography
This commit is contained in:
parent
e950efe221
commit
17acaa449c
|
@ -12,7 +12,7 @@ import {
|
||||||
IsString,
|
IsString,
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
import { Frequency } from '../types/frequency.enum';
|
import { Frequency } from '../types/frequency.enum';
|
||||||
import { Coordinates } from '../../../geography/domain/entities/coordinates';
|
import { Coordinate } from '../../../geography/domain/entities/coordinate';
|
||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
import { HasTruthyWith } from './has-truthy-with.validator';
|
import { HasTruthyWith } from './has-truthy-with.validator';
|
||||||
|
|
||||||
|
@ -115,11 +115,11 @@ export class CreateAdRequest {
|
||||||
@AutoMap()
|
@AutoMap()
|
||||||
sunMargin: number;
|
sunMargin: number;
|
||||||
|
|
||||||
@Type(() => Coordinates)
|
@Type(() => Coordinate)
|
||||||
@IsArray()
|
@IsArray()
|
||||||
@ArrayMinSize(2)
|
@ArrayMinSize(2)
|
||||||
@AutoMap(() => [Coordinates])
|
@AutoMap(() => [Coordinate])
|
||||||
waypoints: Coordinates[];
|
waypoints: Coordinate[];
|
||||||
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@AutoMap()
|
@AutoMap()
|
||||||
|
|
|
@ -1,24 +1,17 @@
|
||||||
import { Coordinates } from '../../../geography/domain/entities/coordinates';
|
import { Coordinate } from '../../../geography/domain/entities/coordinate';
|
||||||
import { Route } from '../../../geography/domain/entities/route';
|
import { Route } from '../../../geography/domain/entities/route';
|
||||||
import { IFindTimezone } from '../../../geography/domain/interfaces/timezone-finder.interface';
|
|
||||||
import { Role } from '../types/role.enum';
|
import { Role } from '../types/role.enum';
|
||||||
import { IGeorouter } from '../../../geography/domain/interfaces/georouter.interface';
|
import { IGeorouter } from '../../../geography/domain/interfaces/georouter.interface';
|
||||||
import { Path } from '../../../geography/domain/types/path.type';
|
import { Path } from '../../../geography/domain/types/path.type';
|
||||||
import { Timezoner } from '../../../geography/domain/types/timezoner';
|
|
||||||
import { GeorouterSettings } from '../../../geography/domain/types/georouter-settings.type';
|
import { GeorouterSettings } from '../../../geography/domain/types/georouter-settings.type';
|
||||||
|
|
||||||
export class Geography {
|
export class Geography {
|
||||||
private points: Coordinates[];
|
private coordinates: Coordinate[];
|
||||||
timezones: string[];
|
|
||||||
driverRoute: Route;
|
driverRoute: Route;
|
||||||
passengerRoute: Route;
|
passengerRoute: Route;
|
||||||
timezoneFinder: IFindTimezone;
|
|
||||||
|
|
||||||
constructor(points: Coordinates[], timezoner: Timezoner) {
|
constructor(coordinates: Coordinate[]) {
|
||||||
this.points = points;
|
this.coordinates = coordinates;
|
||||||
this.timezones = [timezoner.timezone];
|
|
||||||
this.timezoneFinder = timezoner.finder;
|
|
||||||
this.setTimezones();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createRoutes = async (
|
createRoutes = async (
|
||||||
|
@ -26,39 +19,7 @@ export class Geography {
|
||||||
georouter: IGeorouter,
|
georouter: IGeorouter,
|
||||||
settings: GeorouterSettings,
|
settings: GeorouterSettings,
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
const paths: Path[] = [];
|
const paths: Path[] = this.getPaths(roles);
|
||||||
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,
|
|
||||||
};
|
|
||||||
paths.push(commonPath);
|
|
||||||
} else {
|
|
||||||
const driverPath: Path = {
|
|
||||||
key: RouteKey.DRIVER,
|
|
||||||
points: this.points,
|
|
||||||
};
|
|
||||||
const passengerPath: Path = {
|
|
||||||
key: RouteKey.PASSENGER,
|
|
||||||
points: [this.points[0], this.points[this.points.length - 1]],
|
|
||||||
};
|
|
||||||
paths.push(driverPath, passengerPath);
|
|
||||||
}
|
|
||||||
} else if (roles.includes(Role.DRIVER)) {
|
|
||||||
const driverPath: Path = {
|
|
||||||
key: RouteKey.DRIVER,
|
|
||||||
points: this.points,
|
|
||||||
};
|
|
||||||
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]],
|
|
||||||
};
|
|
||||||
paths.push(passengerPath);
|
|
||||||
}
|
|
||||||
const routes = await georouter.route(paths, settings);
|
const routes = await georouter.route(paths, settings);
|
||||||
if (routes.some((route) => route.key == RouteKey.COMMON)) {
|
if (routes.some((route) => route.key == RouteKey.COMMON)) {
|
||||||
this.driverRoute = routes.find(
|
this.driverRoute = routes.find(
|
||||||
|
@ -81,11 +42,46 @@ export class Geography {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private setTimezones = (): void => {
|
private getPaths = (roles: Role[]): Path[] => {
|
||||||
this.timezones = this.timezoneFinder.timezones(
|
const paths: Path[] = [];
|
||||||
this.points[0].lat,
|
if (roles.includes(Role.DRIVER) && roles.includes(Role.PASSENGER)) {
|
||||||
this.points[0].lon,
|
if (this.coordinates.length == 2) {
|
||||||
);
|
// 2 points => same route for driver and passenger
|
||||||
|
const commonPath: Path = {
|
||||||
|
key: RouteKey.COMMON,
|
||||||
|
points: this.coordinates,
|
||||||
|
};
|
||||||
|
paths.push(commonPath);
|
||||||
|
} else {
|
||||||
|
const driverPath: Path = this.createDriverPath();
|
||||||
|
const passengerPath: Path = this.createPassengerPath();
|
||||||
|
paths.push(driverPath, passengerPath);
|
||||||
|
}
|
||||||
|
} else if (roles.includes(Role.DRIVER)) {
|
||||||
|
const driverPath: Path = this.createDriverPath();
|
||||||
|
paths.push(driverPath);
|
||||||
|
} else if (roles.includes(Role.PASSENGER)) {
|
||||||
|
const passengerPath: Path = this.createPassengerPath();
|
||||||
|
paths.push(passengerPath);
|
||||||
|
}
|
||||||
|
return paths;
|
||||||
|
};
|
||||||
|
|
||||||
|
private createDriverPath = (): Path => {
|
||||||
|
return {
|
||||||
|
key: RouteKey.DRIVER,
|
||||||
|
points: this.coordinates,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
private createPassengerPath = (): Path => {
|
||||||
|
return {
|
||||||
|
key: RouteKey.PASSENGER,
|
||||||
|
points: [
|
||||||
|
this.coordinates[0],
|
||||||
|
this.coordinates[this.coordinates.length - 1],
|
||||||
|
],
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { Role } from '../types/role.enum';
|
||||||
import { Geography } from '../entities/geography';
|
import { Geography } from '../entities/geography';
|
||||||
import { IEncodeDirection } from '../../../geography/domain/interfaces/direction-encoder.interface';
|
import { IEncodeDirection } from '../../../geography/domain/interfaces/direction-encoder.interface';
|
||||||
import { TimeConverter } from '../entities/time-converter';
|
import { TimeConverter } from '../entities/time-converter';
|
||||||
|
import { Coordinate } from 'src/modules/geography/domain/entities/coordinate';
|
||||||
|
|
||||||
@CommandHandler(CreateAdCommand)
|
@CommandHandler(CreateAdCommand)
|
||||||
export class CreateAdUseCase {
|
export class CreateAdUseCase {
|
||||||
|
@ -38,7 +39,6 @@ export class CreateAdUseCase {
|
||||||
private readonly directionEncoder: IEncodeDirection,
|
private readonly directionEncoder: IEncodeDirection,
|
||||||
) {
|
) {
|
||||||
this.defaultParams = defaultParamsProvider.getParams();
|
this.defaultParams = defaultParamsProvider.getParams();
|
||||||
this.timezone = this.defaultParams.DEFAULT_TIMEZONE;
|
|
||||||
this.georouter = georouterCreator.create(
|
this.georouter = georouterCreator.create(
|
||||||
this.defaultParams.GEOROUTER_TYPE,
|
this.defaultParams.GEOROUTER_TYPE,
|
||||||
this.defaultParams.GEOROUTER_URL,
|
this.defaultParams.GEOROUTER_URL,
|
||||||
|
@ -48,8 +48,9 @@ export class CreateAdUseCase {
|
||||||
async execute(command: CreateAdCommand): Promise<Ad> {
|
async execute(command: CreateAdCommand): Promise<Ad> {
|
||||||
try {
|
try {
|
||||||
this.ad = this.mapper.map(command.createAdRequest, CreateAdRequest, Ad);
|
this.ad = this.mapper.map(command.createAdRequest, CreateAdRequest, Ad);
|
||||||
|
this.setTimezone(command.createAdRequest.waypoints);
|
||||||
|
this.setGeography(command.createAdRequest.waypoints);
|
||||||
this.setRoles(command.createAdRequest);
|
this.setRoles(command.createAdRequest);
|
||||||
this.setGeography(command.createAdRequest);
|
|
||||||
await this.geography.createRoutes(this.roles, this.georouter, {
|
await this.geography.createRoutes(this.roles, this.georouter, {
|
||||||
withDistance: false,
|
withDistance: false,
|
||||||
withPoints: true,
|
withPoints: true,
|
||||||
|
@ -112,18 +113,24 @@ export class CreateAdUseCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setTimezone = (coordinates: Coordinate[]): void => {
|
||||||
|
this.timezone = this.defaultParams.DEFAULT_TIMEZONE;
|
||||||
|
try {
|
||||||
|
const timezones = this.timezoneFinder.timezones(
|
||||||
|
coordinates[0].lat,
|
||||||
|
coordinates[0].lon,
|
||||||
|
);
|
||||||
|
if (timezones.length > 0) this.timezone = timezones[0];
|
||||||
|
} catch (e) {}
|
||||||
|
};
|
||||||
|
|
||||||
private setRoles = (createAdRequest: CreateAdRequest): void => {
|
private setRoles = (createAdRequest: CreateAdRequest): void => {
|
||||||
this.roles = [];
|
this.roles = [];
|
||||||
if (createAdRequest.driver) this.roles.push(Role.DRIVER);
|
if (createAdRequest.driver) this.roles.push(Role.DRIVER);
|
||||||
if (createAdRequest.passenger) this.roles.push(Role.PASSENGER);
|
if (createAdRequest.passenger) this.roles.push(Role.PASSENGER);
|
||||||
};
|
};
|
||||||
|
|
||||||
private setGeography = (createAdRequest: CreateAdRequest): void => {
|
private setGeography = (coordinates: Coordinate[]): void => {
|
||||||
this.geography = new Geography(createAdRequest.waypoints, {
|
this.geography = new Geography(coordinates);
|
||||||
timezone: this.defaultParams.DEFAULT_TIMEZONE,
|
|
||||||
finder: this.timezoneFinder,
|
|
||||||
});
|
|
||||||
if (this.geography.timezones.length > 0)
|
|
||||||
this.timezone = this.geography.timezones[0];
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,138 @@
|
||||||
|
import { Role } from '../../../domain/types/role.enum';
|
||||||
|
import { Geography } from '../../../domain/entities/geography';
|
||||||
|
import { Coordinate } from '../../../../geography/domain/entities/coordinate';
|
||||||
|
import { IGeorouter } from '../../../../geography/domain/interfaces/georouter.interface';
|
||||||
|
import { GeorouterSettings } from '../../../../geography/domain/types/georouter-settings.type';
|
||||||
|
import { Route } from '../../../../geography/domain/entities/route';
|
||||||
|
import { IGeodesic } from '../../../../geography/domain/interfaces/geodesic.interface';
|
||||||
|
|
||||||
|
const simpleCoordinates: Coordinate[] = [
|
||||||
|
{
|
||||||
|
lon: 6,
|
||||||
|
lat: 47,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lon: 6.1,
|
||||||
|
lat: 47.1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const complexCoordinates: Coordinate[] = [
|
||||||
|
{
|
||||||
|
lon: 6,
|
||||||
|
lat: 47,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lon: 6.1,
|
||||||
|
lat: 47.1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lon: 6.2,
|
||||||
|
lat: 47.2,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const mockGeodesic: IGeodesic = {
|
||||||
|
inverse: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const driverRoute: Route = new Route(mockGeodesic);
|
||||||
|
driverRoute.distance = 25000;
|
||||||
|
|
||||||
|
const commonRoute: Route = new Route(mockGeodesic);
|
||||||
|
commonRoute.distance = 20000;
|
||||||
|
|
||||||
|
const mockGeorouter: IGeorouter = {
|
||||||
|
route: jest
|
||||||
|
.fn()
|
||||||
|
.mockResolvedValueOnce([
|
||||||
|
{
|
||||||
|
key: 'driver',
|
||||||
|
route: driverRoute,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.mockResolvedValueOnce([
|
||||||
|
{
|
||||||
|
key: 'passenger',
|
||||||
|
route: commonRoute,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.mockResolvedValueOnce([
|
||||||
|
{
|
||||||
|
key: 'common',
|
||||||
|
route: commonRoute,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.mockResolvedValueOnce([
|
||||||
|
{
|
||||||
|
key: 'driver',
|
||||||
|
route: driverRoute,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'passenger',
|
||||||
|
route: commonRoute,
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
|
||||||
|
const georouterSettings: GeorouterSettings = {
|
||||||
|
withDistance: false,
|
||||||
|
withPoints: true,
|
||||||
|
withTime: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Geography entity', () => {
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(new Geography(simpleCoordinates)).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a route as driver', async () => {
|
||||||
|
const geography = new Geography(complexCoordinates);
|
||||||
|
await geography.createRoutes(
|
||||||
|
[Role.DRIVER],
|
||||||
|
mockGeorouter,
|
||||||
|
georouterSettings,
|
||||||
|
);
|
||||||
|
expect(geography.driverRoute).toBeDefined();
|
||||||
|
expect(geography.passengerRoute).toBeUndefined();
|
||||||
|
expect(geography.driverRoute.distance).toBe(25000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a route as passenger', async () => {
|
||||||
|
const geography = new Geography(simpleCoordinates);
|
||||||
|
await geography.createRoutes(
|
||||||
|
[Role.PASSENGER],
|
||||||
|
mockGeorouter,
|
||||||
|
georouterSettings,
|
||||||
|
);
|
||||||
|
expect(geography.driverRoute).toBeUndefined();
|
||||||
|
expect(geography.passengerRoute).toBeDefined();
|
||||||
|
expect(geography.passengerRoute.distance).toBe(20000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create routes as driver and passenger with simple coordinates', async () => {
|
||||||
|
const geography = new Geography(simpleCoordinates);
|
||||||
|
await geography.createRoutes(
|
||||||
|
[Role.DRIVER, Role.PASSENGER],
|
||||||
|
mockGeorouter,
|
||||||
|
georouterSettings,
|
||||||
|
);
|
||||||
|
expect(geography.driverRoute).toBeDefined();
|
||||||
|
expect(geography.passengerRoute).toBeDefined();
|
||||||
|
expect(geography.driverRoute.distance).toBe(20000);
|
||||||
|
expect(geography.passengerRoute.distance).toBe(20000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create routes as driver and passenger with complex coordinates', async () => {
|
||||||
|
const geography = new Geography(complexCoordinates);
|
||||||
|
await geography.createRoutes(
|
||||||
|
[Role.DRIVER, Role.PASSENGER],
|
||||||
|
mockGeorouter,
|
||||||
|
georouterSettings,
|
||||||
|
);
|
||||||
|
expect(geography.driverRoute).toBeDefined();
|
||||||
|
expect(geography.passengerRoute).toBeDefined();
|
||||||
|
expect(geography.driverRoute.distance).toBe(25000);
|
||||||
|
expect(geography.passengerRoute.distance).toBe(20000);
|
||||||
|
});
|
||||||
|
});
|
|
@ -15,7 +15,7 @@ describe('TimeConverter', () => {
|
||||||
).toBe(6);
|
).toBe(6);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return undefined when trying to convert a Europe/Paris datetime to utc datetime without a valid date, time or timezone', () => {
|
it('should return undefined when trying to convert a Europe/Paris datetime to utc datetime without a valid date', () => {
|
||||||
expect(
|
expect(
|
||||||
TimeConverter.toUtcDatetime(undefined, '07:00', 'Europe/Paris'),
|
TimeConverter.toUtcDatetime(undefined, '07:00', 'Europe/Paris'),
|
||||||
).toBeUndefined();
|
).toBeUndefined();
|
||||||
|
@ -26,6 +26,9 @@ describe('TimeConverter', () => {
|
||||||
'Europe/Paris',
|
'Europe/Paris',
|
||||||
),
|
),
|
||||||
).toBeUndefined();
|
).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return undefined when trying to convert a Europe/Paris datetime to utc datetime without a valid time', () => {
|
||||||
expect(
|
expect(
|
||||||
TimeConverter.toUtcDatetime(
|
TimeConverter.toUtcDatetime(
|
||||||
new Date('2023-05-01'),
|
new Date('2023-05-01'),
|
||||||
|
@ -36,9 +39,12 @@ describe('TimeConverter', () => {
|
||||||
expect(
|
expect(
|
||||||
TimeConverter.toUtcDatetime(new Date('2023-05-01'), 'a', 'Europe/Paris'),
|
TimeConverter.toUtcDatetime(new Date('2023-05-01'), 'a', 'Europe/Paris'),
|
||||||
).toBeUndefined();
|
).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return undefined when trying to convert a datetime to utc datetime without a valid timezone', () => {
|
||||||
expect(
|
expect(
|
||||||
TimeConverter.toUtcDatetime(
|
TimeConverter.toUtcDatetime(
|
||||||
new Date('2023-13-01'),
|
new Date('2023-12-01'),
|
||||||
'07:00',
|
'07:00',
|
||||||
'OlympusMons/Mars',
|
'OlympusMons/Mars',
|
||||||
),
|
),
|
||||||
|
|
|
@ -205,7 +205,6 @@ export abstract class PrismaRepository<T> implements IRepository<T> {
|
||||||
const command = `INSERT INTO ${this.model} ("${Object.keys(fields).join(
|
const command = `INSERT INTO ${this.model} ("${Object.keys(fields).join(
|
||||||
'","',
|
'","',
|
||||||
)}") VALUES (${Object.values(fields).join(',')})`;
|
)}") VALUES (${Object.values(fields).join(',')})`;
|
||||||
console.log(command);
|
|
||||||
return await this._prisma.$executeRawUnsafe(command);
|
return await this._prisma.$executeRawUnsafe(command);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Prisma.PrismaClientKnownRequestError) {
|
if (e instanceof Prisma.PrismaClientKnownRequestError) {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { Coordinates } from '../../domain/entities/coordinates';
|
import { Coordinate } from '../../domain/entities/coordinate';
|
||||||
import { IEncodeDirection } from '../../domain/interfaces/direction-encoder.interface';
|
import { IEncodeDirection } from '../../domain/interfaces/direction-encoder.interface';
|
||||||
|
|
||||||
export class PostgresDirectionEncoder implements IEncodeDirection {
|
export class PostgresDirectionEncoder implements IEncodeDirection {
|
||||||
encode = (coordinates: Coordinates[]): string =>
|
encode = (coordinates: Coordinate[]): string =>
|
||||||
[
|
[
|
||||||
"'LINESTRING(",
|
"'LINESTRING(",
|
||||||
coordinates.map((point) => [point.lon, point.lat].join(' ')).join(),
|
coordinates.map((point) => [point.lon, point.lat].join(' ')).join(),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { AutoMap } from '@automapper/classes';
|
import { AutoMap } from '@automapper/classes';
|
||||||
import { IsLatitude, IsLongitude, IsNumber } from 'class-validator';
|
import { IsLatitude, IsLongitude, IsNumber } from 'class-validator';
|
||||||
|
|
||||||
export class Coordinates {
|
export class Coordinate {
|
||||||
constructor(lon: number, lat: number) {
|
constructor(lon: number, lat: number) {
|
||||||
this.lon = lon;
|
this.lon = lon;
|
||||||
this.lat = lat;
|
this.lat = lat;
|
|
@ -1,12 +1,12 @@
|
||||||
import { Coordinates } from './coordinates';
|
import { Coordinate } from './coordinate';
|
||||||
|
|
||||||
export class SpacetimePoint {
|
export class SpacetimePoint {
|
||||||
coordinates: Coordinates;
|
coordinate: Coordinate;
|
||||||
duration: number;
|
duration: number;
|
||||||
distance: number;
|
distance: number;
|
||||||
|
|
||||||
constructor(coordinates: Coordinates, duration: number, distance: number) {
|
constructor(coordinate: Coordinate, duration: number, distance: number) {
|
||||||
this.coordinates = coordinates;
|
this.coordinate = coordinate;
|
||||||
this.duration = duration;
|
this.duration = duration;
|
||||||
this.distance = distance;
|
this.distance = distance;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Coordinates } from '../entities/coordinates';
|
import { Coordinate } from '../entities/coordinate';
|
||||||
|
|
||||||
export interface IEncodeDirection {
|
export interface IEncodeDirection {
|
||||||
encode(coordinates: Coordinates[]): string;
|
encode(coordinates: Coordinate[]): string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { PointType } from './point-type.enum';
|
import { PointType } from './point-type.enum';
|
||||||
import { Coordinates } from '../entities/coordinates';
|
import { Coordinate } from '../entities/coordinate';
|
||||||
|
|
||||||
export type Point = Coordinates & {
|
export type Point = Coordinate & {
|
||||||
type?: PointType;
|
type?: PointType;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { Coordinate } from '../../domain/entities/coordinate';
|
||||||
|
|
||||||
|
describe('Coordinate entity', () => {
|
||||||
|
it('should be defined', () => {
|
||||||
|
const coordinate: Coordinate = new Coordinate(6, 47);
|
||||||
|
expect(coordinate).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { PostgresDirectionEncoder } from '../../adapters/secondaries/postgres-direction-encoder';
|
||||||
|
import { Coordinate } from '../../domain/entities/coordinate';
|
||||||
|
|
||||||
|
describe('Postgres direction encoder', () => {
|
||||||
|
it('should be defined', () => {
|
||||||
|
const postgresDirectionEncoder: PostgresDirectionEncoder =
|
||||||
|
new PostgresDirectionEncoder();
|
||||||
|
expect(postgresDirectionEncoder).toBeDefined();
|
||||||
|
});
|
||||||
|
it('should encode coordinates to a postgres direction', () => {
|
||||||
|
const postgresDirectionEncoder: PostgresDirectionEncoder =
|
||||||
|
new PostgresDirectionEncoder();
|
||||||
|
const coordinates: Coordinate[] = [
|
||||||
|
{
|
||||||
|
lon: 6,
|
||||||
|
lat: 47,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lon: 6.1,
|
||||||
|
lat: 47.1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lon: 6.2,
|
||||||
|
lat: 47.2,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const direction = postgresDirectionEncoder.encode(coordinates);
|
||||||
|
expect(direction).toBe("'LINESTRING(6 47,6.1 47.1,6.2 47.2)'");
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,12 +1,12 @@
|
||||||
import { Coordinates } from '../../../../geography/domain/entities/coordinates';
|
import { Coordinate } from '../../../../geography/domain/entities/coordinate';
|
||||||
|
|
||||||
export class SpacetimePoint {
|
export class SpacetimePoint {
|
||||||
coordinates: Coordinates;
|
coordinate: Coordinate;
|
||||||
duration: number;
|
duration: number;
|
||||||
distance: number;
|
distance: number;
|
||||||
|
|
||||||
constructor(coordinates: Coordinates, duration: number, distance: number) {
|
constructor(coordinate: Coordinate, duration: number, distance: number) {
|
||||||
this.coordinates = coordinates;
|
this.coordinate = coordinate;
|
||||||
this.duration = duration;
|
this.duration = duration;
|
||||||
this.distance = distance;
|
this.distance = distance;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue