diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/CleanNetwork.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/CleanNetwork.java index 64cef3959ac..eb6f7893ff1 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/network/CleanNetwork.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/CleanNetwork.java @@ -4,7 +4,7 @@ import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; import org.matsim.application.MATSimAppCommand; -import org.matsim.core.network.DisallowedNextLinks; +import org.matsim.core.network.turnRestrictions.DisallowedNextLinks; import org.matsim.core.network.NetworkUtils; import org.matsim.core.network.algorithms.MultimodalNetworkCleaner; import picocli.CommandLine; diff --git a/contribs/sumo/src/main/java/org/matsim/contrib/sumo/SumoNetworkConverter.java b/contribs/sumo/src/main/java/org/matsim/contrib/sumo/SumoNetworkConverter.java index b384b50d8d0..0dce90bd57b 100644 --- a/contribs/sumo/src/main/java/org/matsim/contrib/sumo/SumoNetworkConverter.java +++ b/contribs/sumo/src/main/java/org/matsim/contrib/sumo/SumoNetworkConverter.java @@ -14,7 +14,7 @@ import org.matsim.api.core.v01.network.Node; import org.matsim.contrib.osm.networkReader.LinkProperties; import org.matsim.contrib.osm.networkReader.OsmTags; -import org.matsim.core.network.DisallowedNextLinks; +import org.matsim.core.network.turnRestrictions.DisallowedNextLinks; import org.matsim.core.network.NetworkUtils; import org.matsim.core.network.algorithms.NetworkCleaner; import org.matsim.core.scenario.ProjectionUtils; diff --git a/matsim/src/main/java/org/matsim/core/network/NetworkUtils.java b/matsim/src/main/java/org/matsim/core/network/NetworkUtils.java index 7c8498bf626..c0c3192f736 100644 --- a/matsim/src/main/java/org/matsim/core/network/NetworkUtils.java +++ b/matsim/src/main/java/org/matsim/core/network/NetworkUtils.java @@ -42,6 +42,7 @@ import org.matsim.core.network.algorithms.NetworkModeRestriction; import org.matsim.core.network.algorithms.NetworkSimplifier; import org.matsim.core.network.io.MatsimNetworkReader; +import org.matsim.core.network.turnRestrictions.DisallowedNextLinks; import org.matsim.core.utils.geometry.CoordUtils; import org.matsim.core.utils.geometry.CoordinateTransformation; import org.matsim.core.utils.misc.OptionalTime; diff --git a/matsim/src/main/java/org/matsim/core/network/algorithms/NetworkMergeDoubleLinks.java b/matsim/src/main/java/org/matsim/core/network/algorithms/NetworkMergeDoubleLinks.java index 183eca29e45..066384002cd 100644 --- a/matsim/src/main/java/org/matsim/core/network/algorithms/NetworkMergeDoubleLinks.java +++ b/matsim/src/main/java/org/matsim/core/network/algorithms/NetworkMergeDoubleLinks.java @@ -28,8 +28,8 @@ import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.network.Node; import org.matsim.core.api.internal.NetworkRunnable; -import org.matsim.core.network.DisallowedNextLinks; -import org.matsim.core.network.DisallowedNextLinksUtils; +import org.matsim.core.network.turnRestrictions.DisallowedNextLinks; +import org.matsim.core.network.turnRestrictions.DisallowedNextLinksUtils; import org.matsim.core.network.NetworkUtils; import java.util.*; diff --git a/matsim/src/main/java/org/matsim/core/network/algorithms/TransportModeNetworkFilter.java b/matsim/src/main/java/org/matsim/core/network/algorithms/TransportModeNetworkFilter.java index 03849cc570c..7ca29a361ab 100644 --- a/matsim/src/main/java/org/matsim/core/network/algorithms/TransportModeNetworkFilter.java +++ b/matsim/src/main/java/org/matsim/core/network/algorithms/TransportModeNetworkFilter.java @@ -28,7 +28,7 @@ import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.network.NetworkFactory; import org.matsim.api.core.v01.network.Node; -import org.matsim.core.network.DisallowedNextLinks; +import org.matsim.core.network.turnRestrictions.DisallowedNextLinks; import org.matsim.core.network.NetworkChangeEvent; import org.matsim.core.network.NetworkUtils; import org.matsim.core.network.TimeDependentNetwork; diff --git a/matsim/src/main/java/org/matsim/core/network/DisallowedNextLinks.java b/matsim/src/main/java/org/matsim/core/network/turnRestrictions/DisallowedNextLinks.java similarity index 99% rename from matsim/src/main/java/org/matsim/core/network/DisallowedNextLinks.java rename to matsim/src/main/java/org/matsim/core/network/turnRestrictions/DisallowedNextLinks.java index 25c0bbd7303..ef87a6e832d 100644 --- a/matsim/src/main/java/org/matsim/core/network/DisallowedNextLinks.java +++ b/matsim/src/main/java/org/matsim/core/network/turnRestrictions/DisallowedNextLinks.java @@ -1,4 +1,4 @@ -package org.matsim.core.network; +package org.matsim.core.network.turnRestrictions; import java.util.ArrayList; import java.util.Collection; diff --git a/matsim/src/main/java/org/matsim/core/network/DisallowedNextLinksUtils.java b/matsim/src/main/java/org/matsim/core/network/turnRestrictions/DisallowedNextLinksUtils.java similarity index 96% rename from matsim/src/main/java/org/matsim/core/network/DisallowedNextLinksUtils.java rename to matsim/src/main/java/org/matsim/core/network/turnRestrictions/DisallowedNextLinksUtils.java index 52ca271e7b0..a787f1c9fe5 100644 --- a/matsim/src/main/java/org/matsim/core/network/DisallowedNextLinksUtils.java +++ b/matsim/src/main/java/org/matsim/core/network/turnRestrictions/DisallowedNextLinksUtils.java @@ -1,4 +1,4 @@ -package org.matsim.core.network; +package org.matsim.core.network.turnRestrictions; import java.util.ArrayList; import java.util.List; @@ -11,6 +11,7 @@ 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.network.NetworkUtils; /** * Some methods to validate DisallowedNextLinks attributes of a network. diff --git a/matsim/src/main/java/org/matsim/core/network/turnRestrictions/TurnRestrictionsContext.java b/matsim/src/main/java/org/matsim/core/network/turnRestrictions/TurnRestrictionsContext.java new file mode 100644 index 00000000000..3c7d88d463a --- /dev/null +++ b/matsim/src/main/java/org/matsim/core/network/turnRestrictions/TurnRestrictionsContext.java @@ -0,0 +1,312 @@ +package org.matsim.core.network.turnRestrictions; + +import org.matsim.api.core.v01.Id; +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.network.NetworkUtils; + +import java.util.*; + +/** + * Turn restriction context that expands networks with turn restrictions with colored subgraphs. + * + * * @author mrieser / Simunto + */ +public final class TurnRestrictionsContext { + + private int nodeCount; + private int linkCount; + + public final Network network; + + public Map, ColoredLink> replacedLinks = new HashMap<>(); + public List coloredNodes = new ArrayList<>(); + public List coloredLinks = new ArrayList<>(); + public Map, List> coloredLinksPerLinkMap = new HashMap<>(); + + private TurnRestrictionsContext(Network network) { + this.network = network; + this.nodeCount = Id.getNumberOfIds(Node.class); + this.linkCount = Id.getNumberOfIds(Link.class); + } + + public static final class ColoredLink { + public final int index; + public final Link link; + public final ColoredNode fromColoredNode; + public final Node fromNode; + public ColoredNode toColoredNode; + public Node toNode; + + private ColoredLink( + int index, + Link link, + ColoredNode fromColoredNode, + Node fromNode, + ColoredNode toColoredNode, + Node toNode + ) { + this.index = index; + this.link = link; + this.fromColoredNode = fromColoredNode; + this.fromNode = fromNode; + this.toColoredNode = toColoredNode; + this.toNode = toNode; + } + } + + public record ColoredNode( + int index, + Node node, + List outLinks, + List inLinks + ) { + + @Override + public int index() { + return index; + } + + @Override + public Node node() { + return node; + } + + @Override + public int hashCode() { + return index; + } + } + + public static TurnRestrictionsContext build(Network network) { + /* + * The implementation follows the algorithm developed by + * Marcel Rieser (Simunto) and Hannes Rewald (Volkswagen Group) + * in October 2023 during the MATSim Code Sprint week in Berlin, + * and documented at https://github.com/matsim-org/matsim-code-examples/wiki/turn-restrictions. + * + * TL;DR: + * Main idea of algorithm: for each link with turn-restrictions, create a sub-graph of the network + * containing all links required to model all allowed paths, but exclude the last link of turn restrictions' + * link sequence to enforce the "disallow" along that route. + * + * + * Implementation details: + * - The easiest solution would be to make a copy of the original network, then start modifying it + * according to the algorithm above (e.g. add and delete links and nodes), and then to convert the + * resulting network into a graph. This would require substantial amount of memory for duplicating + * the complete network, and might pose problems as we will have multiple links with the same id. + * - Given the assumption that turn restrictions apply only to a small amount of the full network, + * we keep the original network intact. Instead, we keep all modifications in separate data-structures + * so they can be used to create the routing-graph. + * - If the network is already filtered for a specific mode, it might be that links referenced + * in a turn restriction are missing. The implementation must be able to deal with such cases, + * prevent NullPointerExceptions. + * - As turn restrictions are mode-specific, the algorithm needs to know for which mode the + * turn restriction need to be considered. + */ + + TurnRestrictionsContext context = new TurnRestrictionsContext(network); + + for (Link startingLink : network.getLinks().values()) { + DisallowedNextLinks disallowedNextLinks = NetworkUtils.getDisallowedNextLinks(startingLink); + if (disallowedNextLinks == null) { + continue; + } + Collection>> turnRestrictions = disallowedNextLinks.getMergedDisallowedLinkSequences(); + if (turnRestrictions == null || turnRestrictions.isEmpty()) { + continue; + } + + // steps 1 to 5: + ColoredLink coloredStartingLink = context.applyTurnRestriction(context, turnRestrictions, startingLink); + + // step 6: turn restrictions have to be applied separately to existing colored links as well. + // see if there are already colored link copies available for this starting link + List coloredLinks = context.coloredLinksPerLinkMap.get(startingLink.getId()); + if (coloredLinks != null) { + for (ColoredLink coloredLink : coloredLinks) { + // optimization: re-point toNode instead of re-applying full turn restrictions + if (coloredLink.toColoredNode == null) { + coloredLink.toColoredNode = coloredStartingLink.toColoredNode; + coloredLink.toNode = null; + } else { + context.applyTurnRestriction(context, turnRestrictions, coloredLink); + } + } + } + } + + return context; + } + + private ColoredLink applyTurnRestriction(TurnRestrictionsContext context, Collection>> restrictions, Link startingLink) { + return this.applyTurnRestriction(context, restrictions, startingLink, null); + } + + private void applyTurnRestriction(TurnRestrictionsContext context, Collection>> restrictions, ColoredLink startingLink) { + this.applyTurnRestriction(context, restrictions, null, startingLink); + } + + private ColoredLink applyTurnRestriction(TurnRestrictionsContext context, Collection>> restrictions, Link startingLink, ColoredLink coloredStartingLink) { + Set affectedNodes = new HashSet<>(); + Set affectedColoredNodes = new HashSet<>(); + Set affectedLinks = new HashSet<>(); + Set affectedColoredLinks = new HashSet<>(); + Set> endLinkIds = new HashSet<>(); + + // step 1 and 2: collect end-links, affected-links and affected-nodes + for (List> restriction : restrictions) { + + Link currentLink; + ColoredLink currentColoredLink; + Node currentNode = startingLink == null ? null : startingLink.getToNode(); + // due to the optimization in step 6, every colored starting link leads to a colored to-node + ColoredNode currentColoredNode = coloredStartingLink == null ? null : coloredStartingLink.toColoredNode; + + // walk along the restricted path, collect affectedLinks, affectedNodes and endLink + for (Id linkId : restriction) { + if (currentNode != null) { + // handle regular node + affectedNodes.add(currentNode); + currentLink = null; + currentColoredLink = null; + for (Link outLink : currentNode.getOutLinks().values()) { + if (outLink.getId() == linkId) { + currentColoredLink = context.replacedLinks.get(linkId); + if (currentColoredLink == null) { + currentLink = outLink; + } + break; + } + } + + if (currentLink != null) { + affectedLinks.add(currentLink); + currentNode = currentLink.getToNode(); + currentColoredNode = null; + } + if (currentColoredLink != null) { + affectedColoredLinks.add(currentColoredLink); + currentNode = currentColoredLink.toNode; + currentColoredNode = currentColoredLink.toColoredNode; + } + if (currentLink == null && currentColoredLink == null) { + // link of restriction is no longer part of the network, maybe we are in a sub-graph + break; + } + } else if (currentColoredNode != null) { + // handle colored node + affectedColoredNodes.add(currentColoredNode); + currentLink = null; + currentColoredLink = null; + for (ColoredLink outLink : currentColoredNode.outLinks) { + if (outLink.link.getId() == linkId) { + currentColoredLink = outLink; + break; + } + } + if (currentColoredLink != null) { + affectedColoredLinks.add(currentColoredLink); + currentNode = currentColoredLink.toNode; + currentColoredNode = currentColoredLink.toColoredNode; + } + if (currentColoredLink == null) { + // link of restriction is no longer part of the network, maybe we are in a sub-graph + break; + } + } + } + endLinkIds.add(restriction.get(restriction.size() - 1)); + } + + // step 3: create colored copies of nodes + Map, ColoredNode> newlyColoredNodes = new HashMap<>(); + for (Node affectedNode : affectedNodes) { + int nodeIndex = context.nodeCount; + context.nodeCount++; + ColoredNode newlyColoredNode = new ColoredNode(nodeIndex, affectedNode, new ArrayList<>(), new ArrayList<>()); + newlyColoredNodes.put(affectedNode.getId(), newlyColoredNode); + context.coloredNodes.add(newlyColoredNode); + } + for (ColoredNode affectedColoredNode : affectedColoredNodes) { + int nodeIndex = context.nodeCount; + context.nodeCount++; + ColoredNode newlyColoredNode = new ColoredNode(nodeIndex, affectedColoredNode.node, new ArrayList<>(), new ArrayList<>()); + newlyColoredNodes.put(affectedColoredNode.node.getId(), newlyColoredNode); + context.coloredNodes.add(newlyColoredNode); + } + + // step 4: create colored copies of links + for (Node affectedNode : affectedNodes) { + for (Link outLink : affectedNode.getOutLinks().values()) { + if (endLinkIds.contains(outLink.getId())) { + continue; + } + ColoredLink replacedOutLink = context.replacedLinks.get(outLink.getId()); + int linkIndex = context.linkCount; + context.linkCount++; + ColoredLink newlyColoredLink; + ColoredNode fromNode = newlyColoredNodes.get(outLink.getFromNode().getId()); + if (affectedLinks.contains(outLink) || (replacedOutLink != null && affectedColoredLinks.contains(replacedOutLink))) { + ColoredNode toNode = newlyColoredNodes.get(outLink.getToNode().getId()); + newlyColoredLink = new ColoredLink(linkIndex, outLink, fromNode, null, toNode, null); + } else { + Node toNode = outLink.getToNode(); + newlyColoredLink = new ColoredLink(linkIndex, outLink, fromNode, null, null, toNode); + } + fromNode.outLinks.add(newlyColoredLink); + context.coloredLinks.add(newlyColoredLink); + context.coloredLinksPerLinkMap.computeIfAbsent(outLink.getId(), id -> new ArrayList<>(3)).add(newlyColoredLink); + } + } + for (ColoredNode affectedNode : affectedColoredNodes) { + for (ColoredLink outLink : affectedNode.outLinks) { + if (endLinkIds.contains(outLink.link.getId())) { + continue; + } + int linkIndex = context.linkCount; + context.linkCount++; + ColoredLink newlyColoredLink; + ColoredNode fromNode = newlyColoredNodes.get(outLink.link.getFromNode().getId()); + if (affectedColoredLinks.contains(outLink)) { + ColoredNode toNode = newlyColoredNodes.get(outLink.link.getToNode().getId()); + newlyColoredLink = new ColoredLink(linkIndex, outLink.link, fromNode, null, toNode, null); + } else { + newlyColoredLink = new ColoredLink(linkIndex, outLink.link, fromNode, null, outLink.toColoredNode, outLink.toNode); + } + fromNode.outLinks.add(newlyColoredLink); + context.coloredLinks.add(newlyColoredLink); + context.coloredLinksPerLinkMap.computeIfAbsent(outLink.link.getId(), id -> new ArrayList<>(3)).add(newlyColoredLink); + } + } + + // step 5: replace starting link + if (startingLink != null) { + ColoredNode toNode = newlyColoredNodes.get(startingLink.getToNode().getId()); + int linkIndex = startingLink.getId().index(); // re-use the index + ColoredLink newlyColoredStartingLink = new ColoredLink(linkIndex, startingLink, null, startingLink.getFromNode(), toNode, null); + context.coloredLinks.add(newlyColoredStartingLink); + context.replacedLinks.put(startingLink.getId(), newlyColoredStartingLink); + + return newlyColoredStartingLink; + } + if (coloredStartingLink != null) { + // don't really replace the colored started link, but re-point it to the newly colored node + coloredStartingLink.toColoredNode = newlyColoredNodes.get(coloredStartingLink.link.getToNode().getId()); + return null; + + } + throw new IllegalArgumentException("either startingLink or coloredStartingLink must be set"); + } + + + public int getNodeCount() { + return nodeCount; + } + + public int getLinkCount() { + return linkCount; + } +} diff --git a/matsim/src/main/java/org/matsim/core/router/speedy/SpeedyGraphBuilder.java b/matsim/src/main/java/org/matsim/core/router/speedy/SpeedyGraphBuilder.java index 5b3a029c3f1..fbfcc23124b 100644 --- a/matsim/src/main/java/org/matsim/core/router/speedy/SpeedyGraphBuilder.java +++ b/matsim/src/main/java/org/matsim/core/router/speedy/SpeedyGraphBuilder.java @@ -4,18 +4,13 @@ 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.network.DisallowedNextLinks; import org.matsim.core.network.NetworkUtils; +import org.matsim.core.network.turnRestrictions.TurnRestrictionsContext; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Set; /** * Creates a {@link SpeedyGraph} for a provided {@link Network}. @@ -48,67 +43,12 @@ private static boolean hasTurnRestrictions(Network network) { } private SpeedyGraph buildWithTurnRestrictions(Network network) { - /* - * The implementation follows the algorithm developed by - * Marcel Rieser (Simunto) and Hannes Rewald (Volkswagen Group) - * in October 2023 during the MATSim Code Sprint week in Berlin, - * and documented at https://github.com/matsim-org/matsim-code-examples/wiki/turn-restrictions. - * - * TL;DR: - * Main idea of algorithm: for each link with turn-restrictions, create a sub-graph of the network - * containing all links required to model all allowed paths, but exclude the last link of turn restrictions' - * link sequence to enforce the "disallow" along that route. - * - * - * Implementation details: - * - The easiest solution would be to make a copy of the original network, then start modifying it - * according to the algorithm above (e.g. add and delete links and nodes), and then to convert the - * resulting network into a graph. This would require substantial amount of memory for duplicating - * the complete network, and might pose problems as we will have multiple links with the same id. - * - Given the assumption that turn restrictions apply only to a small amount of the full network, - * we keep the original network intact. Instead, we keep all modifications in separate data-structures - * so they can be used to create the routing-graph. - * - If the network is already filtered for a specific mode, it might be that links referenced - * in a turn restriction are missing. The implementation must be able to deal with such cases, - * prevent NullPointerExceptions. - * - As turn restrictions are mode-specific, the algorithm needs to know for which mode the - * turn restriction need to be considered. - */ - TurnRestrictionsContext context = new TurnRestrictionsContext(network); - - for (Link startingLink : network.getLinks().values()) { - DisallowedNextLinks disallowedNextLinks = NetworkUtils.getDisallowedNextLinks(startingLink); - if (disallowedNextLinks == null) { - continue; - } - Collection>> turnRestrictions = disallowedNextLinks.getMergedDisallowedLinkSequences(); - if (turnRestrictions == null || turnRestrictions.isEmpty()) { - continue; - } - - // steps 1 to 5: - ColoredLink coloredStartingLink = applyTurnRestriction(context, turnRestrictions, startingLink); - - // step 6: turn restrictions have to be applied separately to existing colored links as well. - // see if there are already colored link copies available for this starting link - List coloredLinks = context.coloredLinksPerLinkMap.get(startingLink.getId()); - if (coloredLinks != null) { - for (ColoredLink coloredLink : coloredLinks) { - // optimization: re-point toNode instead of re-applying full turn restrictions - if (coloredLink.toColoredNode == null) { - coloredLink.toColoredNode = coloredStartingLink.toColoredNode; - coloredLink.toNode = null; - } else { - applyTurnRestriction(context, turnRestrictions, coloredLink); - } - } - } - } + TurnRestrictionsContext context = TurnRestrictionsContext.build(network); // create routing graph from context - this.nodeCount = context.nodeCount; - this.linkCount = context.linkCount; + this.nodeCount = context.getNodeCount(); + this.linkCount = context.getLinkCount(); this.nodeData = new int[this.nodeCount * SpeedyGraph.NODE_SIZE]; this.linkData = new int[this.linkCount * SpeedyGraph.LINK_SIZE]; @@ -129,176 +69,16 @@ private SpeedyGraph buildWithTurnRestrictions(Network network) { addLink(link); } } - for (ColoredNode node : context.coloredNodes) { - this.nodes[node.index] = node.node; + for (TurnRestrictionsContext.ColoredNode node : context.coloredNodes) { + this.nodes[node.index()] = node.node(); } - for (ColoredLink link : context.coloredLinks) { + for (TurnRestrictionsContext.ColoredLink link : context.coloredLinks) { addLink(link); } return new SpeedyGraph(this.nodeData, this.linkData, this.nodes, this.links, true); } - private ColoredLink applyTurnRestriction(TurnRestrictionsContext context, Collection>> restrictions, Link startingLink) { - return this.applyTurnRestriction(context, restrictions, startingLink, null); - } - - private void applyTurnRestriction(TurnRestrictionsContext context, Collection>> restrictions, ColoredLink startingLink) { - this.applyTurnRestriction(context, restrictions, null, startingLink); - } - - private ColoredLink applyTurnRestriction(TurnRestrictionsContext context, Collection>> restrictions, Link startingLink, ColoredLink coloredStartingLink) { - Set affectedNodes = new HashSet<>(); - Set affectedColoredNodes = new HashSet<>(); - Set affectedLinks = new HashSet<>(); - Set affectedColoredLinks = new HashSet<>(); - Set> endLinkIds = new HashSet<>(); - - // step 1 and 2: collect end-links, affected-links and affected-nodes - for (List> restriction : restrictions) { - - Link currentLink; - ColoredLink currentColoredLink; - Node currentNode = startingLink == null ? null : startingLink.getToNode(); - // due to the optimization in step 6, every colored starting link leads to a colored to-node - ColoredNode currentColoredNode = coloredStartingLink == null ? null : coloredStartingLink.toColoredNode; - - // walk along the restricted path, collect affectedLinks, affectedNodes and endLink - for (Id linkId : restriction) { - if (currentNode != null) { - // handle regular node - affectedNodes.add(currentNode); - currentLink = null; - currentColoredLink = null; - for (Link outLink : currentNode.getOutLinks().values()) { - if (outLink.getId() == linkId) { - currentColoredLink = context.replacedLinks.get(linkId); - if (currentColoredLink == null) { - currentLink = outLink; - } - break; - } - } - - if (currentLink != null) { - affectedLinks.add(currentLink); - currentNode = currentLink.getToNode(); - currentColoredNode = null; - } - if (currentColoredLink != null) { - affectedColoredLinks.add(currentColoredLink); - currentNode = currentColoredLink.toNode; - currentColoredNode = currentColoredLink.toColoredNode; - } - if (currentLink == null && currentColoredLink == null) { - // link of restriction is no longer part of the network, maybe we are in a sub-graph - break; - } - } else if (currentColoredNode != null) { - // handle colored node - affectedColoredNodes.add(currentColoredNode); - currentLink = null; - currentColoredLink = null; - for (ColoredLink outLink : currentColoredNode.outLinks) { - if (outLink.link.getId() == linkId) { - currentColoredLink = outLink; - break; - } - } - if (currentColoredLink != null) { - affectedColoredLinks.add(currentColoredLink); - currentNode = currentColoredLink.toNode; - currentColoredNode = currentColoredLink.toColoredNode; - } - if (currentColoredLink == null) { - // link of restriction is no longer part of the network, maybe we are in a sub-graph - break; - } - } - } - endLinkIds.add(restriction.get(restriction.size() - 1)); - } - - // step 3: create colored copies of nodes - Map, ColoredNode> newlyColoredNodes = new HashMap<>(); - for (Node affectedNode : affectedNodes) { - int nodeIndex = context.nodeCount; - context.nodeCount++; - ColoredNode newlyColoredNode = new ColoredNode(nodeIndex, affectedNode, new ArrayList<>()); - newlyColoredNodes.put(affectedNode.getId(), newlyColoredNode); - context.coloredNodes.add(newlyColoredNode); - } - for (ColoredNode affectedColoredNode : affectedColoredNodes) { - int nodeIndex = context.nodeCount; - context.nodeCount++; - ColoredNode newlyColoredNode = new ColoredNode(nodeIndex, affectedColoredNode.node, new ArrayList<>()); - newlyColoredNodes.put(affectedColoredNode.node.getId(), newlyColoredNode); - context.coloredNodes.add(newlyColoredNode); - } - - // step 4: create colored copies of links - for (Node affectedNode : affectedNodes) { - for (Link outLink : affectedNode.getOutLinks().values()) { - if (endLinkIds.contains(outLink.getId())) { - continue; - } - ColoredLink replacedOutLink = context.replacedLinks.get(outLink.getId()); - int linkIndex = context.linkCount; - context.linkCount++; - ColoredLink newlyColoredLink; - ColoredNode fromNode = newlyColoredNodes.get(outLink.getFromNode().getId()); - if (affectedLinks.contains(outLink) || (replacedOutLink != null && affectedColoredLinks.contains(replacedOutLink))) { - ColoredNode toNode = newlyColoredNodes.get(outLink.getToNode().getId()); - newlyColoredLink = new ColoredLink(linkIndex, outLink, fromNode, null, toNode, null); - } else { - Node toNode = outLink.getToNode(); - newlyColoredLink = new ColoredLink(linkIndex, outLink, fromNode, null, null, toNode); - } - fromNode.outLinks.add(newlyColoredLink); - context.coloredLinks.add(newlyColoredLink); - context.coloredLinksPerLinkMap.computeIfAbsent(outLink.getId(), id -> new ArrayList<>(3)).add(newlyColoredLink); - } - } - for (ColoredNode affectedNode : affectedColoredNodes) { - for (ColoredLink outLink : affectedNode.outLinks) { - if (endLinkIds.contains(outLink.link.getId())) { - continue; - } - int linkIndex = context.linkCount; - context.linkCount++; - ColoredLink newlyColoredLink; - ColoredNode fromNode = newlyColoredNodes.get(outLink.link.getFromNode().getId()); - if (affectedColoredLinks.contains(outLink)) { - ColoredNode toNode = newlyColoredNodes.get(outLink.link.getToNode().getId()); - newlyColoredLink = new ColoredLink(linkIndex, outLink.link, fromNode, null, toNode, null); - } else { - newlyColoredLink = new ColoredLink(linkIndex, outLink.link, fromNode, null, outLink.toColoredNode, outLink.toNode); - } - fromNode.outLinks.add(newlyColoredLink); - context.coloredLinks.add(newlyColoredLink); - context.coloredLinksPerLinkMap.computeIfAbsent(outLink.link.getId(), id -> new ArrayList<>(3)).add(newlyColoredLink); - } - } - - // step 5: replace starting link - if (startingLink != null) { - ColoredNode toNode = newlyColoredNodes.get(startingLink.getToNode().getId()); - int linkIndex = startingLink.getId().index(); // re-use the index - ColoredLink newlyColoredStartingLink = new ColoredLink(linkIndex, startingLink, null, startingLink.getFromNode(), toNode, null); - context.coloredLinks.add(newlyColoredStartingLink); - context.replacedLinks.put(startingLink.getId(), newlyColoredStartingLink); - - return newlyColoredStartingLink; - } - if (coloredStartingLink != null) { - // don't really replace the colored started link, but re-point it to the newly colored node - coloredStartingLink.toColoredNode = newlyColoredNodes.get(coloredStartingLink.link.getToNode().getId()); - return null; - - } - throw new IllegalArgumentException("either startingLink or coloredStartingLink must be set"); - } - private SpeedyGraph buildWithoutTurnRestrictions(Network network) { this.nodeCount = Id.getNumberOfIds(Node.class); this.linkCount = Id.getNumberOfIds(Link.class); @@ -341,19 +121,19 @@ private void addLink(Link link) { this.links[linkIdx] = link; } - private void addLink(ColoredLink link) { + private void addLink(TurnRestrictionsContext.ColoredLink link) { int fromNodeIdx = -1; int toNodeIdx = -1; int linkIdx = link.index; if (link.fromColoredNode != null) { - fromNodeIdx = link.fromColoredNode.index; + fromNodeIdx = link.fromColoredNode.index(); } if (link.fromNode != null) { fromNodeIdx = link.fromNode.getId().index(); } if (link.toColoredNode != null) { - toNodeIdx = link.toColoredNode.index; + toNodeIdx = link.toColoredNode.index(); } if (link.toNode != null) { toNodeIdx = link.toNode.getId().index(); @@ -400,54 +180,4 @@ private void setInLink(int toNodeIdx, int linkIdx) { } while (inLinkIdx >= 0); this.linkData[lastLinkIdx * SpeedyGraph.LINK_SIZE + 1] = linkIdx; } - - private static class TurnRestrictionsContext { - int nodeCount; - int linkCount; - final Network network; - Map, ColoredLink> replacedLinks = new HashMap<>(); - List coloredNodes = new ArrayList<>(); - List coloredLinks = new ArrayList<>(); - Map, List> coloredLinksPerLinkMap = new HashMap<>(); - - public TurnRestrictionsContext(Network network) { - this.network = network; - this.nodeCount = Id.getNumberOfIds(Node.class); - this.linkCount = Id.getNumberOfIds(Link.class); - - } - } - - private static final class ColoredLink { - private final int index; - private final Link link; - private final ColoredNode fromColoredNode; - private final Node fromNode; - private ColoredNode toColoredNode; - private Node toNode; - - private ColoredLink( - int index, - Link link, - ColoredNode fromColoredNode, - Node fromNode, - ColoredNode toColoredNode, - Node toNode - ) { - this.index = index; - this.link = link; - this.fromColoredNode = fromColoredNode; - this.fromNode = fromNode; - this.toColoredNode = toColoredNode; - this.toNode = toNode; - } - } - - private record ColoredNode ( - int index, - Node node, - List outLinks - ) { - } - } diff --git a/matsim/src/main/java/org/matsim/utils/objectattributes/ObjectAttributesConverter.java b/matsim/src/main/java/org/matsim/utils/objectattributes/ObjectAttributesConverter.java index 52a342140d0..58041b20664 100644 --- a/matsim/src/main/java/org/matsim/utils/objectattributes/ObjectAttributesConverter.java +++ b/matsim/src/main/java/org/matsim/utils/objectattributes/ObjectAttributesConverter.java @@ -26,7 +26,7 @@ import org.apache.logging.log4j.Logger; import org.matsim.utils.objectattributes.attributeconverters.*; import org.matsim.api.core.v01.Coord; -import org.matsim.core.network.DisallowedNextLinks; +import org.matsim.core.network.turnRestrictions.DisallowedNextLinks; import org.matsim.vehicles.PersonVehicleTypes; import org.matsim.vehicles.PersonVehicles; diff --git a/matsim/src/main/java/org/matsim/utils/objectattributes/attributeconverters/DisallowedNextLinksAttributeConverter.java b/matsim/src/main/java/org/matsim/utils/objectattributes/attributeconverters/DisallowedNextLinksAttributeConverter.java index b1d1f401b62..4563388772c 100644 --- a/matsim/src/main/java/org/matsim/utils/objectattributes/attributeconverters/DisallowedNextLinksAttributeConverter.java +++ b/matsim/src/main/java/org/matsim/utils/objectattributes/attributeconverters/DisallowedNextLinksAttributeConverter.java @@ -7,7 +7,7 @@ import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.IdDeSerializationModule; import org.matsim.api.core.v01.network.Link; -import org.matsim.core.network.DisallowedNextLinks; +import org.matsim.core.network.turnRestrictions.DisallowedNextLinks; import org.matsim.utils.objectattributes.AttributeConverter; import com.fasterxml.jackson.core.JsonProcessingException; diff --git a/matsim/src/test/java/org/matsim/core/network/DisallowedNextLinksTest.java b/matsim/src/test/java/org/matsim/core/network/DisallowedNextLinksTest.java index 48c0bbfe824..ec1bff6a829 100644 --- a/matsim/src/test/java/org/matsim/core/network/DisallowedNextLinksTest.java +++ b/matsim/src/test/java/org/matsim/core/network/DisallowedNextLinksTest.java @@ -16,6 +16,7 @@ import org.matsim.api.core.v01.network.Node; import org.matsim.core.network.io.MatsimNetworkReader; import org.matsim.core.network.io.NetworkWriter; +import org.matsim.core.network.turnRestrictions.DisallowedNextLinks; import org.matsim.utils.objectattributes.attributeconverters.DisallowedNextLinksAttributeConverter; public class DisallowedNextLinksTest { diff --git a/matsim/src/test/java/org/matsim/core/network/DisallowedNextLinksUtilsTest.java b/matsim/src/test/java/org/matsim/core/network/DisallowedNextLinksUtilsTest.java index 9ea987548d5..9ff42900a83 100644 --- a/matsim/src/test/java/org/matsim/core/network/DisallowedNextLinksUtilsTest.java +++ b/matsim/src/test/java/org/matsim/core/network/DisallowedNextLinksUtilsTest.java @@ -8,6 +8,8 @@ import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; +import org.matsim.core.network.turnRestrictions.DisallowedNextLinks; +import org.matsim.core.network.turnRestrictions.DisallowedNextLinksUtils; public class DisallowedNextLinksUtilsTest { diff --git a/matsim/src/test/java/org/matsim/core/network/algorithms/TransportModeNetworkFilterTest.java b/matsim/src/test/java/org/matsim/core/network/algorithms/TransportModeNetworkFilterTest.java index 381728164b2..d079ad61532 100644 --- a/matsim/src/test/java/org/matsim/core/network/algorithms/TransportModeNetworkFilterTest.java +++ b/matsim/src/test/java/org/matsim/core/network/algorithms/TransportModeNetworkFilterTest.java @@ -36,7 +36,7 @@ import org.matsim.api.core.v01.network.Node; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; -import org.matsim.core.network.DisallowedNextLinks; +import org.matsim.core.network.turnRestrictions.DisallowedNextLinks; import org.matsim.core.network.NetworkChangeEvent; import org.matsim.core.network.NetworkChangeEvent.ChangeType; import org.matsim.core.network.NetworkChangeEvent.ChangeValue; diff --git a/matsim/src/test/java/org/matsim/core/router/speedy/SpeedyGraphBuilderTest.java b/matsim/src/test/java/org/matsim/core/router/speedy/SpeedyGraphBuilderTest.java index 36681cecbd4..440a1cb2f43 100644 --- a/matsim/src/test/java/org/matsim/core/router/speedy/SpeedyGraphBuilderTest.java +++ b/matsim/src/test/java/org/matsim/core/router/speedy/SpeedyGraphBuilderTest.java @@ -11,7 +11,7 @@ import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.network.Node; import org.matsim.core.config.groups.ScoringConfigGroup; -import org.matsim.core.network.DisallowedNextLinks; +import org.matsim.core.network.turnRestrictions.DisallowedNextLinks; import org.matsim.core.network.NetworkUtils; import org.matsim.core.router.costcalculators.FreespeedTravelTimeAndDisutility; import org.matsim.core.router.util.LeastCostPathCalculator;