Skip to content

Commit

Permalink
fix: fix O/D Matrix for unordered trainruns (#321)
Browse files Browse the repository at this point in the history
  • Loading branch information
shenriotpro authored Oct 24, 2024
1 parent d170955 commit 3d9644f
Show file tree
Hide file tree
Showing 6 changed files with 466 additions and 70 deletions.
59 changes: 33 additions & 26 deletions src/app/services/data/trainrun.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,18 +344,26 @@ describe("TrainrunService", () => {
trainrunService.splitTrainrunIntoTwoParts(n.getTransition(0));

const trainrunSections2 =
trainrunSectionService.getAllTrainrunSectionsForTrainrun(ts0.getTrainrunId());
trainrunSectionService.getAllTrainrunSectionsForTrainrun(
ts0.getTrainrunId(),
);
expect(trainrunSections2.length).toBe(1);
const trainrunSections3 =
trainrunSectionService.getAllTrainrunSectionsForTrainrun(ts1.getTrainrunId());
trainrunSectionService.getAllTrainrunSectionsForTrainrun(
ts1.getTrainrunId(),
);
expect(trainrunSections3.length).toBe(1);

trainrunService.combineTwoTrainruns(n,
trainrunService.combineTwoTrainruns(
n,
n.getPortOfTrainrunSection(0),
n.getPortOfTrainrunSection(1));
n.getPortOfTrainrunSection(1),
);

const trainrunSections4 =
trainrunSectionService.getAllTrainrunSectionsForTrainrun(ts1.getTrainrunId());
trainrunSectionService.getAllTrainrunSectionsForTrainrun(
ts1.getTrainrunId(),
);
expect(trainrunSections4.length).toBe(2);
});

Expand All @@ -366,35 +374,34 @@ describe("TrainrunService", () => {

const iterators = trainrunService.getRootIterators();

const iterators0 = iterators.get(0);
// trainrun 0: 0 -> 1 -> 2
// 1 root: 0
expect(iterators0.length).toBe(1);
expect(iterators0[0].current().trainrunSection.getSourceNodeId()).toBe(0);
// root: 0
expect(iterators.get(0).current().trainrunSection.getSourceNodeId()).toBe(
0,
);

const iterators1 = iterators.get(1);
// trainrun 1: 1 -> 2
// 1 root: 1
expect(iterators1.length).toBe(1);
expect(iterators1[0].current().trainrunSection.getSourceNodeId()).toBe(1);
// root: 1
expect(iterators.get(1).current().trainrunSection.getSourceNodeId()).toBe(
1,
);

const iterators2 = iterators.get(2);
// trainrun 2: 0 -> 1 -> 2 -> 3
// 1 root: 0
expect(iterators2.length).toBe(1);
expect(iterators2[0].current().trainrunSection.getSourceNodeId()).toBe(0);
// root: 0
expect(iterators.get(2).current().trainrunSection.getSourceNodeId()).toBe(
0,
);

const iterators3 = iterators.get(3);
// trainrun 3: 4 -> 2
// 1 root: 4
expect(iterators3.length).toBe(1);
expect(iterators3[0].current().trainrunSection.getSourceNodeId()).toBe(4);
// root: 4
expect(iterators.get(3).current().trainrunSection.getSourceNodeId()).toBe(
4,
);

const iterators4 = iterators.get(4);
// trainrun 4: 4 -> 2
// 1 root: 4
expect(iterators4.length).toBe(1);
expect(iterators4[0].current().trainrunSection.getSourceNodeId()).toBe(4);
// root: 4
expect(iterators.get(4).current().trainrunSection.getSourceNodeId()).toBe(
4,
);
});

});
29 changes: 20 additions & 9 deletions src/app/services/data/trainrun.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -773,18 +773,29 @@ export class TrainrunService {
return new NonStopTrainrunIterator(this.logService, node, trainrunSection);
}

