From 56e5cec0ce7a4e4b9f6d7839b4d1cf0f3fe86f9e Mon Sep 17 00:00:00 2001 From: nils Date: Thu, 26 Sep 2024 16:50:25 +0200 Subject: [PATCH 01/12] wip --- src/lib/api.ts | 4 +- src/lib/compositionTypes.ts | 19 + src/lib/constants.ts | 4 + src/lib/interval.ts | 38 ++ src/lib/time_utils.ts | 4 + src/lib/types.ts | 2 + src/routes/api/booking/+server.ts | 11 +- .../api/whitelist/capacitySimulation.test.ts | 7 +- .../evaluateSingleInsertions.test.ts | 114 ++++++ src/routes/api/whitelist/helpers.test.ts | 0 src/routes/api/whitelist/insertions.ts | 379 ++++++++++++++++++ src/routes/api/whitelist/routing.ts | 117 ++++++ src/routes/api/whitelist/utils.ts | 95 +++++ 13 files changed, 788 insertions(+), 6 deletions(-) create mode 100644 src/routes/api/whitelist/evaluateSingleInsertions.test.ts create mode 100644 src/routes/api/whitelist/helpers.test.ts create mode 100644 src/routes/api/whitelist/insertions.ts create mode 100644 src/routes/api/whitelist/routing.ts create mode 100644 src/routes/api/whitelist/utils.ts diff --git a/src/lib/api.ts b/src/lib/api.ts index ded802f0..86280791 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -146,7 +146,7 @@ export enum Direction { Backward } -export type oneToManyResult = { +export type OneToManyResult = { duration: number; distance: number; }; @@ -155,7 +155,7 @@ export const oneToMany = async ( one: Coordinates, many: Coordinates[], direction: Direction -): Promise => { +): Promise => { const dir = direction == Direction.Forward ? 'Forward' : 'Backward'; const response = await fetch('https://europe.motis-project.de/', { headers: { diff --git a/src/lib/compositionTypes.ts b/src/lib/compositionTypes.ts index b2973f93..72a85907 100644 --- a/src/lib/compositionTypes.ts +++ b/src/lib/compositionTypes.ts @@ -2,6 +2,20 @@ import type { Capacities } from './capacities'; import type { Interval } from './interval'; import type { Coordinates } from './location'; +export type Company = { + id: number; + coordinates: Coordinates; + vehicles: Vehicle[]; + zoneId: number; +}; + +export type Vehicle = { + id: number; + capacities: Capacities; + events: Event[]; + availabilities: Interval[]; +}; + export type Event = { capacities: Capacities; is_pickup: boolean; @@ -9,4 +23,9 @@ export type Event = { id: number; coordinates: Coordinates; tourId: number; + arrival: Date; + departure: Date; + communicated: Date; + durationFromPrev: number; + durationToNext: number; }; diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 56c2190c..6d5a9078 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -6,3 +6,7 @@ export const MAX_TRAVEL_DURATION = hoursToMs(1); export const MAX_PASSENGER_WAITING_TIME_PICKUP = minutesToMs(10); export const MAX_PASSENGER_WAITING_TIME_DROPOFF = minutesToMs(10); export const SRID = 4326; +export const PASSENGER_CHANGE_TIME = minutesToMs(2); +export const TAXI_DRIVING_TIME_COST_FACTOR = 1; +export const TAXI_WAITING_TIME_COST_FACTOR = 0.2; +export const PASSENGER_TIME_COST_FACTOR = 1; diff --git a/src/lib/interval.ts b/src/lib/interval.ts index 596d7ddb..d49307e7 100644 --- a/src/lib/interval.ts +++ b/src/lib/interval.ts @@ -65,6 +65,13 @@ export class Interval { ); } + shrink(postponeStart: number, preponeEnd: number) { + return new Interval( + new Date(this.startTime.getTime() + postponeStart), + new Date(this.endTime.getTime() - preponeEnd) + ); + } + isMergeable(other: Interval): boolean { return this.overlaps(other) || this.touches(other); } @@ -91,4 +98,35 @@ export class Interval { merged.push(unmerged.pop()!); return merged; }; + + intersect(other: Interval): Interval | undefined { + if (this.overlaps(other)) { + return new Interval( + new Date(Math.max(this.startTime.getTime(), other.startTime.getTime())), + new Date(Math.min(this.endTime.getTime(), other.endTime.getTime())) + ); + } + return undefined; + } + + static intersect = (many: Interval[], one: Interval | undefined): Interval[] => { + if (one == undefined) { + return []; + } + const result: Interval[] = []; + for (let i = 0; i != many.length; ++i) { + if (one.startTime.getTime() > many[i].endTime.getTime()) { + break; + } + if (!many[i].overlaps(one)) { + continue; + } + result.push(many[i].intersect(one)!); + } + return result; + }; + + covers(time: Date): boolean { + return this.startTime <= time && this.endTime >= time; + } } diff --git a/src/lib/time_utils.ts b/src/lib/time_utils.ts index dc63eb0c..4834048d 100644 --- a/src/lib/time_utils.ts +++ b/src/lib/time_utils.ts @@ -9,3 +9,7 @@ export function minutesToMs(minutes: number) { export function hoursToMs(hours: number) { return hours * 3600000; } + +export function yearsToMs(years: number) { + return years * 365 * 3600000 * 24; +} diff --git a/src/lib/types.ts b/src/lib/types.ts index c1daf0e9..a7e9599f 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -137,6 +137,8 @@ export interface EventTable { tour: number; customer: string; request: number; + durationToNext: number; + durationFromPrev: number; } export type Event = Selectable; diff --git a/src/routes/api/booking/+server.ts b/src/routes/api/booking/+server.ts index 6232ec8d..66bf0906 100644 --- a/src/routes/api/booking/+server.ts +++ b/src/routes/api/booking/+server.ts @@ -9,6 +9,7 @@ import { MAX_TRAVEL_DURATION, MIN_PREP_MINUTES } from '$lib/constants.js'; import { sql } from 'kysely'; import { getFareEstimation } from './fare-estimation/fare_estimation.js'; import { covers } from '$lib/sqlHelpers.js'; +import type { RequestEvent } from './$types'; const startAndTargetShareZone = async (from: Coordinates, to: Coordinates) => { const zoneContainingStartAndDestination = await db @@ -18,7 +19,7 @@ const startAndTargetShareZone = async (from: Coordinates, to: Coordinates) => { return zoneContainingStartAndDestination != undefined; }; -export const POST = async (event) => { +export const POST = async (event: RequestEvent) => { const customer = event.locals.user; if (!customer) { return error(403); @@ -427,7 +428,9 @@ export const POST = async (event) => { address: startAddress.id, request: requestId!, tour: tourId!, - customer: customerId + customer: customerId, + durationFromPrev: 0, + durationToNext: 0 }, { is_pickup: false, @@ -438,7 +441,9 @@ export const POST = async (event) => { address: targetAddress.id, request: requestId!, tour: tourId!, - customer: customerId + customer: customerId, + durationFromPrev: 0, + durationToNext: 0 } ]) .execute(); diff --git a/src/routes/api/whitelist/capacitySimulation.test.ts b/src/routes/api/whitelist/capacitySimulation.test.ts index 396e7c22..040fc7d8 100644 --- a/src/routes/api/whitelist/capacitySimulation.test.ts +++ b/src/routes/api/whitelist/capacitySimulation.test.ts @@ -13,7 +13,12 @@ function createEventCapacitiesOnly(capacities: Capacities, is_pickup: boolean): time: new Interval(now, now), id: 1, coordinates: new Coordinates(1, 1), - tourId: 1 + tourId: 1, + arrival: new Date(), + departure: new Date(), + communicated: new Date(), + durationFromPrev: 0, + durationToNext: 0 }; } diff --git a/src/routes/api/whitelist/evaluateSingleInsertions.test.ts b/src/routes/api/whitelist/evaluateSingleInsertions.test.ts new file mode 100644 index 00000000..5910dd70 --- /dev/null +++ b/src/routes/api/whitelist/evaluateSingleInsertions.test.ts @@ -0,0 +1,114 @@ +import { describe, it, expect } from 'vitest'; +import { type Range } from './capacitySimulation'; +import { Coordinates } from '$lib/location'; +import { type RoutingResults } from './routing'; +import { computeTravelDurations } from './insertions'; +import type { Vehicle, Company, Event } from '$lib/compositionTypes'; +import { Interval } from '$lib/interval'; +import { hoursToMs } from '$lib/time_utils'; + +const BASE_MS = new Date(Date.now() + hoursToMs(5)).getTime(); + +const createDate = (h: number) => { + return new Date(BASE_MS + hoursToMs(h)); +}; + +const createCompany = (vehicles: Vehicle[], coordinates: Coordinates): Company => { + return { + id: 1, + coordinates: coordinates, + vehicles, + zoneId: 1 + }; +}; + +const createVehicle = (id: number, events: Event[]): Vehicle => { + return { + id, + capacities: { passengers: 0, bikes: 0, wheelchairs: 0, luggage: 0 }, + events, + availabilities: [] + }; +}; + +const createEvent = (coordinates: Coordinates): Event => { + return { + capacities: { passengers: 0, bikes: 0, wheelchairs: 0, luggage: 0 }, + is_pickup: true, + time: new Interval(createDate(4), createDate(5)), + id: 1, + coordinates, + tourId: 1, + communicated: createDate(6), + arrival: createDate(6), + departure: createDate(3), + durationFromPrev: 0, + durationToNext: 0 + }; +}; + +describe('compute Intervals for single insertions test', () => { + it('TODO', () => { + let eventLatLng = 70; + let companyLatLng = 5; + const companies = [ + createCompany( + [ + createVehicle(1, [ + createEvent(new Coordinates(eventLatLng, eventLatLng++)), + createEvent(new Coordinates(eventLatLng, eventLatLng++)), + createEvent(new Coordinates(eventLatLng, eventLatLng++)) + ]) + ], + new Coordinates(companyLatLng, companyLatLng++) + ) + ]; + const insertions = new Map(); + insertions.set(1, [{ earliestPickup: 0, latestDropoff: 3 }]); + const travelDurations = [50]; + const routingResults: RoutingResults = { + busStops: [ + { + fromCompany: [{ duration: 10, distance: 0 }], + toCompany: [{ duration: 10, distance: 0 }], + fromPrevEvent: [ + { duration: 10, distance: 0 }, + { duration: 10, distance: 0 }, + { duration: 10, distance: 0 } + ], + toNextEvent: [ + { duration: 10, distance: 0 }, + { duration: 10, distance: 0 }, + { duration: 10, distance: 0 } + ] + } + ], + userChosen: { + fromCompany: [{ duration: 10, distance: 0 }], + toCompany: [{ duration: 10, distance: 0 }], + fromPrevEvent: [ + { duration: 10, distance: 0 }, + { duration: 10, distance: 0 }, + { duration: 10, distance: 0 } + ], + toNextEvent: [ + { duration: 10, distance: 0 }, + { duration: 10, distance: 0 }, + { duration: 10, distance: 0 } + ] + } + }; + const busStopTimes = [[new Interval(createDate(5), createDate(6))]]; + const result = computeTravelDurations( + companies, + insertions, + routingResults, + travelDurations, + true, + busStopTimes, + [[true]] + ); + console.log(result); + expect(1).toBe(1); + }); +}); diff --git a/src/routes/api/whitelist/helpers.test.ts b/src/routes/api/whitelist/helpers.test.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/routes/api/whitelist/insertions.ts b/src/routes/api/whitelist/insertions.ts new file mode 100644 index 00000000..ff4fa33d --- /dev/null +++ b/src/routes/api/whitelist/insertions.ts @@ -0,0 +1,379 @@ +import type { Company, Event } from '$lib/compositionTypes'; +import { Interval } from '$lib/interval'; +import { ITERATE_INSERTIONS_MODE, iterateAllInsertions } from './utils'; +import { type RoutingResults } from './routing'; +import type { Range } from './capacitySimulation'; +import { minutesToMs } from '$lib/time_utils'; +import { + MIN_PREP_MINUTES, + PASSENGER_CHANGE_TIME, + PASSENGER_TIME_COST_FACTOR, + TAXI_DRIVING_TIME_COST_FACTOR, + TAXI_WAITING_TIME_COST_FACTOR +} from '$lib/constants'; + +enum InsertionType { + CONNECT, + APPEND, + PREPEND, + INSERT +} + +enum ToInsert { + PICKUP, + DROPOFF, + BOTH +} + +const cases = [ + InsertionType.CONNECT, + InsertionType.APPEND, + InsertionType.PREPEND, + InsertionType.INSERT +]; + +type Cost = { + taxiWaitingTime: number; + taxiDrivingDuration: number; + passengerDuration: number; + total: number; +}; + +type TimeCost = { + time: Date; + cost: Cost; +}; + +type Insertion = { + userChosen: TimeCost; + busStops: TimeCost[][]; + both: TimeCost[][]; + approachDuration: number | undefined; + returnDuration: number | undefined; + company: number; + vehicle: number; + tour1: number; + tour2: number; + event1: number; + event2: number; +}; + +type InsertionDurations = { + approach: number; + return: number; + pickupToDropoff: number; + fromPrev: number; + toNext: number; + fullWindow: number; +}; + +export function costFn( + taxiDrivingDuration: number, + taxiWaitingTime: number, + passengerDuration: number +) { + return ( + TAXI_DRIVING_TIME_COST_FACTOR * taxiDrivingDuration + + PASSENGER_TIME_COST_FACTOR * passengerDuration + + TAXI_WAITING_TIME_COST_FACTOR * taxiWaitingTime + ); +} + +export function evaluateInsertion( + type: InsertionType, + window: Interval, + durations: InsertionDurations, + toInsert: ToInsert, + currentBest: TimeCost | undefined, + busStopWindow: Interval | undefined, + startFixed: boolean +): TimeCost | undefined { + const fullDuration = durations.approach + durations.return + durations.pickupToDropoff; + let arrivalWindow = + window.getDurationMs() < fullDuration + ? undefined + : window.shrink(durations.approach, durations.return); + if (busStopWindow != undefined) { + arrivalWindow = arrivalWindow?.intersect(busStopWindow); + } + if (arrivalWindow == undefined) { + return undefined; + } + const cost = computeCosts(type, durations, toInsert); + if (currentBest == undefined || currentBest.cost.total <= cost.total) { + return undefined; + } + return { + time: startFixed ? arrivalWindow.startTime : arrivalWindow.endTime, + cost + }; +} + +export function computeCosts( + type: InsertionType, + durations: InsertionDurations, + toInsert: ToInsert +): Cost { + const taxiDrivingDuration = + durations.approach + + durations.return + + (toInsert == ToInsert.BOTH ? durations.pickupToDropoff : 0); + const taxiDrivingDurationRelative = + taxiDrivingDuration - + (function () { + switch (type) { + case InsertionType.APPEND: + return durations.fromPrev; + case InsertionType.PREPEND: + return durations.toNext; + case InsertionType.CONNECT: + return durations.toNext + durations.fromPrev; + case InsertionType.INSERT: + return durations.toNext + durations.fromPrev; + } + })(); + const taxiWaitingTime = + type == InsertionType.CONNECT || type == InsertionType.INSERT + ? durations.fullWindow - taxiDrivingDuration - PASSENGER_CHANGE_TIME - toInsert == + ToInsert.BOTH + ? PASSENGER_CHANGE_TIME + : 0 + : 0; + const passengerDuration = (function () { + switch (toInsert) { + case ToInsert.PICKUP: + console.assert(type != InsertionType.APPEND, 'Trying to Append a Pickup event.'); + return durations.return; + case ToInsert.DROPOFF: + console.assert(type != InsertionType.PREPEND, 'Trying to Prepend a Dropoff event.'); + return durations.approach; + case ToInsert.BOTH: + return durations.pickupToDropoff; + } + })(); + const total = costFn(taxiDrivingDuration, taxiWaitingTime, passengerDuration); + return { + taxiDrivingDuration: taxiDrivingDurationRelative, + taxiWaitingTime, + passengerDuration, + total + }; +} + +export function getBestTime( + approachDuration: number, + returnDuration: number, + window: Interval, + busStopTime: Interval, + startFixed: boolean +): Date | undefined { + let arrivalWindow = + window.getDurationMs() < approachDuration + returnDuration + ? undefined + : window.shrink(approachDuration, returnDuration); + arrivalWindow = arrivalWindow?.intersect(busStopTime); + return startFixed ? arrivalWindow?.startTime : arrivalWindow?.endTime; +} + +export function computeTravelDurations( + companies: Company[], + possibleInsertionsByVehicle: Map, + routingResults: RoutingResults, + travelDurations: number[], + startFixed: boolean, + busStopTimes: Interval[][], + busStopCompanyFilter: boolean[][] +): Insertion[][] { + const allInsertions = new Array(); + iterateAllInsertions( + ITERATE_INSERTIONS_MODE.SINGLE, + companies, + busStopCompanyFilter, + possibleInsertionsByVehicle, + (busStopIdx, companyIdx, prevEventIdx, nextEventIdx, vehicle, insertionIdx, _) => { + if (prevEventIdx == undefined) { + return; + } + if (nextEventIdx == undefined) { + return; + } + const prev: Event | undefined = + insertionIdx == 0 ? undefined : vehicle.events[insertionIdx - 1]; + const next: Event | undefined = + insertionIdx == vehicle.events.length ? undefined : vehicle.events[insertionIdx]; + cases.forEach((type) => { + if (type == (startFixed ? InsertionType.APPEND : InsertionType.PREPEND)) { + return; + } + if ( + prev == undefined || + next == undefined || + (prev.tourId == next.tourId) != (type == InsertionType.INSERT) + ) { + return; + } + const returnsToCompany = type === InsertionType.CONNECT || type === InsertionType.APPEND; + const comesFromCompany = type === InsertionType.CONNECT || type === InsertionType.PREPEND; + + const prevTime = comesFromCompany ? prev.arrival : prev.communicated; + if (prevTime < new Date(Date.now() + minutesToMs(MIN_PREP_MINUTES))) { + return; + } + const nextTime = returnsToCompany ? next.departure : next.communicated; + const fullWindowDuration = nextTime.getTime() - prevTime.getTime(); + let window: Interval | undefined = new Interval(prevTime, nextTime); + if (busStopIdx == undefined) { + // insert userChosen + if (type == (startFixed ? InsertionType.PREPEND : InsertionType.APPEND)) { + return; + } + const approachDuration = comesFromCompany + ? routingResults.userChosen.fromCompany[companyIdx].duration + : routingResults.userChosen.fromPrevEvent[prevEventIdx].duration; + const returnDuration = returnsToCompany + ? routingResults.userChosen.toCompany[companyIdx].duration + : routingResults.userChosen.toNextEvent[nextEventIdx].duration; + const toInsert = startFixed ? ToInsert.DROPOFF : ToInsert.PICKUP; + const cost = evaluateInsertion( + type, + window, + { + approach: approachDuration, + return: returnDuration, + pickupToDropoff: 0, + toNext: next.durationFromPrev, + fromPrev: prev.durationToNext, + fullWindow: fullWindowDuration + }, + toInsert, + allInsertions[insertionIdx][toInsert].userChosen, + undefined, + startFixed + ); + if (cost == undefined) { + return; + } + allInsertions[insertionIdx][toInsert].userChosen = cost; + return; + } + const relevantAvailabilities = (function () { + switch (type) { + case InsertionType.APPEND: + return vehicle.availabilities.filter((availability) => availability.covers(prevTime)); + case InsertionType.PREPEND: + return vehicle.availabilities.filter((availability) => availability.covers(nextTime)); + case InsertionType.CONNECT: + return vehicle.availabilities.filter((availability) => + availability.contains(new Interval(prevTime, nextTime)) + ); + case InsertionType.INSERT: + console.assert(false, 'unexpected InsertionType in computeTravelDurations'); + } + })(); + + console.assert( + relevantAvailabilities != undefined && relevantAvailabilities.length < 2, + 'Unexpectedly found 2 intervals, which are supposed to be disjoint, containing the same timestamp.' + ); + if (relevantAvailabilities == undefined || relevantAvailabilities.length == 0) { + return; + } + const relevantAvailability = relevantAvailabilities[0]; + window = window.intersect(relevantAvailability); + if (window == undefined) { + return; + } + const busStopRoutingResult = routingResults.busStops[busStopIdx]; + const travelDuration = travelDurations[busStopIdx]; + const times = busStopTimes[busStopIdx]; + for (let timeIdx = 0; timeIdx != times.length; ++timeIdx) { + // insert busstop + const approachDuration = comesFromCompany + ? busStopRoutingResult.fromCompany[companyIdx].duration + : busStopRoutingResult.fromPrevEvent[prevEventIdx].duration; + const returnDuration = returnsToCompany + ? busStopRoutingResult.toCompany[companyIdx].duration + : busStopRoutingResult.toNextEvent[nextEventIdx].duration; + const bestTime = getBestTime( + approachDuration, + returnDuration, + window, + times[timeIdx], + startFixed + ); + if (bestTime != undefined) { + const toInsert = startFixed ? ToInsert.PICKUP : ToInsert.DROPOFF; + const timeCost = evaluateInsertion( + type, + window, + { + approach: approachDuration, + return: returnDuration, + pickupToDropoff: travelDuration, + toNext: next.durationFromPrev, + fromPrev: prev.durationToNext, + fullWindow: fullWindowDuration + }, + toInsert, + allInsertions[insertionIdx][toInsert].both[busStopIdx][timeIdx], + times[timeIdx], + startFixed + ); + if (timeCost != undefined) { + allInsertions[insertionIdx][toInsert].busStops[busStopIdx][timeIdx] = timeCost; + } + } + + // insert userChosen coordinates and busstop + const approachDurationBoth = + (startFixed + ? comesFromCompany + ? busStopRoutingResult.fromCompany[companyIdx].duration + : busStopRoutingResult.fromPrevEvent[prevEventIdx].duration + : comesFromCompany + ? routingResults.userChosen.fromCompany[companyIdx].duration + : routingResults.userChosen.fromPrevEvent[prevEventIdx].duration) + travelDuration; + const returnDurationBoth = + (startFixed + ? returnsToCompany + ? routingResults.userChosen.toCompany[nextEventIdx].duration + : routingResults.userChosen.toNextEvent[nextEventIdx].duration + : returnsToCompany + ? busStopRoutingResult.toCompany[companyIdx].duration + : busStopRoutingResult.toNextEvent[nextEventIdx].duration) + travelDuration; + const bestTimeBoth = getBestTime( + approachDurationBoth, + returnDurationBoth, + window, + times[timeIdx], + startFixed + ); + if (bestTimeBoth == undefined) { + continue; + } + const timeCost = evaluateInsertion( + type, + window, + { + approach: approachDurationBoth, + return: returnDurationBoth, + pickupToDropoff: travelDuration, + toNext: next.durationFromPrev, + fromPrev: prev.durationToNext, + fullWindow: fullWindowDuration + }, + ToInsert.BOTH, + allInsertions[insertionIdx][ToInsert.BOTH].both[busStopIdx][timeIdx], + times[timeIdx], + startFixed + ); + if (timeCost == undefined) { + continue; + } + allInsertions[insertionIdx][ToInsert.BOTH].both[busStopIdx][timeIdx] = timeCost; + } + }); + } + ); + return allInsertions; +} diff --git a/src/routes/api/whitelist/routing.ts b/src/routes/api/whitelist/routing.ts new file mode 100644 index 00000000..13c59091 --- /dev/null +++ b/src/routes/api/whitelist/routing.ts @@ -0,0 +1,117 @@ +import { Direction, oneToMany, type OneToManyResult } from '$lib/api'; +import type { BusStop } from '$lib/busStop'; +import type { Company } from '$lib/compositionTypes'; +import { Coordinates } from '$lib/location'; +import type { Range } from './capacitySimulation'; +import { iterateAllInsertions, ITERATE_INSERTIONS_MODE } from './utils'; + +export type InsertionRoutingResult = { + fromCompany: OneToManyResult[]; + toCompany: OneToManyResult[]; + fromPrevEvent: OneToManyResult[]; + toNextEvent: OneToManyResult[]; +}; + +export type RoutingResults = { + busStops: InsertionRoutingResult[]; + userChosen: InsertionRoutingResult; +}; + +type RoutingCoordinates = { + busStopForwardMany: Coordinates[][]; + busStopBackwardMany: Coordinates[][]; + userChosenForwardMany: Coordinates[]; + userChosenBackwardMany: Coordinates[]; +}; + +export function gatherRoutingCoordinates( + companies: Company[], + busStops: BusStop[], + insertionsByVehicle: Map, + busStopCompanyFilter: boolean[][] +): RoutingCoordinates { + const userChosenForwardMany = new Array(); + const userChosenBackwardMany = new Array(); + const busStopForwardMany = new Array(busStops.length); + const busStopBackwardMany = new Array(busStops.length); + for (let busStopIdx = 0; busStopIdx != busStops.length; ++busStopIdx) { + busStopForwardMany[busStopIdx] = new Array(); + busStopBackwardMany[busStopIdx] = new Array(); + } + companies.forEach((company, companyIdx) => { + for (let busStopIdx = 0; busStopIdx != busStops.length; ++busStopIdx) { + if (!busStopCompanyFilter[busStopIdx][companyIdx]) { + continue; + } + busStopForwardMany[busStopIdx].push(company.coordinates); + busStopBackwardMany[busStopIdx].push(company.coordinates); + } + userChosenForwardMany.push(company.coordinates); + userChosenBackwardMany.push(company.coordinates); + }); + iterateAllInsertions( + ITERATE_INSERTIONS_MODE.SINGLE, + companies, + busStopCompanyFilter, + insertionsByVehicle, + (busStopIdx, companyIdx, prevEventPos_, nextEventPos_, vehicle, insertionIdx) => { + const backwardMany = + busStopIdx == undefined ? userChosenBackwardMany : busStopBackwardMany[busStopIdx]; + const forwardMany = + busStopIdx == undefined ? userChosenForwardMany : busStopForwardMany[busStopIdx]; + if (insertionIdx != 0) { + backwardMany.push(vehicle.events[insertionIdx - 1].coordinates); + } + if (insertionIdx != vehicle.events.length) { + forwardMany.push(vehicle.events[insertionIdx].coordinates); + } + } + ); + return { + busStopForwardMany, + busStopBackwardMany, + userChosenForwardMany, + userChosenBackwardMany + }; +} + +export async function routing( + coordinates: RoutingCoordinates, + userChosen: Coordinates, + busStops: BusStop[], + busStopCompanyFilter: boolean[][] +): Promise { + console.assert(busStopCompanyFilter.length != 0); + const from = await oneToMany(userChosen, coordinates.userChosenBackwardMany, Direction.Backward); + const to = await oneToMany(userChosen, coordinates.userChosenForwardMany, Direction.Forward); + const ret = { + userChosen: { + fromCompany: from.slice(0, busStopCompanyFilter.length), + fromPrevEvent: from.slice(busStopCompanyFilter.length), + toCompany: to.slice(0, busStopCompanyFilter.length), + toNextEvent: to.slice(busStopCompanyFilter.length) + }, + busStops: new Array(busStops.length) + }; + for (let busStopIdx = 0; busStopIdx != busStops.length; ++busStopIdx) { + const busStop = busStops[busStopIdx]; + const relevantCompanyCount = busStopCompanyFilter[busStopIdx].filter((f) => f).length; + const from = await oneToMany( + busStop.coordinates, + coordinates.busStopBackwardMany[busStopIdx], + Direction.Backward + ); + const to = await oneToMany( + busStop.coordinates, + coordinates.busStopForwardMany[busStopIdx], + Direction.Forward + ); + ret.busStops[busStopIdx] = { + fromCompany: from.slice(0, relevantCompanyCount), + fromPrevEvent: from.slice(relevantCompanyCount), + toCompany: to.slice(0, relevantCompanyCount), + toNextEvent: to.slice(relevantCompanyCount) + }; + } + return ret; +} diff --git a/src/routes/api/whitelist/utils.ts b/src/routes/api/whitelist/utils.ts new file mode 100644 index 00000000..2975020a --- /dev/null +++ b/src/routes/api/whitelist/utils.ts @@ -0,0 +1,95 @@ +import type { Company, Vehicle } from '$lib/compositionTypes'; +import type { Range } from './capacitySimulation'; + +export enum ITERATE_INSERTIONS_MODE { + SINGLE, + PAIRS +} + +export function iterateAllInsertions( + type: ITERATE_INSERTIONS_MODE, + companies: Company[], + busStopCompanyFilter: boolean[][], + insertions: Map, + insertionFn: ( + busStopIdx: number | undefined, + companyIdx: number, + prevEventPos: number | undefined, + nextEventPos: number | undefined, + vehicle: Vehicle, + outerInsertionIdx: number, + innerInsertionIdx: number | undefined + ) => void +) { + const iterateInsertions = ( + companyFilter: boolean[] | undefined, + busStopIdx: number | undefined, + prevEventPos: number, + nextEventPos: number + ) => { + companies.forEach((company, companyIdx) => { + if (companyFilter != undefined && !companyFilter[companyIdx]) { + return; + } + console.log('company', companyIdx); + company.vehicles.forEach((vehicle, vid) => { + console.log('vehicle', vid); + insertions.get(vehicle.id)!.forEach((insertion) => { + for ( + let outerIdx = insertion.earliestPickup; + outerIdx != insertion.latestDropoff; + ++outerIdx + ) { + //console.log('insertion', outerIdx); + //console.log('nnext', nextEventPos); + if (type == ITERATE_INSERTIONS_MODE.SINGLE) { + insertionFn( + busStopIdx, + companyIdx, + outerIdx == 0 ? undefined : prevEventPos, + outerIdx == vehicle.events.length ? undefined : nextEventPos, + vehicle, + outerIdx, + undefined + ); + } else { + for ( + let innerIdx = outerIdx + 1; + innerIdx != insertion.latestDropoff + 1; + ++innerIdx + ) { + insertionFn( + busStopIdx, + companyIdx, + outerIdx == 0 ? undefined : prevEventPos, + outerIdx == vehicle.events.length ? undefined : nextEventPos, + vehicle, + outerIdx, + innerIdx + ); + } + } + console.log('outer', outerIdx); + if (outerIdx != 0) { + prevEventPos++; + } + if (outerIdx != vehicle.events.length) { + nextEventPos++; + } + } + }); + }); + }); + }; + let prevEventPos = companies.length; + let nextEventPos = companies.length; + iterateInsertions(undefined, undefined, prevEventPos, nextEventPos); + busStopCompanyFilter.forEach((companyFilter, busStopIdx) => { + console.log('busstop', busStopIdx); + console.assert(companyFilter.length == companies.length); + const companyCount = companyFilter.filter((f) => f).length; + prevEventPos = companyCount; + nextEventPos = companyCount; + iterateInsertions(companyFilter, busStopIdx, prevEventPos, nextEventPos); + }); +} From 9710912e6e693cf11176f4c506c58ceeaf757f51 Mon Sep 17 00:00:00 2001 From: nils Date: Thu, 26 Sep 2024 17:00:21 +0200 Subject: [PATCH 02/12] wip --- src/lib/interval.ts | 17 ----------------- src/routes/api/whitelist/helpers.test.ts | 18 ++++++++++++++++++ src/routes/api/whitelist/insertions.ts | 6 +++--- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/lib/interval.ts b/src/lib/interval.ts index d49307e7..3878a7e1 100644 --- a/src/lib/interval.ts +++ b/src/lib/interval.ts @@ -109,23 +109,6 @@ export class Interval { return undefined; } - static intersect = (many: Interval[], one: Interval | undefined): Interval[] => { - if (one == undefined) { - return []; - } - const result: Interval[] = []; - for (let i = 0; i != many.length; ++i) { - if (one.startTime.getTime() > many[i].endTime.getTime()) { - break; - } - if (!many[i].overlaps(one)) { - continue; - } - result.push(many[i].intersect(one)!); - } - return result; - }; - covers(time: Date): boolean { return this.startTime <= time && this.endTime >= time; } diff --git a/src/routes/api/whitelist/helpers.test.ts b/src/routes/api/whitelist/helpers.test.ts index e69de29b..eca87637 100644 --- a/src/routes/api/whitelist/helpers.test.ts +++ b/src/routes/api/whitelist/helpers.test.ts @@ -0,0 +1,18 @@ +import { describe, it, expect } from 'vitest'; +import { computeCosts, InsertionType, ToInsert, type InsertionDurations } from './insertions'; + +describe('', () => { + it('TODO', () => { + const toInsert = ToInsert.PICKUP; + const type = InsertionType.APPEND; + const durations: InsertionDurations = { + approach: 5, + return: 10, + pickupToDropoff: 5, + fromPrev: 5, + toNext: 5, + fullWindow:15 + } + computeCosts(type, durations, toInsert); + }); +}); \ No newline at end of file diff --git a/src/routes/api/whitelist/insertions.ts b/src/routes/api/whitelist/insertions.ts index ff4fa33d..e66bae70 100644 --- a/src/routes/api/whitelist/insertions.ts +++ b/src/routes/api/whitelist/insertions.ts @@ -12,14 +12,14 @@ import { TAXI_WAITING_TIME_COST_FACTOR } from '$lib/constants'; -enum InsertionType { +export enum InsertionType { CONNECT, APPEND, PREPEND, INSERT } -enum ToInsert { +export enum ToInsert { PICKUP, DROPOFF, BOTH @@ -58,7 +58,7 @@ type Insertion = { event2: number; }; -type InsertionDurations = { +export type InsertionDurations = { approach: number; return: number; pickupToDropoff: number; From fa8e398896c5594dc74905197643992d63ecb19e Mon Sep 17 00:00:00 2001 From: nils Date: Fri, 27 Sep 2024 01:57:02 +0200 Subject: [PATCH 03/12] wip --- src/routes/api/whitelist/helpers.test.ts | 26 ++++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/routes/api/whitelist/helpers.test.ts b/src/routes/api/whitelist/helpers.test.ts index eca87637..d9fd817d 100644 --- a/src/routes/api/whitelist/helpers.test.ts +++ b/src/routes/api/whitelist/helpers.test.ts @@ -3,16 +3,16 @@ import { computeCosts, InsertionType, ToInsert, type InsertionDurations } from ' describe('', () => { it('TODO', () => { - const toInsert = ToInsert.PICKUP; - const type = InsertionType.APPEND; - const durations: InsertionDurations = { - approach: 5, - return: 10, - pickupToDropoff: 5, - fromPrev: 5, - toNext: 5, - fullWindow:15 - } - computeCosts(type, durations, toInsert); - }); -}); \ No newline at end of file + const toInsert = ToInsert.PICKUP; + const type = InsertionType.APPEND; + const durations: InsertionDurations = { + approach: 5, + return: 10, + pickupToDropoff: 5, + fromPrev: 5, + toNext: 5, + fullWindow: 15 + }; + computeCosts(type, durations, toInsert); + }); +}); From 0123717c1b128ea323c46fbf4c1892050c531977 Mon Sep 17 00:00:00 2001 From: nils Date: Fri, 27 Sep 2024 02:30:09 +0200 Subject: [PATCH 04/12] wip --- src/routes/api/whitelist/insertionTypes.ts | 35 +++++++++++++ src/routes/api/whitelist/insertions.ts | 60 +++++++++++----------- src/routes/api/whitelist/routing.ts | 10 ++-- src/routes/api/whitelist/utils.ts | 27 +++++----- 4 files changed, 82 insertions(+), 50 deletions(-) create mode 100644 src/routes/api/whitelist/insertionTypes.ts diff --git a/src/routes/api/whitelist/insertionTypes.ts b/src/routes/api/whitelist/insertionTypes.ts new file mode 100644 index 00000000..755adcf3 --- /dev/null +++ b/src/routes/api/whitelist/insertionTypes.ts @@ -0,0 +1,35 @@ +import type { Vehicle } from "$lib/compositionTypes"; + +export type InsertionInfo = { + companyIdx: number, + prevEventIdx: number | undefined, + nextEventIdx: number | undefined, + vehicle: Vehicle, + insertionIdx: number +} + +export type InsertionEvaluation = { + userChosen: TimeCost; + busStops: TimeCost[][]; + both: TimeCost[][]; + approachDuration: number | undefined; + returnDuration: number | undefined; + company: number; + vehicle: number; + tour1: number; + tour2: number; + event1: number; + event2: number; +} + +export type TimeCost = { + time: Date; + cost: Cost; +}; + +export type Cost = { + taxiWaitingTime: number; + taxiDrivingDuration: number; + passengerDuration: number; + total: number; +}; \ No newline at end of file diff --git a/src/routes/api/whitelist/insertions.ts b/src/routes/api/whitelist/insertions.ts index e66bae70..3c1f07b7 100644 --- a/src/routes/api/whitelist/insertions.ts +++ b/src/routes/api/whitelist/insertions.ts @@ -190,17 +190,17 @@ export function computeTravelDurations( companies, busStopCompanyFilter, possibleInsertionsByVehicle, - (busStopIdx, companyIdx, prevEventIdx, nextEventIdx, vehicle, insertionIdx, _) => { - if (prevEventIdx == undefined) { + (busStopIdx, insertionInfo, _) => { + if (insertionInfo.prevEventIdx == undefined) { return; } - if (nextEventIdx == undefined) { + if (insertionInfo.nextEventIdx == undefined) { return; } const prev: Event | undefined = - insertionIdx == 0 ? undefined : vehicle.events[insertionIdx - 1]; + insertionInfo.insertionIdx == 0 ? undefined : insertionInfo.vehicle.events[insertionInfo.insertionIdx - 1]; const next: Event | undefined = - insertionIdx == vehicle.events.length ? undefined : vehicle.events[insertionIdx]; + insertionInfo.insertionIdx == insertionInfo.vehicle.events.length ? undefined : insertionInfo.vehicle.events[insertionInfo.insertionIdx]; cases.forEach((type) => { if (type == (startFixed ? InsertionType.APPEND : InsertionType.PREPEND)) { return; @@ -228,11 +228,11 @@ export function computeTravelDurations( return; } const approachDuration = comesFromCompany - ? routingResults.userChosen.fromCompany[companyIdx].duration - : routingResults.userChosen.fromPrevEvent[prevEventIdx].duration; + ? routingResults.userChosen.fromCompany[insertionInfo.companyIdx].duration + : routingResults.userChosen.fromPrevEvent[insertionInfo.prevEventIdx!].duration; const returnDuration = returnsToCompany - ? routingResults.userChosen.toCompany[companyIdx].duration - : routingResults.userChosen.toNextEvent[nextEventIdx].duration; + ? routingResults.userChosen.toCompany[insertionInfo.companyIdx].duration + : routingResults.userChosen.toNextEvent[insertionInfo.nextEventIdx!].duration; const toInsert = startFixed ? ToInsert.DROPOFF : ToInsert.PICKUP; const cost = evaluateInsertion( type, @@ -246,24 +246,24 @@ export function computeTravelDurations( fullWindow: fullWindowDuration }, toInsert, - allInsertions[insertionIdx][toInsert].userChosen, + allInsertions[insertionInfo.insertionIdx][toInsert].userChosen, undefined, startFixed ); if (cost == undefined) { return; } - allInsertions[insertionIdx][toInsert].userChosen = cost; + allInsertions[insertionInfo.insertionIdx][toInsert].userChosen = cost; return; } const relevantAvailabilities = (function () { switch (type) { case InsertionType.APPEND: - return vehicle.availabilities.filter((availability) => availability.covers(prevTime)); + return insertionInfo.vehicle.availabilities.filter((availability) => availability.covers(prevTime)); case InsertionType.PREPEND: - return vehicle.availabilities.filter((availability) => availability.covers(nextTime)); + return insertionInfo.vehicle.availabilities.filter((availability) => availability.covers(nextTime)); case InsertionType.CONNECT: - return vehicle.availabilities.filter((availability) => + return insertionInfo.vehicle.availabilities.filter((availability) => availability.contains(new Interval(prevTime, nextTime)) ); case InsertionType.INSERT: @@ -289,11 +289,11 @@ export function computeTravelDurations( for (let timeIdx = 0; timeIdx != times.length; ++timeIdx) { // insert busstop const approachDuration = comesFromCompany - ? busStopRoutingResult.fromCompany[companyIdx].duration - : busStopRoutingResult.fromPrevEvent[prevEventIdx].duration; + ? busStopRoutingResult.fromCompany[insertionInfo.companyIdx].duration + : busStopRoutingResult.fromPrevEvent[insertionInfo.prevEventIdx!].duration; const returnDuration = returnsToCompany - ? busStopRoutingResult.toCompany[companyIdx].duration - : busStopRoutingResult.toNextEvent[nextEventIdx].duration; + ? busStopRoutingResult.toCompany[insertionInfo.companyIdx].duration + : busStopRoutingResult.toNextEvent[insertionInfo.nextEventIdx!].duration; const bestTime = getBestTime( approachDuration, returnDuration, @@ -315,12 +315,12 @@ export function computeTravelDurations( fullWindow: fullWindowDuration }, toInsert, - allInsertions[insertionIdx][toInsert].both[busStopIdx][timeIdx], + allInsertions[insertionInfo.insertionIdx][toInsert].both[busStopIdx][timeIdx], times[timeIdx], startFixed ); if (timeCost != undefined) { - allInsertions[insertionIdx][toInsert].busStops[busStopIdx][timeIdx] = timeCost; + allInsertions[insertionInfo.insertionIdx][toInsert].busStops[busStopIdx][timeIdx] = timeCost; } } @@ -328,19 +328,19 @@ export function computeTravelDurations( const approachDurationBoth = (startFixed ? comesFromCompany - ? busStopRoutingResult.fromCompany[companyIdx].duration - : busStopRoutingResult.fromPrevEvent[prevEventIdx].duration + ? busStopRoutingResult.fromCompany[insertionInfo.companyIdx].duration + : busStopRoutingResult.fromPrevEvent[insertionInfo.prevEventIdx!].duration : comesFromCompany - ? routingResults.userChosen.fromCompany[companyIdx].duration - : routingResults.userChosen.fromPrevEvent[prevEventIdx].duration) + travelDuration; + ? routingResults.userChosen.fromCompany[insertionInfo.companyIdx].duration + : routingResults.userChosen.fromPrevEvent[insertionInfo.prevEventIdx!].duration) + travelDuration; const returnDurationBoth = (startFixed ? returnsToCompany - ? routingResults.userChosen.toCompany[nextEventIdx].duration - : routingResults.userChosen.toNextEvent[nextEventIdx].duration + ? routingResults.userChosen.toCompany[insertionInfo.nextEventIdx!].duration + : routingResults.userChosen.toNextEvent[insertionInfo.nextEventIdx!].duration : returnsToCompany - ? busStopRoutingResult.toCompany[companyIdx].duration - : busStopRoutingResult.toNextEvent[nextEventIdx].duration) + travelDuration; + ? busStopRoutingResult.toCompany[insertionInfo.companyIdx].duration + : busStopRoutingResult.toNextEvent[insertionInfo.nextEventIdx!].duration) + travelDuration; const bestTimeBoth = getBestTime( approachDurationBoth, returnDurationBoth, @@ -363,14 +363,14 @@ export function computeTravelDurations( fullWindow: fullWindowDuration }, ToInsert.BOTH, - allInsertions[insertionIdx][ToInsert.BOTH].both[busStopIdx][timeIdx], + allInsertions[insertionInfo.insertionIdx][ToInsert.BOTH].both[busStopIdx][timeIdx], times[timeIdx], startFixed ); if (timeCost == undefined) { continue; } - allInsertions[insertionIdx][ToInsert.BOTH].both[busStopIdx][timeIdx] = timeCost; + allInsertions[insertionInfo.insertionIdx][ToInsert.BOTH].both[busStopIdx][timeIdx] = timeCost; } }); } diff --git a/src/routes/api/whitelist/routing.ts b/src/routes/api/whitelist/routing.ts index 13c59091..77cbda73 100644 --- a/src/routes/api/whitelist/routing.ts +++ b/src/routes/api/whitelist/routing.ts @@ -54,16 +54,16 @@ export function gatherRoutingCoordinates( companies, busStopCompanyFilter, insertionsByVehicle, - (busStopIdx, companyIdx, prevEventPos_, nextEventPos_, vehicle, insertionIdx) => { + (busStopIdx, insertionInfo) => { const backwardMany = busStopIdx == undefined ? userChosenBackwardMany : busStopBackwardMany[busStopIdx]; const forwardMany = busStopIdx == undefined ? userChosenForwardMany : busStopForwardMany[busStopIdx]; - if (insertionIdx != 0) { - backwardMany.push(vehicle.events[insertionIdx - 1].coordinates); + if (insertionInfo.insertionIdx != 0) { + backwardMany.push(insertionInfo.vehicle.events[insertionInfo.insertionIdx - 1].coordinates); } - if (insertionIdx != vehicle.events.length) { - forwardMany.push(vehicle.events[insertionIdx].coordinates); + if (insertionInfo.insertionIdx != insertionInfo.vehicle.events.length) { + forwardMany.push(insertionInfo.vehicle.events[insertionInfo.insertionIdx].coordinates); } } ); diff --git a/src/routes/api/whitelist/utils.ts b/src/routes/api/whitelist/utils.ts index 2975020a..86cb84cd 100644 --- a/src/routes/api/whitelist/utils.ts +++ b/src/routes/api/whitelist/utils.ts @@ -1,5 +1,6 @@ import type { Company, Vehicle } from '$lib/compositionTypes'; import type { Range } from './capacitySimulation'; +import type { InsertionInfo } from './insertionTypes'; export enum ITERATE_INSERTIONS_MODE { SINGLE, @@ -13,11 +14,7 @@ export function iterateAllInsertions( insertions: Map, insertionFn: ( busStopIdx: number | undefined, - companyIdx: number, - prevEventPos: number | undefined, - nextEventPos: number | undefined, - vehicle: Vehicle, - outerInsertionIdx: number, + info: InsertionInfo, innerInsertionIdx: number | undefined ) => void ) { @@ -42,14 +39,18 @@ export function iterateAllInsertions( ) { //console.log('insertion', outerIdx); //console.log('nnext', nextEventPos); + const info = + { + insertionIdx: outerIdx, + companyIdx, + vehicle, + prevEventIdx: outerIdx == 0 ? undefined : prevEventPos, + nextEventIdx: outerIdx == vehicle.events.length ? undefined : nextEventPos + }; if (type == ITERATE_INSERTIONS_MODE.SINGLE) { insertionFn( busStopIdx, - companyIdx, - outerIdx == 0 ? undefined : prevEventPos, - outerIdx == vehicle.events.length ? undefined : nextEventPos, - vehicle, - outerIdx, + info, undefined ); } else { @@ -60,11 +61,7 @@ export function iterateAllInsertions( ) { insertionFn( busStopIdx, - companyIdx, - outerIdx == 0 ? undefined : prevEventPos, - outerIdx == vehicle.events.length ? undefined : nextEventPos, - vehicle, - outerIdx, + info, innerIdx ); } From f358968f53f03752c50c56c98bfcdb629f79e252 Mon Sep 17 00:00:00 2001 From: nils Date: Fri, 27 Sep 2024 02:31:14 +0200 Subject: [PATCH 05/12] w --- .../api/whitelist/gatherCoordinates.test.ts | 491 ++++++++++++++++++ 1 file changed, 491 insertions(+) create mode 100644 src/routes/api/whitelist/gatherCoordinates.test.ts diff --git a/src/routes/api/whitelist/gatherCoordinates.test.ts b/src/routes/api/whitelist/gatherCoordinates.test.ts new file mode 100644 index 00000000..1db937ea --- /dev/null +++ b/src/routes/api/whitelist/gatherCoordinates.test.ts @@ -0,0 +1,491 @@ +import { describe, it, expect } from 'vitest'; +import { type Range } from './capacitySimulation'; +import { Coordinates } from '$lib/location'; +import type { Vehicle, Company, Event } from '$lib/compositionTypes'; +import { Interval } from '$lib/interval'; +import { gatherRoutingCoordinates } from './routing'; + +const createCompany = (vehicles: Vehicle[], coordinates: Coordinates): Company => { + return { + id: 1, + coordinates: coordinates, + vehicles, + zoneId: 1 + }; +}; + +const createVehicle = (id: number, events: Event[]): Vehicle => { + return { + id, + capacities: { passengers: 0, bikes: 0, wheelchairs: 0, luggage: 0 }, + events, + availabilities: [] + }; +}; + +const createEvent = (coordinates: Coordinates): Event => { + return { + capacities: { passengers: 0, bikes: 0, wheelchairs: 0, luggage: 0 }, + is_pickup: true, + time: new Interval(new Date(), new Date()), + id: 1, + coordinates, + tourId: 1, + arrival: new Date(), + departure: new Date(), + communicated: new Date(), + durationFromPrev: 0, + durationToNext: 0 + }; +}; + +const createBusStop = () => { + return { coordinates: new Coordinates(1, 1), times: [] }; +}; + +describe('gather coordinates test', () => { + it('insertion only possible before first event', () => { + let eventLatLng = 100; + let companyLatLng = 5; + const companies = [ + createCompany( + [ + createVehicle(1, [ + createEvent(new Coordinates(eventLatLng, eventLatLng++)), + createEvent(new Coordinates(eventLatLng, eventLatLng++)) + ]) + ], + new Coordinates(companyLatLng, companyLatLng++) + ) + ]; + const busStops = [createBusStop(), createBusStop()]; + const busStopCompanyFilter = [[true], [true]]; + const insertions = new Map(); + insertions.set(1, [{ earliestPickup: 0, latestDropoff: 0 }]); + const coordinates = gatherRoutingCoordinates( + companies, + busStops, + insertions, + busStopCompanyFilter + ); + expect(coordinates.busStopBackwardMany).toHaveLength(2); + expect(coordinates.busStopForwardMany).toHaveLength(2); + expect(coordinates.userChosenBackwardMany).toHaveLength(1); + expect(coordinates.userChosenForwardMany).toHaveLength(2); + busStops.forEach((_, idx) => { + expect(coordinates.busStopBackwardMany[idx]).toHaveLength(1); + expect(coordinates.busStopForwardMany[idx]).toHaveLength(2); + }); + // company + expect(coordinates.userChosenBackwardMany[0].lat).toBe(5); + expect(coordinates.userChosenForwardMany[0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[0][0].lat).toBe(5); + expect(coordinates.busStopForwardMany[0][0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[1][0].lat).toBe(5); + expect(coordinates.busStopForwardMany[1][0].lat).toBe(5); + + // event + expect(coordinates.userChosenBackwardMany[1]).toBe(undefined); + expect(coordinates.userChosenForwardMany[1].lat).toBe(100); + expect(coordinates.busStopBackwardMany[0][1]).toBe(undefined); + expect(coordinates.busStopForwardMany[0][1].lat).toBe(100); + expect(coordinates.busStopBackwardMany[1][1]).toBe(undefined); + expect(coordinates.busStopForwardMany[1][1].lat).toBe(100); + }); + it('insertion only possible after last event', () => { + let eventLatLng = 100; + let companyLatLng = 5; + const companies = [ + createCompany( + [ + createVehicle(1, [ + createEvent(new Coordinates(eventLatLng, eventLatLng++)), + createEvent(new Coordinates(eventLatLng, eventLatLng++)) + ]) + ], + new Coordinates(companyLatLng, companyLatLng++) + ) + ]; + const busStops = [createBusStop(), createBusStop()]; + const busStopCompanyFilter = [[true], [true]]; + const insertions = new Map(); + insertions.set(1, [{ earliestPickup: 2, latestDropoff: 2 }]); + const coordinates = gatherRoutingCoordinates( + companies, + busStops, + insertions, + busStopCompanyFilter + ); + expect(coordinates.busStopBackwardMany).toHaveLength(2); + expect(coordinates.busStopForwardMany).toHaveLength(2); + expect(coordinates.userChosenBackwardMany).toHaveLength(2); + expect(coordinates.userChosenForwardMany).toHaveLength(1); + busStops.forEach((_, idx) => { + expect(coordinates.busStopBackwardMany[idx]).toHaveLength(2); + expect(coordinates.busStopForwardMany[idx]).toHaveLength(1); + }); + // company + expect(coordinates.userChosenBackwardMany[0].lat).toBe(5); + expect(coordinates.userChosenForwardMany[0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[0][0].lat).toBe(5); + expect(coordinates.busStopForwardMany[0][0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[1][0].lat).toBe(5); + expect(coordinates.busStopForwardMany[1][0].lat).toBe(5); + + // event + expect(coordinates.userChosenForwardMany[1]).toBe(undefined); + expect(coordinates.userChosenBackwardMany[1].lat).toBe(101); + expect(coordinates.busStopForwardMany[0][1]).toBe(undefined); + expect(coordinates.busStopBackwardMany[0][1].lat).toBe(101); + expect(coordinates.busStopForwardMany[1][1]).toBe(undefined); + expect(coordinates.busStopBackwardMany[1][1].lat).toBe(101); + }); + it('insertion only possible between events', () => { + let eventLatLng = 100; + let companyLatLng = 5; + const companies = [ + createCompany( + [ + createVehicle(1, [ + createEvent(new Coordinates(eventLatLng, eventLatLng++)), + createEvent(new Coordinates(eventLatLng, eventLatLng++)) + ]) + ], + new Coordinates(companyLatLng, companyLatLng++) + ) + ]; + const busStops = [createBusStop(), createBusStop()]; + const busStopCompanyFilter = [[true], [true]]; + const insertions = new Map(); + insertions.set(1, [{ earliestPickup: 1, latestDropoff: 1 }]); + const coordinates = gatherRoutingCoordinates( + companies, + busStops, + insertions, + busStopCompanyFilter + ); + expect(coordinates.busStopBackwardMany).toHaveLength(2); + expect(coordinates.busStopForwardMany).toHaveLength(2); + expect(coordinates.userChosenBackwardMany).toHaveLength(2); + expect(coordinates.userChosenForwardMany).toHaveLength(2); + busStops.forEach((_, idx) => { + expect(coordinates.busStopBackwardMany[idx]).toHaveLength(2); + expect(coordinates.busStopForwardMany[idx]).toHaveLength(2); + }); + // company + expect(coordinates.userChosenBackwardMany[0].lat).toBe(5); + expect(coordinates.userChosenForwardMany[0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[0][0].lat).toBe(5); + expect(coordinates.busStopForwardMany[0][0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[1][0].lat).toBe(5); + expect(coordinates.busStopForwardMany[1][0].lat).toBe(5); + + // event + expect(coordinates.userChosenForwardMany[1].lat).toBe(101); + expect(coordinates.userChosenBackwardMany[1].lat).toBe(100); + expect(coordinates.busStopForwardMany[0][1].lat).toBe(101); + expect(coordinates.busStopBackwardMany[0][1].lat).toBe(100); + expect(coordinates.busStopForwardMany[1][1].lat).toBe(101); + expect(coordinates.busStopBackwardMany[1][1].lat).toBe(100); + }); + it('insertion not possible after last event', () => { + let eventLatLng = 100; + let companyLatLng = 5; + const companies = [ + createCompany( + [ + createVehicle(1, [ + createEvent(new Coordinates(eventLatLng, eventLatLng++)), + createEvent(new Coordinates(eventLatLng, eventLatLng++)) + ]) + ], + new Coordinates(companyLatLng, companyLatLng++) + ) + ]; + const busStops = [createBusStop(), createBusStop()]; + const busStopCompanyFilter = [[true], [true]]; + const insertions = new Map(); + insertions.set(1, [{ earliestPickup: 0, latestDropoff: 1 }]); + const coordinates = gatherRoutingCoordinates( + companies, + busStops, + insertions, + busStopCompanyFilter + ); + expect(coordinates.busStopBackwardMany).toHaveLength(2); + expect(coordinates.busStopForwardMany).toHaveLength(2); + expect(coordinates.userChosenBackwardMany).toHaveLength(2); + expect(coordinates.userChosenForwardMany).toHaveLength(3); + busStops.forEach((_, idx) => { + expect(coordinates.busStopBackwardMany[idx]).toHaveLength(2); + expect(coordinates.busStopForwardMany[idx]).toHaveLength(3); + }); + // company + expect(coordinates.userChosenBackwardMany[0].lat).toBe(5); + expect(coordinates.userChosenForwardMany[0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[0][0].lat).toBe(5); + expect(coordinates.busStopForwardMany[0][0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[1][0].lat).toBe(5); + expect(coordinates.busStopForwardMany[1][0].lat).toBe(5); + + // event + expect(coordinates.userChosenForwardMany[1].lat).toBe(100); + expect(coordinates.userChosenBackwardMany[1].lat).toBe(100); + expect(coordinates.busStopForwardMany[0][1].lat).toBe(100); + expect(coordinates.busStopBackwardMany[0][1].lat).toBe(100); + expect(coordinates.busStopForwardMany[1][1].lat).toBe(100); + expect(coordinates.busStopBackwardMany[1][1].lat).toBe(100); + + expect(coordinates.userChosenForwardMany[2].lat).toBe(101); + expect(coordinates.userChosenBackwardMany[2]).toBe(undefined); + expect(coordinates.busStopForwardMany[0][2].lat).toBe(101); + expect(coordinates.busStopBackwardMany[0][2]).toBe(undefined); + expect(coordinates.busStopForwardMany[1][2].lat).toBe(101); + expect(coordinates.busStopBackwardMany[1][2]).toBe(undefined); + }); + it('2companies, all insertions possible', () => { + let eventLatLng = 100; + let companyLatLng = 5; + const companies = [ + createCompany( + [ + createVehicle(1, [ + createEvent(new Coordinates(eventLatLng, eventLatLng++)), + createEvent(new Coordinates(eventLatLng, eventLatLng++)) + ]) + ], + new Coordinates(companyLatLng, companyLatLng++) + ), + createCompany( + [ + createVehicle(2, [ + createEvent(new Coordinates(eventLatLng, eventLatLng++)), + createEvent(new Coordinates(eventLatLng, eventLatLng++)) + ]) + ], + new Coordinates(companyLatLng, companyLatLng++) + ) + ]; + const busStops = [createBusStop(), createBusStop()]; + const busStopCompanyFilter = [ + [true, true], + [true, true] + ]; + const insertions = new Map(); + insertions.set(1, [{ earliestPickup: 0, latestDropoff: 2 }]); + insertions.set(2, [{ earliestPickup: 0, latestDropoff: 2 }]); + const coordinates = gatherRoutingCoordinates( + companies, + busStops, + insertions, + busStopCompanyFilter + ); + expect(coordinates.busStopBackwardMany).toHaveLength(2); + expect(coordinates.busStopForwardMany).toHaveLength(2); + expect(coordinates.userChosenBackwardMany).toHaveLength(6); + expect(coordinates.userChosenForwardMany).toHaveLength(6); + busStops.forEach((_, idx) => { + expect(coordinates.busStopBackwardMany[idx]).toHaveLength(6); + expect(coordinates.busStopForwardMany[idx]).toHaveLength(6); + }); + // Company coordinates + expect(coordinates.userChosenBackwardMany[0].lat).toBe(5); + expect(coordinates.userChosenForwardMany[0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[1][0].lat).toBe(5); + expect(coordinates.busStopForwardMany[1][0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[0][0].lat).toBe(5); + expect(coordinates.busStopForwardMany[0][0].lat).toBe(5); + expect(coordinates.userChosenBackwardMany[1].lat).toBe(6); + expect(coordinates.userChosenForwardMany[1].lat).toBe(6); + expect(coordinates.busStopBackwardMany[0][1].lat).toBe(6); + expect(coordinates.busStopForwardMany[0][1].lat).toBe(6); + expect(coordinates.busStopBackwardMany[1][1].lat).toBe(6); + expect(coordinates.busStopForwardMany[1][1].lat).toBe(6); + + // Event coordinates + expect(coordinates.userChosenBackwardMany[2].lat).toBe(100); + expect(coordinates.userChosenForwardMany[2].lat).toBe(100); + expect(coordinates.busStopBackwardMany[0][2].lat).toBe(100); + expect(coordinates.busStopForwardMany[0][2].lat).toBe(100); + expect(coordinates.busStopBackwardMany[1][2].lat).toBe(100); + expect(coordinates.busStopForwardMany[1][2].lat).toBe(100); + + expect(coordinates.userChosenBackwardMany[3].lat).toBe(101); + expect(coordinates.userChosenForwardMany[3].lat).toBe(101); + expect(coordinates.busStopBackwardMany[0][3].lat).toBe(101); + expect(coordinates.busStopForwardMany[0][3].lat).toBe(101); + expect(coordinates.busStopBackwardMany[1][3].lat).toBe(101); + expect(coordinates.busStopForwardMany[1][3].lat).toBe(101); + + expect(coordinates.userChosenBackwardMany[4].lat).toBe(102); + expect(coordinates.userChosenForwardMany[4].lat).toBe(102); + expect(coordinates.busStopBackwardMany[0][4].lat).toBe(102); + expect(coordinates.busStopForwardMany[0][4].lat).toBe(102); + expect(coordinates.busStopBackwardMany[1][4].lat).toBe(102); + expect(coordinates.busStopForwardMany[1][4].lat).toBe(102); + + expect(coordinates.userChosenBackwardMany[5].lat).toBe(103); + expect(coordinates.userChosenForwardMany[5].lat).toBe(103); + expect(coordinates.busStopBackwardMany[0][5].lat).toBe(103); + expect(coordinates.busStopForwardMany[0][5].lat).toBe(103); + expect(coordinates.busStopBackwardMany[1][5].lat).toBe(103); + expect(coordinates.busStopForwardMany[1][5].lat).toBe(103); + }); + it('2companies, 1 company-busstop combination filtered out', () => { + let eventLatLng = 100; + let companyLatLng = 5; + const companies = [ + createCompany( + [ + createVehicle(1, [ + createEvent(new Coordinates(eventLatLng, eventLatLng++)), + createEvent(new Coordinates(eventLatLng, eventLatLng++)) + ]) + ], + new Coordinates(companyLatLng, companyLatLng++) + ), + createCompany( + [ + createVehicle(2, [ + createEvent(new Coordinates(eventLatLng, eventLatLng++)), + createEvent(new Coordinates(eventLatLng, eventLatLng++)) + ]) + ], + new Coordinates(companyLatLng, companyLatLng++) + ) + ]; + const busStops = [createBusStop(), createBusStop()]; + const busStopCompanyFilter = [ + [false, true], + [true, true] + ]; + const insertions = new Map(); + insertions.set(1, [{ earliestPickup: 0, latestDropoff: 2 }]); + insertions.set(2, [{ earliestPickup: 0, latestDropoff: 2 }]); + const coordinates = gatherRoutingCoordinates( + companies, + busStops, + insertions, + busStopCompanyFilter + ); + expect(coordinates.busStopBackwardMany).toHaveLength(2); + expect(coordinates.busStopForwardMany).toHaveLength(2); + expect(coordinates.userChosenBackwardMany).toHaveLength(6); + expect(coordinates.userChosenForwardMany).toHaveLength(6); + expect(coordinates.busStopBackwardMany[0]).toHaveLength(3); + expect(coordinates.busStopForwardMany[0]).toHaveLength(3); + expect(coordinates.busStopBackwardMany[1]).toHaveLength(6); + expect(coordinates.busStopForwardMany[1]).toHaveLength(6); + // Company coordinates + expect(coordinates.userChosenBackwardMany[0].lat).toBe(5); + expect(coordinates.userChosenForwardMany[0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[1][0].lat).toBe(5); + expect(coordinates.busStopForwardMany[1][0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[0][0].lat).toBe(6); + expect(coordinates.busStopForwardMany[0][0].lat).toBe(6); + expect(coordinates.userChosenBackwardMany[1].lat).toBe(6); + expect(coordinates.userChosenForwardMany[1].lat).toBe(6); + expect(coordinates.busStopBackwardMany[1][1].lat).toBe(6); + expect(coordinates.busStopForwardMany[1][1].lat).toBe(6); + // Event coordinates + expect(coordinates.userChosenBackwardMany[2].lat).toBe(100); + expect(coordinates.userChosenForwardMany[2].lat).toBe(100); + expect(coordinates.busStopBackwardMany[1][2].lat).toBe(100); + expect(coordinates.busStopForwardMany[1][2].lat).toBe(100); + + expect(coordinates.userChosenBackwardMany[3].lat).toBe(101); + expect(coordinates.userChosenForwardMany[3].lat).toBe(101); + expect(coordinates.busStopBackwardMany[1][3].lat).toBe(101); + expect(coordinates.busStopForwardMany[1][3].lat).toBe(101); + + expect(coordinates.userChosenBackwardMany[4].lat).toBe(102); + expect(coordinates.userChosenForwardMany[4].lat).toBe(102); + expect(coordinates.busStopBackwardMany[0][1].lat).toBe(102); + expect(coordinates.busStopForwardMany[0][1].lat).toBe(102); + expect(coordinates.busStopBackwardMany[1][4].lat).toBe(102); + expect(coordinates.busStopForwardMany[1][4].lat).toBe(102); + + expect(coordinates.userChosenBackwardMany[5].lat).toBe(103); + expect(coordinates.userChosenForwardMany[5].lat).toBe(103); + expect(coordinates.busStopBackwardMany[0][2].lat).toBe(103); + expect(coordinates.busStopForwardMany[0][2].lat).toBe(103); + expect(coordinates.busStopBackwardMany[1][5].lat).toBe(103); + expect(coordinates.busStopForwardMany[1][5].lat).toBe(103); + }); + it('2companies, 1 busstop filtered out', () => { + let eventLatLng = 100; + let companyLatLng = 5; + const companies = [ + createCompany( + [ + createVehicle(1, [ + createEvent(new Coordinates(eventLatLng, eventLatLng++)), + createEvent(new Coordinates(eventLatLng, eventLatLng++)) + ]) + ], + new Coordinates(companyLatLng, companyLatLng++) + ), + createCompany( + [ + createVehicle(2, [ + createEvent(new Coordinates(eventLatLng, eventLatLng++)), + createEvent(new Coordinates(eventLatLng, eventLatLng++)) + ]) + ], + new Coordinates(companyLatLng, companyLatLng++) + ) + ]; + const busStops = [createBusStop(), createBusStop()]; + const busStopCompanyFilter = [ + [false, false], + [true, true] + ]; + const insertions = new Map(); + insertions.set(1, [{ earliestPickup: 0, latestDropoff: 2 }]); + insertions.set(2, [{ earliestPickup: 0, latestDropoff: 2 }]); + const coordinates = gatherRoutingCoordinates( + companies, + busStops, + insertions, + busStopCompanyFilter + ); + expect(coordinates.busStopBackwardMany).toHaveLength(2); + expect(coordinates.busStopForwardMany).toHaveLength(2); + expect(coordinates.userChosenBackwardMany).toHaveLength(6); + expect(coordinates.userChosenForwardMany).toHaveLength(6); + expect(coordinates.busStopBackwardMany[0]).toHaveLength(0); + expect(coordinates.busStopForwardMany[0]).toHaveLength(0); + expect(coordinates.busStopBackwardMany[1]).toHaveLength(6); + expect(coordinates.busStopForwardMany[1]).toHaveLength(6); + // Company coordinates + expect(coordinates.userChosenBackwardMany[0].lat).toBe(5); + expect(coordinates.userChosenForwardMany[0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[1][0].lat).toBe(5); + expect(coordinates.busStopForwardMany[1][0].lat).toBe(5); + + expect(coordinates.userChosenBackwardMany[1].lat).toBe(6); + expect(coordinates.userChosenForwardMany[1].lat).toBe(6); + expect(coordinates.busStopBackwardMany[1][1].lat).toBe(6); + expect(coordinates.busStopForwardMany[1][1].lat).toBe(6); + // Event coordinates + expect(coordinates.userChosenBackwardMany[2].lat).toBe(100); + expect(coordinates.userChosenForwardMany[2].lat).toBe(100); + expect(coordinates.busStopBackwardMany[1][2].lat).toBe(100); + expect(coordinates.busStopForwardMany[1][2].lat).toBe(100); + + expect(coordinates.userChosenBackwardMany[3].lat).toBe(101); + expect(coordinates.userChosenForwardMany[3].lat).toBe(101); + expect(coordinates.busStopBackwardMany[1][3].lat).toBe(101); + expect(coordinates.busStopForwardMany[1][3].lat).toBe(101); + + expect(coordinates.userChosenBackwardMany[4].lat).toBe(102); + expect(coordinates.userChosenForwardMany[4].lat).toBe(102); + expect(coordinates.busStopBackwardMany[1][4].lat).toBe(102); + expect(coordinates.busStopForwardMany[1][4].lat).toBe(102); + + expect(coordinates.userChosenBackwardMany[5].lat).toBe(103); + expect(coordinates.userChosenForwardMany[5].lat).toBe(103); + expect(coordinates.busStopBackwardMany[1][5].lat).toBe(103); + expect(coordinates.busStopForwardMany[1][5].lat).toBe(103); + }); +}); From 79c3c08cc75a6eba6902a0a41bfe7cf6a1a49973 Mon Sep 17 00:00:00 2001 From: nils Date: Fri, 27 Sep 2024 02:54:42 +0200 Subject: [PATCH 06/12] wip --- src/routes/api/whitelist/utils.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/routes/api/whitelist/utils.ts b/src/routes/api/whitelist/utils.ts index 86cb84cd..4cb9d20b 100644 --- a/src/routes/api/whitelist/utils.ts +++ b/src/routes/api/whitelist/utils.ts @@ -25,19 +25,21 @@ export function iterateAllInsertions( nextEventPos: number ) => { companies.forEach((company, companyIdx) => { + console.log('company', companyIdx); if (companyFilter != undefined && !companyFilter[companyIdx]) { return; } - console.log('company', companyIdx); company.vehicles.forEach((vehicle, vid) => { console.log('vehicle', vid); + console.log(insertions); insertions.get(vehicle.id)!.forEach((insertion) => { + console.log(insertion); for ( let outerIdx = insertion.earliestPickup; outerIdx != insertion.latestDropoff; ++outerIdx ) { - //console.log('insertion', outerIdx); + console.log('insertion', outerIdx); //console.log('nnext', nextEventPos); const info = { @@ -47,6 +49,8 @@ export function iterateAllInsertions( prevEventIdx: outerIdx == 0 ? undefined : prevEventPos, nextEventIdx: outerIdx == vehicle.events.length ? undefined : nextEventPos }; + //console.log(info); + console.log("events: ", vehicle.events.length); if (type == ITERATE_INSERTIONS_MODE.SINGLE) { insertionFn( busStopIdx, @@ -66,7 +70,6 @@ export function iterateAllInsertions( ); } } - console.log('outer', outerIdx); if (outerIdx != 0) { prevEventPos++; } @@ -81,6 +84,7 @@ export function iterateAllInsertions( let prevEventPos = companies.length; let nextEventPos = companies.length; iterateInsertions(undefined, undefined, prevEventPos, nextEventPos); + console.log("companies done"); busStopCompanyFilter.forEach((companyFilter, busStopIdx) => { console.log('busstop', busStopIdx); console.assert(companyFilter.length == companies.length); From a4c42eb1fa534dc90b088cc0c2bbac8be2804ea5 Mon Sep 17 00:00:00 2001 From: nils Date: Fri, 27 Sep 2024 03:05:30 +0200 Subject: [PATCH 07/12] wip --- src/routes/api/whitelist/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/api/whitelist/utils.ts b/src/routes/api/whitelist/utils.ts index 4cb9d20b..f3656aa6 100644 --- a/src/routes/api/whitelist/utils.ts +++ b/src/routes/api/whitelist/utils.ts @@ -36,7 +36,7 @@ export function iterateAllInsertions( console.log(insertion); for ( let outerIdx = insertion.earliestPickup; - outerIdx != insertion.latestDropoff; + outerIdx != insertion.latestDropoff + 1; ++outerIdx ) { console.log('insertion', outerIdx); From bd600dcacb5ea8a2260f6ad5fe7c2fc59bb0e0af Mon Sep 17 00:00:00 2001 From: nils Date: Fri, 27 Sep 2024 03:06:31 +0200 Subject: [PATCH 08/12] wip --- src/routes/api/whitelist/utils.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/routes/api/whitelist/utils.ts b/src/routes/api/whitelist/utils.ts index f3656aa6..81ae130b 100644 --- a/src/routes/api/whitelist/utils.ts +++ b/src/routes/api/whitelist/utils.ts @@ -25,22 +25,16 @@ export function iterateAllInsertions( nextEventPos: number ) => { companies.forEach((company, companyIdx) => { - console.log('company', companyIdx); if (companyFilter != undefined && !companyFilter[companyIdx]) { return; } company.vehicles.forEach((vehicle, vid) => { - console.log('vehicle', vid); - console.log(insertions); insertions.get(vehicle.id)!.forEach((insertion) => { - console.log(insertion); for ( let outerIdx = insertion.earliestPickup; outerIdx != insertion.latestDropoff + 1; ++outerIdx ) { - console.log('insertion', outerIdx); - //console.log('nnext', nextEventPos); const info = { insertionIdx: outerIdx, @@ -49,8 +43,6 @@ export function iterateAllInsertions( prevEventIdx: outerIdx == 0 ? undefined : prevEventPos, nextEventIdx: outerIdx == vehicle.events.length ? undefined : nextEventPos }; - //console.log(info); - console.log("events: ", vehicle.events.length); if (type == ITERATE_INSERTIONS_MODE.SINGLE) { insertionFn( busStopIdx, @@ -84,10 +76,7 @@ export function iterateAllInsertions( let prevEventPos = companies.length; let nextEventPos = companies.length; iterateInsertions(undefined, undefined, prevEventPos, nextEventPos); - console.log("companies done"); busStopCompanyFilter.forEach((companyFilter, busStopIdx) => { - console.log('busstop', busStopIdx); - console.assert(companyFilter.length == companies.length); const companyCount = companyFilter.filter((f) => f).length; prevEventPos = companyCount; nextEventPos = companyCount; From dcb52cd01736f9c7aad01df33b0c782b95f0d66f Mon Sep 17 00:00:00 2001 From: nils Date: Fri, 27 Sep 2024 03:58:29 +0200 Subject: [PATCH 09/12] wip --- .../api/whitelist/gatherCoordinates.test.ts | 4 +- src/routes/api/whitelist/insertionTypes.ts | 22 +- src/routes/api/whitelist/insertions.ts | 332 ++++++++++-------- src/routes/api/whitelist/utils.ts | 27 +- 4 files changed, 199 insertions(+), 186 deletions(-) diff --git a/src/routes/api/whitelist/gatherCoordinates.test.ts b/src/routes/api/whitelist/gatherCoordinates.test.ts index 1db937ea..c6d1caa7 100644 --- a/src/routes/api/whitelist/gatherCoordinates.test.ts +++ b/src/routes/api/whitelist/gatherCoordinates.test.ts @@ -34,8 +34,8 @@ const createEvent = (coordinates: Coordinates): Event => { arrival: new Date(), departure: new Date(), communicated: new Date(), - durationFromPrev: 0, - durationToNext: 0 + durationFromPrev: 0, + durationToNext: 0 }; }; diff --git a/src/routes/api/whitelist/insertionTypes.ts b/src/routes/api/whitelist/insertionTypes.ts index 755adcf3..8de90b8f 100644 --- a/src/routes/api/whitelist/insertionTypes.ts +++ b/src/routes/api/whitelist/insertionTypes.ts @@ -1,12 +1,12 @@ -import type { Vehicle } from "$lib/compositionTypes"; +import type { Vehicle } from '$lib/compositionTypes'; export type InsertionInfo = { - companyIdx: number, - prevEventIdx: number | undefined, - nextEventIdx: number | undefined, - vehicle: Vehicle, - insertionIdx: number -} + companyIdx: number; + prevEventIdx: number; + nextEventIdx: number; + vehicle: Vehicle; + insertionIdx: number; +}; export type InsertionEvaluation = { userChosen: TimeCost; @@ -20,11 +20,11 @@ export type InsertionEvaluation = { tour2: number; event1: number; event2: number; -} +}; export type TimeCost = { - time: Date; - cost: Cost; + time: Date; + cost: Cost; }; export type Cost = { @@ -32,4 +32,4 @@ export type Cost = { taxiDrivingDuration: number; passengerDuration: number; total: number; -}; \ No newline at end of file +}; diff --git a/src/routes/api/whitelist/insertions.ts b/src/routes/api/whitelist/insertions.ts index 3c1f07b7..a007b2b4 100644 --- a/src/routes/api/whitelist/insertions.ts +++ b/src/routes/api/whitelist/insertions.ts @@ -3,7 +3,7 @@ import { Interval } from '$lib/interval'; import { ITERATE_INSERTIONS_MODE, iterateAllInsertions } from './utils'; import { type RoutingResults } from './routing'; import type { Range } from './capacitySimulation'; -import { minutesToMs } from '$lib/time_utils'; +import { hoursToMs, minutesToMs } from '$lib/time_utils'; import { MIN_PREP_MINUTES, PASSENGER_CHANGE_TIME, @@ -11,6 +11,8 @@ import { TAXI_DRIVING_TIME_COST_FACTOR, TAXI_WAITING_TIME_COST_FACTOR } from '$lib/constants'; +import type { InsertionInfo } from './insertionTypes'; +import { Vehicle } from '../../(user)/taxi/types'; export enum InsertionType { CONNECT, @@ -185,91 +187,98 @@ export function computeTravelDurations( busStopCompanyFilter: boolean[][] ): Insertion[][] { const allInsertions = new Array(); - iterateAllInsertions( - ITERATE_INSERTIONS_MODE.SINGLE, - companies, - busStopCompanyFilter, - possibleInsertionsByVehicle, - (busStopIdx, insertionInfo, _) => { - if (insertionInfo.prevEventIdx == undefined) { + const insertionFn = ( + busStopIdx: number | undefined, + insertionInfo: InsertionInfo, + _: number | undefined + ) => { + const prev: Event | undefined = + insertionInfo.insertionIdx == 0 + ? undefined + : insertionInfo.vehicle.events[insertionInfo.insertionIdx - 1]; + const next: Event | undefined = + insertionInfo.insertionIdx == insertionInfo.vehicle.events.length + ? undefined + : insertionInfo.vehicle.events[insertionInfo.insertionIdx]; + cases.forEach((type) => { + if (prev == undefined && type != InsertionType.PREPEND) { return; } - if (insertionInfo.nextEventIdx == undefined) { + if (next == undefined && type != InsertionType.APPEND) { return; } - const prev: Event | undefined = - insertionInfo.insertionIdx == 0 ? undefined : insertionInfo.vehicle.events[insertionInfo.insertionIdx - 1]; - const next: Event | undefined = - insertionInfo.insertionIdx == insertionInfo.vehicle.events.length ? undefined : insertionInfo.vehicle.events[insertionInfo.insertionIdx]; - cases.forEach((type) => { - if (type == (startFixed ? InsertionType.APPEND : InsertionType.PREPEND)) { - return; - } - if ( - prev == undefined || - next == undefined || - (prev.tourId == next.tourId) != (type == InsertionType.INSERT) - ) { - return; - } - const returnsToCompany = type === InsertionType.CONNECT || type === InsertionType.APPEND; - const comesFromCompany = type === InsertionType.CONNECT || type === InsertionType.PREPEND; + if ( + prev != undefined && + next != undefined && + (prev.tourId == next.tourId) != (type == InsertionType.INSERT) + ) { + return; + } + const returnsToCompany = type === InsertionType.CONNECT || type === InsertionType.APPEND; + const comesFromCompany = type === InsertionType.CONNECT || type === InsertionType.PREPEND; - const prevTime = comesFromCompany ? prev.arrival : prev.communicated; - if (prevTime < new Date(Date.now() + minutesToMs(MIN_PREP_MINUTES))) { + const prevTime = + prev == undefined ? new Date(0) : comesFromCompany ? prev.arrival : prev.communicated; + if (prevTime < new Date(Date.now() + minutesToMs(MIN_PREP_MINUTES))) { + return; + } + const nextTime = + next == undefined + ? new Date(Date.now() + hoursToMs(240000)) + : returnsToCompany + ? next.departure + : next.communicated; + const fullWindowDuration = nextTime.getTime() - prevTime.getTime(); + let window: Interval | undefined = new Interval(prevTime, nextTime); + if (busStopIdx == undefined) { + // insert userChosen + if (type == (startFixed ? InsertionType.APPEND : InsertionType.PREPEND)) { return; } - const nextTime = returnsToCompany ? next.departure : next.communicated; - const fullWindowDuration = nextTime.getTime() - prevTime.getTime(); - let window: Interval | undefined = new Interval(prevTime, nextTime); - if (busStopIdx == undefined) { - // insert userChosen - if (type == (startFixed ? InsertionType.PREPEND : InsertionType.APPEND)) { - return; - } - const approachDuration = comesFromCompany - ? routingResults.userChosen.fromCompany[insertionInfo.companyIdx].duration - : routingResults.userChosen.fromPrevEvent[insertionInfo.prevEventIdx!].duration; - const returnDuration = returnsToCompany - ? routingResults.userChosen.toCompany[insertionInfo.companyIdx].duration - : routingResults.userChosen.toNextEvent[insertionInfo.nextEventIdx!].duration; - const toInsert = startFixed ? ToInsert.DROPOFF : ToInsert.PICKUP; - const cost = evaluateInsertion( - type, - window, - { - approach: approachDuration, - return: returnDuration, - pickupToDropoff: 0, - toNext: next.durationFromPrev, - fromPrev: prev.durationToNext, - fullWindow: fullWindowDuration - }, - toInsert, - allInsertions[insertionInfo.insertionIdx][toInsert].userChosen, - undefined, - startFixed - ); - if (cost == undefined) { - return; - } - allInsertions[insertionInfo.insertionIdx][toInsert].userChosen = cost; + console.assert( + insertionInfo.prevEventIdx != 0 || comesFromCompany, + 'Accessing nonexistin previous event.' + ); + console.assert( + insertionInfo.nextEventIdx != insertionInfo.vehicle.events.length || returnsToCompany, + 'Accessing nonexisting next event.' + ); + const toInsert = startFixed ? ToInsert.DROPOFF : ToInsert.PICKUP; + const cost = evaluateInsertion( + type, + window, + { + approach: comesFromCompany + ? routingResults.userChosen.fromCompany[insertionInfo.companyIdx].duration + : routingResults.userChosen.fromPrevEvent[insertionInfo.prevEventIdx].duration, + return: returnsToCompany + ? routingResults.userChosen.toCompany[insertionInfo.companyIdx].duration + : routingResults.userChosen.toNextEvent[insertionInfo.nextEventIdx].duration, + pickupToDropoff: 0, + toNext: next == undefined ? 0 : next.durationFromPrev, + fromPrev: prev == undefined ? 0 : prev.durationToNext, + fullWindow: fullWindowDuration + }, + toInsert, + allInsertions[insertionInfo.insertionIdx][toInsert].userChosen, + undefined, + startFixed + ); + if (cost == undefined) { return; } - const relevantAvailabilities = (function () { - switch (type) { - case InsertionType.APPEND: - return insertionInfo.vehicle.availabilities.filter((availability) => availability.covers(prevTime)); - case InsertionType.PREPEND: - return insertionInfo.vehicle.availabilities.filter((availability) => availability.covers(nextTime)); - case InsertionType.CONNECT: - return insertionInfo.vehicle.availabilities.filter((availability) => + allInsertions[insertionInfo.insertionIdx][toInsert].userChosen = cost; + return; + } + if (type != InsertionType.INSERT) { + const relevantAvailabilities = + type == InsertionType.CONNECT + ? insertionInfo.vehicle.availabilities.filter((availability) => availability.contains(new Interval(prevTime, nextTime)) + ) + : insertionInfo.vehicle.availabilities.filter((availability) => + availability.covers(prevTime) ); - case InsertionType.INSERT: - console.assert(false, 'unexpected InsertionType in computeTravelDurations'); - } - })(); console.assert( relevantAvailabilities != undefined && relevantAvailabilities.length < 2, @@ -278,102 +287,115 @@ export function computeTravelDurations( if (relevantAvailabilities == undefined || relevantAvailabilities.length == 0) { return; } - const relevantAvailability = relevantAvailabilities[0]; - window = window.intersect(relevantAvailability); + window = window.intersect(relevantAvailabilities[0]); if (window == undefined) { return; } - const busStopRoutingResult = routingResults.busStops[busStopIdx]; - const travelDuration = travelDurations[busStopIdx]; - const times = busStopTimes[busStopIdx]; - for (let timeIdx = 0; timeIdx != times.length; ++timeIdx) { - // insert busstop - const approachDuration = comesFromCompany - ? busStopRoutingResult.fromCompany[insertionInfo.companyIdx].duration - : busStopRoutingResult.fromPrevEvent[insertionInfo.prevEventIdx!].duration; - const returnDuration = returnsToCompany - ? busStopRoutingResult.toCompany[insertionInfo.companyIdx].duration - : busStopRoutingResult.toNextEvent[insertionInfo.nextEventIdx!].duration; - const bestTime = getBestTime( - approachDuration, - returnDuration, - window, - times[timeIdx], - startFixed - ); - if (bestTime != undefined) { - const toInsert = startFixed ? ToInsert.PICKUP : ToInsert.DROPOFF; - const timeCost = evaluateInsertion( - type, - window, - { - approach: approachDuration, - return: returnDuration, - pickupToDropoff: travelDuration, - toNext: next.durationFromPrev, - fromPrev: prev.durationToNext, - fullWindow: fullWindowDuration - }, - toInsert, - allInsertions[insertionInfo.insertionIdx][toInsert].both[busStopIdx][timeIdx], - times[timeIdx], - startFixed - ); - if (timeCost != undefined) { - allInsertions[insertionInfo.insertionIdx][toInsert].busStops[busStopIdx][timeIdx] = timeCost; - } - } + } + const busStopRoutingResult = routingResults.busStops[busStopIdx]; + const travelDuration = travelDurations[busStopIdx]; + const times = busStopTimes[busStopIdx]; + for (let timeIdx = 0; timeIdx != times.length; ++timeIdx) { + // insert userChosen coordinates and busstop + const approachDurationBoth = + (startFixed + ? comesFromCompany + ? busStopRoutingResult.fromCompany[insertionInfo.companyIdx].duration + : busStopRoutingResult.fromPrevEvent[insertionInfo.prevEventIdx].duration + : comesFromCompany + ? routingResults.userChosen.fromCompany[insertionInfo.companyIdx].duration + : routingResults.userChosen.fromPrevEvent[insertionInfo.prevEventIdx].duration) + + travelDuration; + const returnDurationBoth = + (startFixed + ? returnsToCompany + ? routingResults.userChosen.toCompany[insertionInfo.nextEventIdx].duration + : routingResults.userChosen.toNextEvent[insertionInfo.nextEventIdx].duration + : returnsToCompany + ? busStopRoutingResult.toCompany[insertionInfo.companyIdx].duration + : busStopRoutingResult.toNextEvent[insertionInfo.nextEventIdx].duration) + + travelDuration; + const bestTimeBoth = getBestTime( + approachDurationBoth, + returnDurationBoth, + window, + times[timeIdx], + startFixed + ); + if (bestTimeBoth == undefined) { + continue; + } + const timeCost = evaluateInsertion( + type, + window, + { + approach: approachDurationBoth, + return: returnDurationBoth, + pickupToDropoff: travelDuration, + toNext: next == undefined ? 0 : next.durationFromPrev, + fromPrev: prev == undefined ? 0 : prev.durationToNext, + fullWindow: fullWindowDuration + }, + ToInsert.BOTH, + allInsertions[insertionInfo.insertionIdx][ToInsert.BOTH].both[busStopIdx][timeIdx], + times[timeIdx], + startFixed + ); + if (timeCost == undefined) { + continue; + } + allInsertions[insertionInfo.insertionIdx][ToInsert.BOTH].both[busStopIdx][timeIdx] = + timeCost; - // insert userChosen coordinates and busstop - const approachDurationBoth = - (startFixed - ? comesFromCompany - ? busStopRoutingResult.fromCompany[insertionInfo.companyIdx].duration - : busStopRoutingResult.fromPrevEvent[insertionInfo.prevEventIdx!].duration - : comesFromCompany - ? routingResults.userChosen.fromCompany[insertionInfo.companyIdx].duration - : routingResults.userChosen.fromPrevEvent[insertionInfo.prevEventIdx!].duration) + travelDuration; - const returnDurationBoth = - (startFixed - ? returnsToCompany - ? routingResults.userChosen.toCompany[insertionInfo.nextEventIdx!].duration - : routingResults.userChosen.toNextEvent[insertionInfo.nextEventIdx!].duration - : returnsToCompany - ? busStopRoutingResult.toCompany[insertionInfo.companyIdx].duration - : busStopRoutingResult.toNextEvent[insertionInfo.nextEventIdx!].duration) + travelDuration; - const bestTimeBoth = getBestTime( - approachDurationBoth, - returnDurationBoth, - window, - times[timeIdx], - startFixed - ); - if (bestTimeBoth == undefined) { - continue; - } + if (type == (startFixed ? InsertionType.PREPEND : InsertionType.APPEND)) { + return; + } + // insert busstop + const approachDuration = comesFromCompany + ? busStopRoutingResult.fromCompany[insertionInfo.companyIdx].duration + : busStopRoutingResult.fromPrevEvent[insertionInfo.prevEventIdx].duration; + const returnDuration = returnsToCompany + ? busStopRoutingResult.toCompany[insertionInfo.companyIdx].duration + : busStopRoutingResult.toNextEvent[insertionInfo.nextEventIdx].duration; + const bestTime = getBestTime( + approachDuration, + returnDuration, + window, + times[timeIdx], + startFixed + ); + if (bestTime != undefined) { + const toInsert = startFixed ? ToInsert.PICKUP : ToInsert.DROPOFF; const timeCost = evaluateInsertion( type, window, { - approach: approachDurationBoth, - return: returnDurationBoth, + approach: approachDuration, + return: returnDuration, pickupToDropoff: travelDuration, - toNext: next.durationFromPrev, - fromPrev: prev.durationToNext, + toNext: next == undefined ? 0 : next.durationFromPrev, + fromPrev: prev == undefined ? 0 : prev.durationToNext, fullWindow: fullWindowDuration }, - ToInsert.BOTH, - allInsertions[insertionInfo.insertionIdx][ToInsert.BOTH].both[busStopIdx][timeIdx], + toInsert, + allInsertions[insertionInfo.insertionIdx][toInsert].both[busStopIdx][timeIdx], times[timeIdx], startFixed ); - if (timeCost == undefined) { - continue; + if (timeCost != undefined) { + allInsertions[insertionInfo.insertionIdx][toInsert].busStops[busStopIdx][timeIdx] = + timeCost; } - allInsertions[insertionInfo.insertionIdx][ToInsert.BOTH].both[busStopIdx][timeIdx] = timeCost; } - }); - } + } + }); + }; + iterateAllInsertions( + ITERATE_INSERTIONS_MODE.SINGLE, + companies, + busStopCompanyFilter, + possibleInsertionsByVehicle, + insertionFn ); return allInsertions; } diff --git a/src/routes/api/whitelist/utils.ts b/src/routes/api/whitelist/utils.ts index 81ae130b..1f90b429 100644 --- a/src/routes/api/whitelist/utils.ts +++ b/src/routes/api/whitelist/utils.ts @@ -21,8 +21,8 @@ export function iterateAllInsertions( const iterateInsertions = ( companyFilter: boolean[] | undefined, busStopIdx: number | undefined, - prevEventPos: number, - nextEventPos: number + prevEventIdx: number, + nextEventIdx: number ) => { companies.forEach((company, companyIdx) => { if (companyFilter != undefined && !companyFilter[companyIdx]) { @@ -35,38 +35,29 @@ export function iterateAllInsertions( outerIdx != insertion.latestDropoff + 1; ++outerIdx ) { - const info = - { + const info = { insertionIdx: outerIdx, companyIdx, vehicle, - prevEventIdx: outerIdx == 0 ? undefined : prevEventPos, - nextEventIdx: outerIdx == vehicle.events.length ? undefined : nextEventPos + prevEventIdx, + nextEventIdx }; if (type == ITERATE_INSERTIONS_MODE.SINGLE) { - insertionFn( - busStopIdx, - info, - undefined - ); + insertionFn(busStopIdx, info, undefined); } else { for ( let innerIdx = outerIdx + 1; innerIdx != insertion.latestDropoff + 1; ++innerIdx ) { - insertionFn( - busStopIdx, - info, - innerIdx - ); + insertionFn(busStopIdx, info, innerIdx); } } if (outerIdx != 0) { - prevEventPos++; + prevEventIdx++; } if (outerIdx != vehicle.events.length) { - nextEventPos++; + nextEventIdx++; } } }); From b8e31973cd56607fe982f354d90ec000e6b815ba Mon Sep 17 00:00:00 2001 From: nils Date: Fri, 27 Sep 2024 04:43:07 +0200 Subject: [PATCH 10/12] wip --- src/routes/api/whitelist/insertions.ts | 139 ++++++++++++------------- src/routes/api/whitelist/utils.ts | 14 +-- 2 files changed, 73 insertions(+), 80 deletions(-) diff --git a/src/routes/api/whitelist/insertions.ts b/src/routes/api/whitelist/insertions.ts index a007b2b4..f063fd1c 100644 --- a/src/routes/api/whitelist/insertions.ts +++ b/src/routes/api/whitelist/insertions.ts @@ -144,10 +144,8 @@ export function computeCosts( const passengerDuration = (function () { switch (toInsert) { case ToInsert.PICKUP: - console.assert(type != InsertionType.APPEND, 'Trying to Append a Pickup event.'); return durations.return; case ToInsert.DROPOFF: - console.assert(type != InsertionType.PREPEND, 'Trying to Prepend a Dropoff event.'); return durations.approach; case ToInsert.BOTH: return durations.pickupToDropoff; @@ -186,7 +184,16 @@ export function computeTravelDurations( busStopTimes: Interval[][], busStopCompanyFilter: boolean[][] ): Insertion[][] { - const allInsertions = new Array(); + let insertionCount=0; + [...possibleInsertionsByVehicle].forEach(([v, ranges]) => { + ranges.forEach(range => { + insertionCount+=range.latestDropoff+1-range.earliestPickup; + }); + }); + const allInsertions = new Array(insertionCount); + for(let i=0;i!=allInsertions.length;++i){ + allInsertions[i] = new Array(3); + } const insertionFn = ( busStopIdx: number | undefined, insertionInfo: InsertionInfo, @@ -230,6 +237,15 @@ export function computeTravelDurations( : next.communicated; const fullWindowDuration = nextTime.getTime() - prevTime.getTime(); let window: Interval | undefined = new Interval(prevTime, nextTime); + console.log(insertionInfo.vehicle.events.length); + console.log("aa", insertionInfo.nextEventIdx); + console.log(next==undefined); + console.log(insertionInfo.insertionIdx); + console.log(insertionInfo.insertionIdx == insertionInfo.vehicle.events.length); + const toCompanyFromUserChosen = routingResults.userChosen.toCompany[insertionInfo.companyIdx].duration; + const fromCompanyToUserChosen = routingResults.userChosen.fromCompany[insertionInfo.companyIdx].duration; + const fromPrevToUserChosen = prev==undefined?0:routingResults.userChosen.fromPrevEvent[insertionInfo.prevEventIdx].duration; + const toNextFromUserChosen = next==undefined?0:routingResults.userChosen.toNextEvent[insertionInfo.nextEventIdx].duration; if (busStopIdx == undefined) { // insert userChosen if (type == (startFixed ? InsertionType.APPEND : InsertionType.PREPEND)) { @@ -249,18 +265,18 @@ export function computeTravelDurations( window, { approach: comesFromCompany - ? routingResults.userChosen.fromCompany[insertionInfo.companyIdx].duration - : routingResults.userChosen.fromPrevEvent[insertionInfo.prevEventIdx].duration, + ? fromCompanyToUserChosen + : fromPrevToUserChosen, return: returnsToCompany - ? routingResults.userChosen.toCompany[insertionInfo.companyIdx].duration - : routingResults.userChosen.toNextEvent[insertionInfo.nextEventIdx].duration, + ? toCompanyFromUserChosen + : toNextFromUserChosen, pickupToDropoff: 0, toNext: next == undefined ? 0 : next.durationFromPrev, fromPrev: prev == undefined ? 0 : prev.durationToNext, fullWindow: fullWindowDuration }, toInsert, - allInsertions[insertionInfo.insertionIdx][toInsert].userChosen, + allInsertions[insertionInfo.insertionIdx][toInsert]==undefined?undefined:allInsertions[insertionInfo.insertionIdx][toInsert].userChosen, undefined, startFixed ); @@ -296,48 +312,40 @@ export function computeTravelDurations( const travelDuration = travelDurations[busStopIdx]; const times = busStopTimes[busStopIdx]; for (let timeIdx = 0; timeIdx != times.length; ++timeIdx) { + const fromCompanyToBus = busStopRoutingResult.fromCompany[insertionInfo.companyIdx].duration; + const toCompanyFromBus = busStopRoutingResult.toCompany[insertionInfo.companyIdx].duration; + const fromPrevToBus = busStopRoutingResult.fromPrevEvent[insertionInfo.prevEventIdx].duration; + const toNextFromBus = routingResults.userChosen.toNextEvent[insertionInfo.nextEventIdx].duration; // insert userChosen coordinates and busstop - const approachDurationBoth = - (startFixed - ? comesFromCompany - ? busStopRoutingResult.fromCompany[insertionInfo.companyIdx].duration - : busStopRoutingResult.fromPrevEvent[insertionInfo.prevEventIdx].duration - : comesFromCompany - ? routingResults.userChosen.fromCompany[insertionInfo.companyIdx].duration - : routingResults.userChosen.fromPrevEvent[insertionInfo.prevEventIdx].duration) + - travelDuration; - const returnDurationBoth = - (startFixed - ? returnsToCompany - ? routingResults.userChosen.toCompany[insertionInfo.nextEventIdx].duration - : routingResults.userChosen.toNextEvent[insertionInfo.nextEventIdx].duration - : returnsToCompany - ? busStopRoutingResult.toCompany[insertionInfo.companyIdx].duration - : busStopRoutingResult.toNextEvent[insertionInfo.nextEventIdx].duration) + - travelDuration; - const bestTimeBoth = getBestTime( - approachDurationBoth, - returnDurationBoth, - window, - times[timeIdx], - startFixed - ); - if (bestTimeBoth == undefined) { - continue; - } const timeCost = evaluateInsertion( type, window, { - approach: approachDurationBoth, - return: returnDurationBoth, + approach: + (startFixed + ? comesFromCompany + ? fromCompanyToBus + : fromPrevToBus + : returnsToCompany + ? toCompanyFromUserChosen + : toNextFromUserChosen) + + travelDuration, + return: + (startFixed + ? returnsToCompany + ? toCompanyFromUserChosen + : toNextFromUserChosen + : returnsToCompany + ? toCompanyFromBus + : toNextFromBus) + + travelDuration, pickupToDropoff: travelDuration, toNext: next == undefined ? 0 : next.durationFromPrev, fromPrev: prev == undefined ? 0 : prev.durationToNext, fullWindow: fullWindowDuration }, ToInsert.BOTH, - allInsertions[insertionInfo.insertionIdx][ToInsert.BOTH].both[busStopIdx][timeIdx], + allInsertions[insertionInfo.insertionIdx][ToInsert.BOTH]==undefined?undefined:allInsertions[insertionInfo.insertionIdx][ToInsert.BOTH].both[busStopIdx][timeIdx], times[timeIdx], startFixed ); @@ -347,45 +355,34 @@ export function computeTravelDurations( allInsertions[insertionInfo.insertionIdx][ToInsert.BOTH].both[busStopIdx][timeIdx] = timeCost; + // insert busstop if (type == (startFixed ? InsertionType.PREPEND : InsertionType.APPEND)) { return; } - // insert busstop - const approachDuration = comesFromCompany - ? busStopRoutingResult.fromCompany[insertionInfo.companyIdx].duration - : busStopRoutingResult.fromPrevEvent[insertionInfo.prevEventIdx].duration; - const returnDuration = returnsToCompany - ? busStopRoutingResult.toCompany[insertionInfo.companyIdx].duration - : busStopRoutingResult.toNextEvent[insertionInfo.nextEventIdx].duration; - const bestTime = getBestTime( - approachDuration, - returnDuration, + const toInsert = startFixed ? ToInsert.PICKUP : ToInsert.DROPOFF; + const timeCostBus = evaluateInsertion( + type, window, + { + approach: comesFromCompany + ? fromCompanyToBus + : fromPrevToBus, + return: returnsToCompany + ? toCompanyFromBus + : toNextFromBus, + pickupToDropoff: travelDuration, + toNext: next == undefined ? 0 : next.durationFromPrev, + fromPrev: prev == undefined ? 0 : prev.durationToNext, + fullWindow: fullWindowDuration + }, + toInsert, + allInsertions[insertionInfo.insertionIdx][toInsert].both[busStopIdx][timeIdx], times[timeIdx], startFixed ); - if (bestTime != undefined) { - const toInsert = startFixed ? ToInsert.PICKUP : ToInsert.DROPOFF; - const timeCost = evaluateInsertion( - type, - window, - { - approach: approachDuration, - return: returnDuration, - pickupToDropoff: travelDuration, - toNext: next == undefined ? 0 : next.durationFromPrev, - fromPrev: prev == undefined ? 0 : prev.durationToNext, - fullWindow: fullWindowDuration - }, - toInsert, - allInsertions[insertionInfo.insertionIdx][toInsert].both[busStopIdx][timeIdx], - times[timeIdx], - startFixed - ); - if (timeCost != undefined) { - allInsertions[insertionInfo.insertionIdx][toInsert].busStops[busStopIdx][timeIdx] = - timeCost; - } + if (timeCostBus != undefined) { + allInsertions[insertionInfo.insertionIdx][toInsert].busStops[busStopIdx][timeIdx] = + timeCostBus; } } }); diff --git a/src/routes/api/whitelist/utils.ts b/src/routes/api/whitelist/utils.ts index 1f90b429..cea5dcb8 100644 --- a/src/routes/api/whitelist/utils.ts +++ b/src/routes/api/whitelist/utils.ts @@ -20,10 +20,10 @@ export function iterateAllInsertions( ) { const iterateInsertions = ( companyFilter: boolean[] | undefined, - busStopIdx: number | undefined, - prevEventIdx: number, - nextEventIdx: number + busStopIdx: number | undefined ) => { + let prevEventIdx=0; + let nextEventIdx=0; companies.forEach((company, companyIdx) => { if (companyFilter != undefined && !companyFilter[companyIdx]) { return; @@ -64,13 +64,9 @@ export function iterateAllInsertions( }); }); }; - let prevEventPos = companies.length; - let nextEventPos = companies.length; - iterateInsertions(undefined, undefined, prevEventPos, nextEventPos); + iterateInsertions(undefined, undefined); busStopCompanyFilter.forEach((companyFilter, busStopIdx) => { const companyCount = companyFilter.filter((f) => f).length; - prevEventPos = companyCount; - nextEventPos = companyCount; - iterateInsertions(companyFilter, busStopIdx, prevEventPos, nextEventPos); + iterateInsertions(companyFilter, busStopIdx); }); } From 1806534c7553924b5de80a26231c951f7367546a Mon Sep 17 00:00:00 2001 From: nils Date: Fri, 27 Sep 2024 04:43:46 +0200 Subject: [PATCH 11/12] wip --- src/routes/api/whitelist/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/api/whitelist/utils.ts b/src/routes/api/whitelist/utils.ts index cea5dcb8..34dd332b 100644 --- a/src/routes/api/whitelist/utils.ts +++ b/src/routes/api/whitelist/utils.ts @@ -1,4 +1,4 @@ -import type { Company, Vehicle } from '$lib/compositionTypes'; +import type { Company } from '$lib/compositionTypes'; import type { Range } from './capacitySimulation'; import type { InsertionInfo } from './insertionTypes'; From e4111c023555bfeee155f5ba6d1ccc714c7c6dce Mon Sep 17 00:00:00 2001 From: nils Date: Fri, 27 Sep 2024 11:23:34 +0200 Subject: [PATCH 12/12] wip --- .../evaluateSingleInsertions.test.ts | 13 +- src/routes/api/whitelist/insertionTypes.ts | 6 +- src/routes/api/whitelist/insertions.ts | 146 +++++++++++------- 3 files changed, 100 insertions(+), 65 deletions(-) diff --git a/src/routes/api/whitelist/evaluateSingleInsertions.test.ts b/src/routes/api/whitelist/evaluateSingleInsertions.test.ts index 5910dd70..e5dadc67 100644 --- a/src/routes/api/whitelist/evaluateSingleInsertions.test.ts +++ b/src/routes/api/whitelist/evaluateSingleInsertions.test.ts @@ -31,7 +31,7 @@ const createVehicle = (id: number, events: Event[]): Vehicle => { }; }; -const createEvent = (coordinates: Coordinates): Event => { +const createEvent = (coordinates: Coordinates, time: number): Event => { return { capacities: { passengers: 0, bikes: 0, wheelchairs: 0, luggage: 0 }, is_pickup: true, @@ -39,7 +39,7 @@ const createEvent = (coordinates: Coordinates): Event => { id: 1, coordinates, tourId: 1, - communicated: createDate(6), + communicated: createDate(time), arrival: createDate(6), departure: createDate(3), durationFromPrev: 0, @@ -55,14 +55,15 @@ describe('compute Intervals for single insertions test', () => { createCompany( [ createVehicle(1, [ - createEvent(new Coordinates(eventLatLng, eventLatLng++)), - createEvent(new Coordinates(eventLatLng, eventLatLng++)), - createEvent(new Coordinates(eventLatLng, eventLatLng++)) + createEvent(new Coordinates(eventLatLng, eventLatLng++), 4), + createEvent(new Coordinates(eventLatLng, eventLatLng++),5), + createEvent(new Coordinates(eventLatLng, eventLatLng++),6) ]) ], new Coordinates(companyLatLng, companyLatLng++) ) ]; + companies[0].vehicles.forEach((v)=>v.events.forEach((e)=>{e.arrival=createDate(2);e.departure=createDate(7);})); const insertions = new Map(); insertions.set(1, [{ earliestPickup: 0, latestDropoff: 3 }]); const travelDurations = [50]; @@ -108,7 +109,7 @@ describe('compute Intervals for single insertions test', () => { busStopTimes, [[true]] ); - console.log(result); + console.log("res", result); expect(1).toBe(1); }); }); diff --git a/src/routes/api/whitelist/insertionTypes.ts b/src/routes/api/whitelist/insertionTypes.ts index 8de90b8f..2b34ac92 100644 --- a/src/routes/api/whitelist/insertionTypes.ts +++ b/src/routes/api/whitelist/insertionTypes.ts @@ -9,9 +9,9 @@ export type InsertionInfo = { }; export type InsertionEvaluation = { - userChosen: TimeCost; - busStops: TimeCost[][]; - both: TimeCost[][]; + userChosen: TimeCost|undefined; + busStops: TimeCost|undefined[][]; + both: TimeCost|undefined[][]; approachDuration: number | undefined; returnDuration: number | undefined; company: number; diff --git a/src/routes/api/whitelist/insertions.ts b/src/routes/api/whitelist/insertions.ts index f063fd1c..620d133b 100644 --- a/src/routes/api/whitelist/insertions.ts +++ b/src/routes/api/whitelist/insertions.ts @@ -12,7 +12,6 @@ import { TAXI_WAITING_TIME_COST_FACTOR } from '$lib/constants'; import type { InsertionInfo } from './insertionTypes'; -import { Vehicle } from '../../(user)/taxi/types'; export enum InsertionType { CONNECT, @@ -44,20 +43,20 @@ type Cost = { type TimeCost = { time: Date; cost: Cost; + approachDuration: number; + returnDuration: number; }; type Insertion = { - userChosen: TimeCost; + userChosen: TimeCost|undefined; busStops: TimeCost[][]; both: TimeCost[][]; - approachDuration: number | undefined; - returnDuration: number | undefined; company: number; vehicle: number; - tour1: number; - tour2: number; - event1: number; - event2: number; + tour1: number|undefined; + tour2: number|undefined; + event1: number|undefined; + event2: number|undefined; }; export type InsertionDurations = { @@ -91,10 +90,16 @@ export function evaluateInsertion( startFixed: boolean ): TimeCost | undefined { const fullDuration = durations.approach + durations.return + durations.pickupToDropoff; + console.log("window", window); + console.log(fullDuration); + console.log(window.getDurationMs()); let arrivalWindow = window.getDurationMs() < fullDuration ? undefined : window.shrink(durations.approach, durations.return); + if (arrivalWindow == undefined) { + return undefined; + } if (busStopWindow != undefined) { arrivalWindow = arrivalWindow?.intersect(busStopWindow); } @@ -102,12 +107,14 @@ export function evaluateInsertion( return undefined; } const cost = computeCosts(type, durations, toInsert); - if (currentBest == undefined || currentBest.cost.total <= cost.total) { + if (currentBest != undefined && currentBest.cost.total <= cost.total) { return undefined; } return { time: startFixed ? arrivalWindow.startTime : arrivalWindow.endTime, - cost + cost, + approachDuration: durations.approach, + returnDuration: durations.return }; } @@ -183,17 +190,14 @@ export function computeTravelDurations( startFixed: boolean, busStopTimes: Interval[][], busStopCompanyFilter: boolean[][] -): Insertion[][] { +): Insertion[] { let insertionCount=0; [...possibleInsertionsByVehicle].forEach(([v, ranges]) => { ranges.forEach(range => { insertionCount+=range.latestDropoff+1-range.earliestPickup; }); }); - const allInsertions = new Array(insertionCount); - for(let i=0;i!=allInsertions.length;++i){ - allInsertions[i] = new Array(3); - } + const allInsertions = new Array(insertionCount); const insertionFn = ( busStopIdx: number | undefined, insertionInfo: InsertionInfo, @@ -229,6 +233,25 @@ export function computeTravelDurations( if (prevTime < new Date(Date.now() + minutesToMs(MIN_PREP_MINUTES))) { return; } + if(allInsertions[insertionInfo.insertionIdx]==undefined){ + const busStops = new Array(busStopTimes.length); + const both = new Array(busStopTimes.length); + for(let i=0;i!=busStops.length;++i){ + busStops[i]=new Array(busStopTimes[i].length); + both[i]=new Array(busStopTimes[i].length); + } + allInsertions[insertionInfo.insertionIdx] = { + userChosen: undefined, + busStops, + both, + company: companies[insertionInfo.companyIdx].id, + vehicle: insertionInfo.vehicle.id, + tour1: prev==undefined?undefined:prev.tourId, + tour2: next==undefined?undefined:next.tourId, + event1: prev==undefined?undefined:prev.id, + event2: next==undefined?undefined:next.id + } + } const nextTime = next == undefined ? new Date(Date.now() + hoursToMs(240000)) @@ -252,38 +275,45 @@ export function computeTravelDurations( return; } console.assert( - insertionInfo.prevEventIdx != 0 || comesFromCompany, + insertionInfo.insertionIdx != 0 || comesFromCompany, 'Accessing nonexistin previous event.' ); console.assert( - insertionInfo.nextEventIdx != insertionInfo.vehicle.events.length || returnsToCompany, + insertionInfo.insertionIdx != insertionInfo.vehicle.events.length || returnsToCompany, 'Accessing nonexisting next event.' ); const toInsert = startFixed ? ToInsert.DROPOFF : ToInsert.PICKUP; + const entryExists = allInsertions[insertionInfo.insertionIdx].userChosen!=undefined; + const approachDuration = comesFromCompany + ? fromCompanyToUserChosen + : fromPrevToUserChosen; + const returnDuration = returnsToCompany + ? toCompanyFromUserChosen + : toNextFromUserChosen; const cost = evaluateInsertion( type, window, { - approach: comesFromCompany - ? fromCompanyToUserChosen - : fromPrevToUserChosen, - return: returnsToCompany - ? toCompanyFromUserChosen - : toNextFromUserChosen, + approach:approachDuration, + return:returnDuration, pickupToDropoff: 0, toNext: next == undefined ? 0 : next.durationFromPrev, fromPrev: prev == undefined ? 0 : prev.durationToNext, fullWindow: fullWindowDuration }, toInsert, - allInsertions[insertionInfo.insertionIdx][toInsert]==undefined?undefined:allInsertions[insertionInfo.insertionIdx][toInsert].userChosen, + allInsertions[insertionInfo.insertionIdx].userChosen, undefined, startFixed ); + console.log("cost", cost); if (cost == undefined) { return; } - allInsertions[insertionInfo.insertionIdx][toInsert].userChosen = cost; + allInsertions[insertionInfo.insertionIdx] = { + ...allInsertions[insertionInfo.insertionIdx], + userChosen: cost + }; return; } if (type != InsertionType.INSERT) { @@ -317,71 +347,75 @@ export function computeTravelDurations( const fromPrevToBus = busStopRoutingResult.fromPrevEvent[insertionInfo.prevEventIdx].duration; const toNextFromBus = routingResults.userChosen.toNextEvent[insertionInfo.nextEventIdx].duration; // insert userChosen coordinates and busstop - const timeCost = evaluateInsertion( + const approachDurationBoth = + (startFixed + ? comesFromCompany + ? fromCompanyToBus + : fromPrevToBus + : returnsToCompany + ? toCompanyFromUserChosen + : toNextFromUserChosen) + + travelDuration; + const returnDurationBoth = + (startFixed + ? returnsToCompany + ? toCompanyFromUserChosen + : toNextFromUserChosen + : returnsToCompany + ? toCompanyFromBus + : toNextFromBus) + + travelDuration; + const timeCostBoth = evaluateInsertion( type, window, { - approach: - (startFixed - ? comesFromCompany - ? fromCompanyToBus - : fromPrevToBus - : returnsToCompany - ? toCompanyFromUserChosen - : toNextFromUserChosen) + - travelDuration, - return: - (startFixed - ? returnsToCompany - ? toCompanyFromUserChosen - : toNextFromUserChosen - : returnsToCompany - ? toCompanyFromBus - : toNextFromBus) + - travelDuration, + approach:approachDurationBoth, + return: returnDurationBoth, pickupToDropoff: travelDuration, toNext: next == undefined ? 0 : next.durationFromPrev, fromPrev: prev == undefined ? 0 : prev.durationToNext, fullWindow: fullWindowDuration }, ToInsert.BOTH, - allInsertions[insertionInfo.insertionIdx][ToInsert.BOTH]==undefined?undefined:allInsertions[insertionInfo.insertionIdx][ToInsert.BOTH].both[busStopIdx][timeIdx], + allInsertions[insertionInfo.insertionIdx].both[busStopIdx][timeIdx], times[timeIdx], startFixed ); - if (timeCost == undefined) { + if (timeCostBoth == undefined) { continue; } - allInsertions[insertionInfo.insertionIdx][ToInsert.BOTH].both[busStopIdx][timeIdx] = - timeCost; + allInsertions[insertionInfo.insertionIdx].both![busStopIdx][timeIdx] = + timeCostBoth; // insert busstop if (type == (startFixed ? InsertionType.PREPEND : InsertionType.APPEND)) { return; } const toInsert = startFixed ? ToInsert.PICKUP : ToInsert.DROPOFF; + const approachDuration = comesFromCompany + ? fromCompanyToBus + : fromPrevToBus; + const returnDuration = returnsToCompany + ? toCompanyFromBus + : toNextFromBus; const timeCostBus = evaluateInsertion( type, window, { - approach: comesFromCompany - ? fromCompanyToBus - : fromPrevToBus, - return: returnsToCompany - ? toCompanyFromBus - : toNextFromBus, + approach: approachDuration, + return: returnDuration, pickupToDropoff: travelDuration, toNext: next == undefined ? 0 : next.durationFromPrev, fromPrev: prev == undefined ? 0 : prev.durationToNext, fullWindow: fullWindowDuration }, toInsert, - allInsertions[insertionInfo.insertionIdx][toInsert].both[busStopIdx][timeIdx], + allInsertions[insertionInfo.insertionIdx].both[busStopIdx][timeIdx], times[timeIdx], startFixed ); if (timeCostBus != undefined) { - allInsertions[insertionInfo.insertionIdx][toInsert].busStops[busStopIdx][timeIdx] = + allInsertions[insertionInfo.insertionIdx].busStops[busStopIdx][timeIdx] = timeCostBus; } }