Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file NLEdgeControlBuilder.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Michael Behrisch
18 : /// @author Leonhard Luecken
19 : /// @date Mon, 9 Jul 2001
20 : ///
21 : // Interface for building edges
22 : /****************************************************************************/
23 : #include <config.h>
24 :
25 : #include <vector>
26 : #include <string>
27 : #include <map>
28 : #include <algorithm>
29 : #include <iterator>
30 : #include <mesosim/MELoop.h>
31 : #include <microsim/MSGlobals.h>
32 : #include <microsim/MSLane.h>
33 : #include <microsim/MSEdge.h>
34 : #include <microsim/MSEdgeControl.h>
35 : #include <utils/common/StringTokenizer.h>
36 : #include <utils/common/UtilExceptions.h>
37 : #include <utils/options/OptionsCont.h>
38 : #include "NLBuilder.h"
39 : #include "NLEdgeControlBuilder.h"
40 : #include <utils/iodevices/OutputDevice.h>
41 :
42 :
43 : // ===========================================================================
44 : // method definitions
45 : // ===========================================================================
46 39017 : NLEdgeControlBuilder::NLEdgeControlBuilder()
47 39017 : : myCurrentNumericalLaneID(0), myCurrentNumericalEdgeID(0), myEdges(0), myCurrentLaneIndex(-1) {
48 39017 : myActiveEdge = (MSEdge*) nullptr;
49 39017 : myLaneStorage = new std::vector<MSLane*>();
50 39017 : }
51 :
52 :
53 39017 : NLEdgeControlBuilder::~NLEdgeControlBuilder() {
54 39017 : delete myLaneStorage;
55 78034 : }
56 :
57 :
58 : void
59 1616906 : NLEdgeControlBuilder::beginEdgeParsing(
60 : const std::string& id, const SumoXMLEdgeFunc function,
61 : const std::string& streetName,
62 : const std::string& edgeType,
63 : const std::string& routingType,
64 : int priority,
65 : const std::string& bidi,
66 : double distance) {
67 : // closeEdge might not have been called because the last edge had an error, so we clear the lane storage
68 1616906 : myLaneStorage->clear();
69 1616906 : myActiveEdge = buildEdge(id, function, streetName, edgeType, routingType, priority, distance);
70 1616906 : if (MSEdge::dictionary(id) != nullptr) {
71 12 : throw InvalidArgument("Another edge with the id '" + id + "' exists.");
72 : }
73 1616902 : myEdges.push_back(myActiveEdge);
74 1616902 : if (bidi != "") {
75 27964 : myBidiEdges[myActiveEdge] = bidi;
76 : }
77 1616902 : }
78 :
79 :
80 : MSLane*
81 1549451 : NLEdgeControlBuilder::addLane(const std::string& id,
82 : double maxSpeed, double friction, double length,
83 : const PositionVector& shape, double width,
84 : SVCPermissions permissions,
85 : SVCPermissions changeLeft, SVCPermissions changeRight,
86 : int index, bool isRampAccel,
87 : const std::string& type,
88 : const PositionVector& outlineShape) {
89 1549451 : MSLane* lane = new MSLane(id, maxSpeed, friction, length, myActiveEdge, myCurrentNumericalLaneID++, shape, width, permissions, changeLeft, changeRight, index, isRampAccel, type, outlineShape);
90 1549451 : myLaneStorage->push_back(lane);
91 1549451 : myCurrentLaneIndex = index;
92 1549451 : return lane;
93 : }
94 :
95 :
96 : void
97 2178 : NLEdgeControlBuilder::addStopOffsets(const StopOffset& stopOffset) {
98 2178 : if (myCurrentLaneIndex == -1) {
99 1158 : setDefaultStopOffset(stopOffset);
100 : } else {
101 1020 : updateCurrentLaneStopOffset(stopOffset);
102 : }
103 2178 : }
104 :
105 :
106 : std::string
107 0 : NLEdgeControlBuilder::reportCurrentEdgeOrLane() const {
108 0 : std::stringstream ss;
109 0 : if (myCurrentLaneIndex != -1) {
110 0 : ss << "lane " << myCurrentLaneIndex << " of ";
111 : }
112 0 : ss << "edge '" << myActiveEdge->getID() << "'";
113 0 : return ss.str();
114 0 : }
115 :
116 :
117 : void
118 1020 : NLEdgeControlBuilder::updateCurrentLaneStopOffset(const StopOffset& stopOffset) {
119 1020 : if (myLaneStorage->size() == 0) {
120 0 : throw ProcessError("myLaneStorage cannot be empty");
121 : }
122 1020 : if (stopOffset.isDefined()) {
123 1020 : if (myLaneStorage->back()->getLaneStopOffsets().isDefined()) {
124 0 : WRITE_WARNING("Duplicate stopOffset definition for lane " + toString(myLaneStorage->back()->getIndex()) +
125 : " on edge " + myActiveEdge->getID() + "!")
126 : } else {
127 1020 : myLaneStorage->back()->setLaneStopOffset(stopOffset);
128 : }
129 : }
130 1020 : }
131 :
132 :
133 : void
134 1158 : NLEdgeControlBuilder::setDefaultStopOffset(const StopOffset& stopOffsets) {
135 1158 : if (myCurrentDefaultStopOffset.isDefined()) {
136 0 : WRITE_WARNING("Duplicate stopOffset definition for edge " + myActiveEdge->getID() + ". Ignoring duplicate specification.")
137 : } else {
138 1158 : myCurrentDefaultStopOffset = stopOffsets;
139 : }
140 1158 : }
141 :
142 :
143 : void
144 1616774 : NLEdgeControlBuilder::applyDefaultStopOffsetsToLanes() {
145 1616774 : if (myActiveEdge == nullptr) {
146 0 : throw ProcessError("myActiveEdge cannot be nullptr");
147 : }
148 1616774 : if (myCurrentDefaultStopOffset.isDefined()) {
149 3118 : for (const auto& l : *myLaneStorage) {
150 1960 : if (!l->getLaneStopOffsets().isDefined()) {
151 1132 : l->setLaneStopOffset(myCurrentDefaultStopOffset);
152 : }
153 : }
154 : }
155 1616774 : }
156 :
157 :
158 : void
159 8030 : NLEdgeControlBuilder::addNeigh(const std::string id) {
160 8030 : myOppositeLanes.push_back({myLaneStorage->back(), id});
161 8030 : }
162 :
163 :
164 : MSEdge*
165 1616774 : NLEdgeControlBuilder::closeEdge() {
166 1616774 : applyDefaultStopOffsetsToLanes();
167 1616774 : std::vector<MSLane*>* lanes = new std::vector<MSLane*>();
168 1616774 : lanes->reserve(myLaneStorage->size());
169 1616774 : copy(myLaneStorage->begin(), myLaneStorage->end(), back_inserter(*lanes));
170 1616774 : myLaneStorage->clear();
171 1616774 : myActiveEdge->initialize(lanes);
172 1616774 : myCurrentDefaultStopOffset.reset();
173 1616774 : return myActiveEdge;
174 : }
175 :
176 :
177 : void
178 1951050 : NLEdgeControlBuilder::closeLane() {
179 1951050 : myCurrentLaneIndex = -1;
180 1951050 : }
181 :
182 :
183 : MSEdgeControl*
184 38596 : NLEdgeControlBuilder::build(const MMVersion& networkVersion) {
185 44433 : if (MSGlobals::gUseMesoSim && !OptionsCont::getOptions().getBool("meso-lane-queue")) {
186 11594 : MSEdge::setMesoIgnoredVClasses(parseVehicleClasses(OptionsCont::getOptions().getStringVector("meso-ignore-lanes-by-vclass")));
187 : }
188 : // connecting opposite lanes must happen before MSEdge::closeBuilding
189 46626 : for (auto item : myOppositeLanes) {
190 8030 : MSLane* oppo = MSLane::dictionary(item.second);
191 8030 : if (oppo == nullptr) {
192 0 : WRITE_ERRORF("Unknown neigh lane '%' for lane '%'", item.second, item.first->getID());
193 : } else {
194 8030 : item.first->setOpposite(oppo);
195 : }
196 : }
197 : // consistency check
198 : std::set<const MSLane*> checked;
199 46611 : for (auto item : myOppositeLanes) {
200 8025 : if (item.first->getOpposite() != nullptr) {
201 8025 : if (item.first->getOpposite()->getOpposite() != item.first) {
202 16 : if (checked.count(item.first->getOpposite()) == 0) {
203 18 : WRITE_WARNINGF(TL("Asymmetrical neigh lane '%' for lane '%'"), item.second, item.first->getID());
204 6 : item.first->getOpposite()->setOpposite(item.first);
205 : } else {
206 50 : throw ProcessError(TLF("Mutually inconsistent neigh lane definitions for lanes '%', '%' and '%'",
207 30 : item.first->getID(), item.first->getOpposite()->getID(), Named::getIDSecure(item.first->getOpposite()->getOpposite())));
208 : }
209 : }
210 : checked.insert(item.first);
211 8025 : checked.insert(item.first->getOpposite());
212 : }
213 : }
214 1652844 : for (MSEdge* const edge : myEdges) {
215 1614258 : edge->closeBuilding();
216 : }
217 1652844 : for (MSEdge* const edge : myEdges) {
218 1614258 : edge->rebuildAllowedTargets(false);
219 : // segment building depends on the finished list of successors (for multi-queue)
220 1614258 : if (MSGlobals::gUseMesoSim && !edge->getLanes().empty()) {
221 285288 : MSGlobals::gMesoNet->buildSegmentsFor(*edge, OptionsCont::getOptions());
222 : }
223 : }
224 : // mark internal edges belonging to a roundabout (after all edges are build)
225 38586 : if (MSGlobals::gUsingInternalLanes) {
226 1360129 : for (MSEdge* const edge : myEdges) {
227 1327434 : if (edge->isInternal()) {
228 699126 : if (edge->getNumSuccessors() != 1 || edge->getNumPredecessors() != 1) {
229 0 : throw ProcessError(TLF("Internal edge '%' is not properly connected (probably a manually modified net.xml).", edge->getID()));
230 : }
231 699126 : if (edge->getSuccessors()[0]->isRoundabout() || edge->getPredecessors()[0]->isRoundabout()) {
232 : edge->markAsRoundabout();
233 : }
234 : }
235 : }
236 : }
237 38586 : if (!deprecatedVehicleClassesSeen.empty()) {
238 0 : WRITE_WARNINGF(TL("Deprecated vehicle classes '%' in input network."), toString(deprecatedVehicleClassesSeen));
239 : deprecatedVehicleClassesSeen.clear();
240 : }
241 : // check for bi-directional edges (this are edges in opposing direction and superposable/congruent shapes)
242 38586 : if (myBidiEdges.size() > 0 || networkVersion > MMVersion(1, 0)) {
243 45561 : for (auto& item : myBidiEdges) {
244 27964 : item.first->checkAndRegisterBiDirEdge(item.second);
245 : }
246 : //WRITE_MESSAGEF(TL("Loaded % bidirectional edges"), toString(myBidiEdges.size()));
247 : } else {
248 : // legacy network
249 867685 : for (MSEdge* e : myEdges) {
250 1693402 : e->checkAndRegisterBiDirEdge();
251 : }
252 : }
253 : // take into account bidi lanes when deciding on whether an edge allows changing
254 1652844 : for (MSEdge* const edge : myEdges) {
255 1614258 : edge->buildLaneChanger();
256 : }
257 77172 : return new MSEdgeControl(myEdges);
258 : }
259 :
260 :
261 : MSEdge*
262 1464904 : NLEdgeControlBuilder::buildEdge(const std::string& id, const SumoXMLEdgeFunc function,
263 : const std::string& streetName, const std::string& edgeType, const std::string& routingType,
264 : const int priority, const double distance) {
265 1464904 : return new MSEdge(id, myCurrentNumericalEdgeID++, function, streetName, edgeType, routingType, priority, distance);
266 : }
267 :
268 16502 : void NLEdgeControlBuilder::addCrossingEdges(const std::vector<std::string>& crossingEdges) {
269 16502 : myActiveEdge->setCrossingEdges(crossingEdges);
270 16502 : }
271 :
272 :
273 : /****************************************************************************/
|