// For each trainrun, get iterators from the source (trainruns may be split).
public getRootIterators(): Map<number, TrainrunIterator[]> {
// For each trainrun, get iterator from the smallest consecutiveTime.
public getRootIterators(): Map<number, TrainrunIterator> {
const trainrunSections = this.trainrunSectionService.getTrainrunSections();
const iterators = new Map<number, TrainrunIterator[]>();
const iterators = new Map<number, TrainrunIterator>();
const consecutiveTimes = new Map<number, number>();
trainrunSections.forEach((ts) => {
const node = ts.getSourceNode();
const trainrunId = ts.getTrainrunId();
let node = ts.getSourceNode();
if (node.isEndNode(ts)) {
const it = iterators.get(ts.getTrainrunId());
if (it === undefined) {
iterators.set(ts.getTrainrunId(), [this.getIterator(node, ts)]);
} else {
it.push(this.getIterator(node, ts));
const it = iterators.get(trainrunId);
const consecutiveTime = ts.getSourceDepartureDto().consecutiveTime;
if (it === undefined || consecutiveTimes.get(trainrunId) > consecutiveTime) {
iterators.set(trainrunId, this.getIterator(node, ts));
consecutiveTimes.set(trainrunId, consecutiveTime);
}
}
node = ts.getTargetNode();
if (node.isEndNode(ts)) {
const it = iterators.get(trainrunId);
const consecutiveTime = ts.getTargetDepartureDto().consecutiveTime;
if (it === undefined || consecutiveTimes.get(trainrunId) > consecutiveTime) {
iterators.set(trainrunId, this.getIterator(node, ts));
consecutiveTimes.set(trainrunId, consecutiveTime);
}
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -497,8 +497,7 @@ export class EditorToolsViewComponent {
return this.buildCSVString(headers, rows);
}

// Split trainruns are not supported at the moment:
// https://github.com/SchweizerischeBundesbahnen/netzgrafik-editor-frontend/issues/285
// TODO: this may be incorrect for trainruns going through the same node several times.
private convertToOriginDestinationCSV(): string {
// Duration of the schedule to consider (in minutes).
// TODO: ideally this would be 24 hours, but performance is a concern.
Expand Down
44 changes: 22 additions & 22 deletions src/app/view/util/origin-destination-graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,29 +220,26 @@ const buildSectionEdges = (
const edges = [];
const its = trainrunService.getRootIterators();
trainruns.forEach((trainrun) => {
const tsIterators = its.get(trainrun.getId());
if (tsIterators === undefined) {
const tsIterator = its.get(trainrun.getId());
if (tsIterator === undefined) {
console.log("Ignoring trainrun (no root found): ", trainrun.getId());
return;
}
tsIterators.forEach((tsIterator) => {
edges.push(
...buildSectionEdgesFromIterator(tsIterator, false, timeLimit),
);
// Don't forget the reverse direction.
const ts = tsIterator.current().trainrunSection;
const nextIterator = trainrunService.getIterator(ts.getTargetNode(), ts);
edges.push(
...buildSectionEdgesFromIterator(nextIterator, true, timeLimit),
);
});
edges.push(...buildSectionEdgesFromIterator(tsIterator, false, timeLimit));
// Don't forget the reverse direction.
const ts = tsIterator.current().trainrunSection;
const nextIterator = trainrunService.getIterator(
tsIterator.current().node,
ts,
);
edges.push(...buildSectionEdgesFromIterator(nextIterator, true, timeLimit));
});
return edges;
};

const buildSectionEdgesFromIterator = (
tsIterator: TrainrunIterator,
reverse: boolean,
reverseIterator: boolean,
timeLimit: number,
): Edge[] => {
const edges = [];
Expand All @@ -251,14 +248,18 @@ const buildSectionEdgesFromIterator = (
while (tsIterator.hasNext()) {
tsIterator.next();
const ts = tsIterator.current().trainrunSection;
const trainrunId = reverse ? -ts.getTrainrunId() : ts.getTrainrunId();
const v1Time = reverse
const trainrunId = reverseIterator
? -ts.getTrainrunId()
: ts.getTrainrunId();
const reverseSection =
tsIterator.current().node.getId() !== ts.getTargetNodeId();
const v1Time = reverseSection
? ts.getTargetDepartureDto().consecutiveTime
: ts.getSourceDepartureDto().consecutiveTime;
const v1Node = reverse ? ts.getTargetNodeId() : ts.getSourceNodeId();
const v1Node = reverseSection ? ts.getTargetNodeId() : ts.getSourceNodeId();
// If we don't stop here, we need to remember where we started.
if (
reverse
reverseSection
? ts.getSourceNode().isNonStop(ts)
: ts.getTargetNode().isNonStop(ts)
) {
Expand All @@ -269,16 +270,15 @@ const buildSectionEdgesFromIterator = (
continue;
}
let v1 = new Vertex(v1Node, true, v1Time, trainrunId);
let nonStop = false;
// If we didn't stop previously, we need to use the stored start.
if (nonStopV1Time !== -1) {
v1 = new Vertex(nonStopV1Node, true, nonStopV1Time, trainrunId);
nonStopV1Time = -1;
nonStop = true;
}
const v2Time = reverse
const v2Time = reverseSection
? ts.getSourceArrivalDto().consecutiveTime
: ts.getTargetArrivalDto().consecutiveTime;
const v2Node = reverse ? ts.getSourceNodeId() : ts.getTargetNodeId();
const v2Node = reverseSection ? ts.getSourceNodeId() : ts.getTargetNodeId();
const v2 = new Vertex(v2Node, false, v2Time, trainrunId);

for (let i = 0; i * ts.getTrainrun().getFrequency() < timeLimit; i++) {
Expand Down
Loading

0 comments on commit 3d9644f

Please sign in to comment.