From 38c5fa87e0ede5b9a0a1d5ef8f0af947585b54ce Mon Sep 17 00:00:00 2001 From: u229351 Date: Thu, 12 Sep 2024 14:19:08 +0200 Subject: [PATCH] fix pseudo network generator and add test --- .../matsim/pt/utils/CreatePseudoNetwork.java | 137 ++++++---------- .../CreatePseudoNetworkWithLoopLinks.java | 153 ------------------ .../utils/TransitScheduleValidatorTest.java | 14 ++ 3 files changed, 63 insertions(+), 241 deletions(-) delete mode 100644 matsim/src/main/java/org/matsim/pt/utils/CreatePseudoNetworkWithLoopLinks.java diff --git a/matsim/src/main/java/org/matsim/pt/utils/CreatePseudoNetwork.java b/matsim/src/main/java/org/matsim/pt/utils/CreatePseudoNetwork.java index 906c97396ee..f584e788b3d 100644 --- a/matsim/src/main/java/org/matsim/pt/utils/CreatePseudoNetwork.java +++ b/matsim/src/main/java/org/matsim/pt/utils/CreatePseudoNetwork.java @@ -20,14 +20,7 @@ package org.matsim.pt.utils; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - +import com.google.common.annotations.Beta; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.TransportMode; import org.matsim.api.core.v01.network.Link; @@ -37,23 +30,21 @@ import org.matsim.core.population.routes.RouteUtils; import org.matsim.core.utils.collections.Tuple; import org.matsim.core.utils.geometry.CoordUtils; -import org.matsim.pt.transitSchedule.api.TransitLine; -import org.matsim.pt.transitSchedule.api.TransitRoute; -import org.matsim.pt.transitSchedule.api.TransitRouteStop; -import org.matsim.pt.transitSchedule.api.TransitSchedule; -import org.matsim.pt.transitSchedule.api.TransitStopArea; -import org.matsim.pt.transitSchedule.api.TransitStopFacility; +import org.matsim.pt.transitSchedule.api.*; + +import java.util.*; /** * Builds a network where transit vehicles can drive along and assigns the correct - * links to the transit stop facilities and routes of transit lines. As each transit - * stop facility can only be connected to at most one link, the algorithm is forced - * to duplicated transit stop facilities in certain cases to build the network. + * links to the transit stop facilities and routes of transit lines. Each stop facility + * is assigned to a loop link, located in a node with the same coordinates as the stop. + * The stop facility ID is used for node and link IDs. * - * See {@link CreatePseudoNetworkWithLoopLinks} for a version that uses loop links instead of duplicating stop facilities. + * @author mrieser, davibicudo * - * @author mrieser + * @implNote THis functionality might be merged with {@link CreatePseudoNetwork}. */ +@Beta public class CreatePseudoNetwork { private final TransitSchedule schedule; @@ -62,13 +53,8 @@ public class CreatePseudoNetwork { private final double linkFreeSpeed; private final double linkCapacity; - - private final Map, Link> links = new HashMap, Link>(); - private final Map, TransitStopFacility> stopFacilities = new HashMap, TransitStopFacility>(); - private final Map nodes = new HashMap(); - private final Map> facilityCopies = new HashMap>(); - - private long linkIdCounter = 0; + private final Map links = new HashMap(); + private final Map nodes = new HashMap<>(); private final Set transitModes = Collections.singleton(TransportMode.pt); @@ -81,7 +67,7 @@ public CreatePseudoNetwork(final TransitSchedule schedule, final Network network } public CreatePseudoNetwork(final TransitSchedule schedule, final Network network, final String networkIdPrefix, - final double linkFreeSpeed, final double linkCapacity) { + final double linkFreeSpeed, final double linkCapacity) { this.schedule = schedule; this.network = network; this.prefix = networkIdPrefix; @@ -91,24 +77,29 @@ public CreatePseudoNetwork(final TransitSchedule schedule, final Network network public void createNetwork() { - List> toBeRemoved = new LinkedList>(); + createStopNodesAndLoopLinks(); + List> toBeRemoved = new LinkedList<>(); for (TransitLine tLine : this.schedule.getTransitLines().values()) { for (TransitRoute tRoute : tLine.getRoutes().values()) { - ArrayList> routeLinks = new ArrayList>(); + ArrayList> routeLinks = new ArrayList<>(); TransitRouteStop prevStop = null; for (TransitRouteStop stop : tRoute.getStops()) { - Link link = getNetworkLink(prevStop, stop); - routeLinks.add(link.getId()); + if (prevStop != null) { + Link link = getNetworkLink(prevStop.getStopFacility(), stop.getStopFacility()); + routeLinks.add(link.getId()); + } + Link loopLink = getNetworkLink(stop.getStopFacility(), stop.getStopFacility()); + routeLinks.add(loopLink.getId()); prevStop = stop; } - if (routeLinks.size() > 0) { - NetworkRoute route = RouteUtils.createNetworkRoute(routeLinks ); + if (!routeLinks.isEmpty()) { + NetworkRoute route = RouteUtils.createNetworkRoute(routeLinks); tRoute.setRoute(route); } else { System.err.println("Line " + tLine.getId() + " route " + tRoute.getId() + " has less than two stops. Removing this route from schedule."); - toBeRemoved.add(new Tuple(tLine, tRoute)); + toBeRemoved.add(new Tuple<>(tLine, tRoute)); } } } @@ -118,64 +109,39 @@ public void createNetwork() { } } - private Link getNetworkLink(final TransitRouteStop fromStop, final TransitRouteStop toStop) { - TransitStopFacility fromFacility = (fromStop == null) ? toStop.getStopFacility() : fromStop.getStopFacility(); - TransitStopFacility toFacility = toStop.getStopFacility(); - - Node fromNode = this.nodes.get(fromFacility); - if (fromNode == null) { - fromNode = this.network.getFactory().createNode(Id.create(this.prefix + toFacility.getId(), Node.class), fromFacility.getCoord()); - this.network.addNode(fromNode); - this.nodes.put(toFacility, fromNode); + private void createStopNodesAndLoopLinks() { + for (TransitStopFacility stop : this.schedule.getFacilities().values()) { + Node node = this.network.getFactory().createNode(Id.createNodeId(this.prefix + stop.getId()), stop.getCoord()); + this.network.addNode(node); + this.nodes.put(stop, node); + Link loopLink = getNetworkLink(stop, stop); + stop.setLinkId(loopLink.getId()); } + } + private Link getNetworkLink(final TransitStopFacility fromFacility, final TransitStopFacility toFacility) { + Node fromNode = this.nodes.get(fromFacility); Node toNode = this.nodes.get(toFacility); - if (toNode == null) { - toNode = this.network.getFactory().createNode(Id.create(this.prefix + toFacility.getId(), Node.class), toFacility.getCoord()); - this.network.addNode(toNode); - this.nodes.put(toFacility, toNode); - } - Tuple connection = new Tuple(fromNode, toNode); + Connection connection = new Connection(fromNode, toNode); Link link = this.links.get(connection); - if (link == null) { - link = createAndAddLink(fromNode, toNode, connection); - - if (toFacility.getLinkId() == null) { - toFacility.setLinkId(link.getId()); - this.stopFacilities.put(connection, toFacility); - } else { - List copies = this.facilityCopies.get(toFacility); - if (copies == null) { - copies = new ArrayList(); - this.facilityCopies.put(toFacility, copies); - } - Id newId = Id.create(toFacility.getId().toString() + "." + Integer.toString(copies.size() + 1), TransitStopFacility.class); - TransitStopFacility newFacility = this.schedule.getFactory().createTransitStopFacility(newId, toFacility.getCoord(), toFacility.getIsBlockingLane()); - newFacility.setStopAreaId(Id.create(toFacility.getId(), TransitStopArea.class)); - newFacility.setLinkId(link.getId()); - newFacility.setName(toFacility.getName()); - copies.add(newFacility); - this.nodes.put(newFacility, toNode); - this.schedule.addStopFacility(newFacility); - toStop.setStopFacility(newFacility); - this.stopFacilities.put(connection, newFacility); - } - } else { - toStop.setStopFacility(this.stopFacilities.get(connection)); - } - return link; + return link == null ? createAndAddLink(connection) : link; } - private Link createAndAddLink(Node fromNode, Node toNode, - Tuple connection) { + private Link createAndAddLink(Connection connection) { Link link; - link = this.network.getFactory().createLink(Id.create(this.prefix + this.linkIdCounter++, Link.class), fromNode, toNode); - if (fromNode == toNode) { - link.setLength(50); + Id id; + double length; + if (connection.fromNode == connection.toNode) { + id = Id.createLinkId (this.prefix + connection.fromNode.getId()); + length = 50.0; } else { - link.setLength(CoordUtils.calcEuclideanDistance(fromNode.getCoord(), toNode.getCoord())); + id = Id.createLinkId(this.prefix + connection.fromNode.getId() + "-" + connection.toNode.getId()); + length = CoordUtils.calcEuclideanDistance(connection.fromNode.getCoord(), connection.toNode.getCoord()); } + link = this.network.getFactory().createLink(id, connection.fromNode, connection.toNode); + link.setLength(length); + link.setFreespeed(linkFreeSpeed); link.setCapacity(linkCapacity); link.setNumberOfLanes(1); @@ -185,11 +151,6 @@ private Link createAndAddLink(Node fromNode, Node toNode, return link; } - public Link getLinkBetweenStops(final TransitStopFacility fromStop, final TransitStopFacility toStop) { - Node fromNode = this.nodes.get(fromStop); - Node toNode = this.nodes.get(toStop); - Tuple connection = new Tuple(fromNode, toNode); - return this.links.get(connection); - } + record Connection (Node fromNode, Node toNode) {} } diff --git a/matsim/src/main/java/org/matsim/pt/utils/CreatePseudoNetworkWithLoopLinks.java b/matsim/src/main/java/org/matsim/pt/utils/CreatePseudoNetworkWithLoopLinks.java deleted file mode 100644 index d0532315137..00000000000 --- a/matsim/src/main/java/org/matsim/pt/utils/CreatePseudoNetworkWithLoopLinks.java +++ /dev/null @@ -1,153 +0,0 @@ -/* *********************************************************************** * - * project: org.matsim.* - * CreatePseudoNetwork - * * - * *********************************************************************** * - * * - * copyright : (C) 2009 by the members listed in the COPYING, * - * LICENSE and WARRANTY file. * - * email : info at matsim dot org * - * * - * *********************************************************************** * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * See also COPYING, LICENSE and WARRANTY file * - * * - * *********************************************************************** */ - -package org.matsim.pt.utils; - -import com.google.common.annotations.Beta; -import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.TransportMode; -import org.matsim.api.core.v01.network.Link; -import org.matsim.api.core.v01.network.Network; -import org.matsim.api.core.v01.network.Node; -import org.matsim.core.population.routes.NetworkRoute; -import org.matsim.core.population.routes.RouteUtils; -import org.matsim.core.utils.collections.Tuple; -import org.matsim.core.utils.geometry.CoordUtils; -import org.matsim.pt.transitSchedule.api.*; - -import java.util.*; - -/** - * Builds a network where transit vehicles can drive along and assigns the correct - * links to the transit stop facilities and routes of transit lines. Each stop facility - * is assigned to a loop link, located in a node with the same coordinates as the stop. - * The stop facility ID is used for node and link IDs. - * - * @author mrieser, davibicudo - * - * @implNote THis functionality might be merged with {@link CreatePseudoNetwork}. - */ -@Beta -public class CreatePseudoNetworkWithLoopLinks { - - private final TransitSchedule schedule; - private final Network network; - private final String prefix; - private final double linkFreeSpeed; - private final double linkCapacity; - - private final Map, Link> links = new HashMap<>(); - private final Map nodes = new HashMap<>(); - - private final Set transitModes = Collections.singleton(TransportMode.pt); - - public CreatePseudoNetworkWithLoopLinks(final TransitSchedule schedule, final Network network, final String networkIdPrefix) { - this.schedule = schedule; - this.network = network; - this.prefix = networkIdPrefix; - this.linkFreeSpeed = 100.0 / 3.6; - this.linkCapacity = 100000.0; - } - - public CreatePseudoNetworkWithLoopLinks(final TransitSchedule schedule, final Network network, final String networkIdPrefix, - final double linkFreeSpeed, final double linkCapacity) { - this.schedule = schedule; - this.network = network; - this.prefix = networkIdPrefix; - this.linkFreeSpeed = linkFreeSpeed; - this.linkCapacity = linkCapacity; - } - - public void createNetwork() { - - createStopNodesAndLoopLinks(); - - List> toBeRemoved = new LinkedList<>(); - for (TransitLine tLine : this.schedule.getTransitLines().values()) { - for (TransitRoute tRoute : tLine.getRoutes().values()) { - ArrayList> routeLinks = new ArrayList<>(); - TransitRouteStop prevStop = null; - for (TransitRouteStop stop : tRoute.getStops()) { - if (prevStop != null) { - Link link = getNetworkLink(prevStop, stop); - routeLinks.add(link.getId()); - } - prevStop = stop; - } - - if (!routeLinks.isEmpty()) { - NetworkRoute route = RouteUtils.createNetworkRoute(routeLinks); - tRoute.setRoute(route); - } else { - System.err.println("Line " + tLine.getId() + " route " + tRoute.getId() + " has less than two stops. Removing this route from schedule."); - toBeRemoved.add(new Tuple<>(tLine, tRoute)); - } - } - } - - for (Tuple remove : toBeRemoved) { - remove.getFirst().removeRoute(remove.getSecond()); - } - } - - private void createStopNodesAndLoopLinks() { - for (TransitStopFacility stop : this.schedule.getFacilities().values()) { - Node node = this.network.getFactory().createNode(Id.createNodeId(this.prefix + stop.getId()), stop.getCoord()); - this.network.addNode(node); - this.nodes.put(stop, node); - - Link loopLink = this.network.getFactory().createLink(Id.createLinkId (this.prefix + stop.getId()), node, node); - stop.setLinkId(loopLink.getId()); - this.network.addLink(loopLink); - Tuple connection = new Tuple<>(node, node); - this.links.put(connection, loopLink); - } - } - - private Link getNetworkLink(final TransitRouteStop fromStop, final TransitRouteStop toStop) { - TransitStopFacility fromFacility = fromStop.getStopFacility(); - TransitStopFacility toFacility = toStop.getStopFacility(); - - Node fromNode = this.nodes.get(fromFacility); - Node toNode = this.nodes.get(toFacility); - - Tuple connection = new Tuple<>(fromNode, toNode); - Link link = this.links.get(connection); - return link == null ? createAndAddLink(connection) : link; - } - - private Link createAndAddLink(Tuple connection) { - Node fromNode = connection.getFirst(); - Node toNode = connection.getSecond(); - Link link; - link = this.network.getFactory().createLink(Id.createLinkId(this.prefix + fromNode.getId() + "-" + toNode.getId()), fromNode, - toNode); - link.setLength(CoordUtils.calcEuclideanDistance(fromNode.getCoord(), toNode.getCoord())); - - link.setFreespeed(linkFreeSpeed); - link.setCapacity(linkCapacity); - link.setNumberOfLanes(1); - this.network.addLink(link); - link.setAllowedModes(this.transitModes); - this.links.put(connection, link); - return link; - } - -} diff --git a/matsim/src/test/java/org/matsim/pt/utils/TransitScheduleValidatorTest.java b/matsim/src/test/java/org/matsim/pt/utils/TransitScheduleValidatorTest.java index fb97f3b1ecd..606e9e1b9df 100644 --- a/matsim/src/test/java/org/matsim/pt/utils/TransitScheduleValidatorTest.java +++ b/matsim/src/test/java/org/matsim/pt/utils/TransitScheduleValidatorTest.java @@ -27,16 +27,20 @@ import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.NetworkWriter; import org.matsim.core.config.ConfigUtils; import org.matsim.core.scenario.ScenarioUtils; import org.matsim.pt.transitSchedule.api.TransitLine; import org.matsim.pt.transitSchedule.api.TransitRoute; import org.matsim.pt.transitSchedule.api.TransitSchedule; import org.matsim.pt.transitSchedule.api.TransitScheduleFactory; +import org.matsim.pt.transitSchedule.api.TransitScheduleReader; +import org.matsim.pt.transitSchedule.api.TransitScheduleWriter; import org.matsim.pt.transitSchedule.api.TransitStopFacility; import java.util.Collections; import java.util.List; +import org.matsim.pt.utils.TransitScheduleValidator.ValidationResult.ValidationIssue; import static org.assertj.core.api.Assertions.assertThat; @@ -51,6 +55,16 @@ void testPtTutorial() { assertThat(validationResult.getIssues()).isEmpty(); } + @Test + void testPtTutorialPseudoNetwork() { + Scenario scenario = ScenarioUtils.createScenario(ConfigUtils.createConfig()); + new TransitScheduleReader(scenario).readFile("test/scenarios/pt-tutorial/transitSchedule.xml"); + new CreatePseudoNetwork(scenario.getTransitSchedule(), scenario.getNetwork(), "").createNetwork(); + TransitScheduleValidator.ValidationResult validationResult = TransitScheduleValidator.validateAll( + scenario.getTransitSchedule(), scenario.getNetwork()); + assertThat(validationResult.getIssues()).isEmpty(); + } + @Test void testPtTutorialWithError() { Scenario scenario = ScenarioUtils.loadScenario(