Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2026 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 41511 : NLEdgeControlBuilder::NLEdgeControlBuilder()
47 41511 : : myCurrentNumericalLaneID(0), myCurrentNumericalEdgeID(0), myEdges(0), myCurrentLaneIndex(-1) {
48 41511 : myActiveEdge = (MSEdge*) nullptr;
49 41511 : myLaneStorage = new std::vector<MSLane*>();
50 41511 : }
51 :
52 :
53 41511 : NLEdgeControlBuilder::~NLEdgeControlBuilder() {
54 41511 : delete myLaneStorage;
55 41511 : if (myActiveEdge != nullptr && MSEdge::dictionary(myActiveEdge->getID()) != myActiveEdge) {
56 94 : delete myActiveEdge;
57 : }
58 83022 : }
59 :
60 :
61 : void
62 1720800 : NLEdgeControlBuilder::beginEdgeParsing(
63 : const std::string& id, const SumoXMLEdgeFunc function,
64 : const std::string& streetName,
65 : const std::string& edgeType,
66 : const std::string& routingType,
67 : int priority,
68 : const std::string& bidi,
69 : double distance) {
70 : // closeEdge might not have been called because the last edge had an error, so we clear the lane storage
71 1720800 : myLaneStorage->clear();
72 : // if the previous edge was broken (never registered in the static dict), remove and delete it
73 1720800 : if (myActiveEdge != nullptr && MSEdge::dictionary(myActiveEdge->getID()) != myActiveEdge) {
74 : myEdges.pop_back();
75 34 : delete myActiveEdge;
76 34 : myActiveEdge = nullptr;
77 : }
78 1720800 : if (MSEdge::dictionary(id) != nullptr) {
79 12 : throw InvalidArgument("Another edge with the id '" + id + "' exists.");
80 : }
81 1720796 : myActiveEdge = buildEdge(id, function, streetName, edgeType, routingType, priority, distance);
82 1720796 : myEdges.push_back(myActiveEdge);
83 1720796 : if (bidi != "") {
84 29008 : myBidiEdges[myActiveEdge] = bidi;
85 : }
86 1720796 : }
87 :
88 :
89 : MSLane*
90 1652826 : NLEdgeControlBuilder::addLane(const std::string& id,
91 : double maxSpeed, double friction, double length,
92 : const PositionVector& shape, double width,
93 : SVCPermissions permissions,
94 : SVCPermissions changeLeft, SVCPermissions changeRight,
95 : int index, bool isRampAccel,
96 : const std::string& type,
97 : const PositionVector& outlineShape) {
98 1652826 : MSLane* lane = new MSLane(id, maxSpeed, friction, length, myActiveEdge, myCurrentNumericalLaneID++, shape, width, permissions, changeLeft, changeRight, index, isRampAccel, type, outlineShape);
99 1652826 : myLaneStorage->push_back(lane);
100 1652826 : myCurrentLaneIndex = index;
101 1652826 : return lane;
102 : }
103 :
104 :
105 : void
106 2178 : NLEdgeControlBuilder::addStopOffsets(const StopOffset& stopOffset) {
107 2178 : if (myCurrentLaneIndex == -1) {
108 1158 : setDefaultStopOffset(stopOffset);
109 : } else {
110 1020 : updateCurrentLaneStopOffset(stopOffset);
111 : }
112 2178 : }
113 :
114 :
115 : std::string
116 0 : NLEdgeControlBuilder::reportCurrentEdgeOrLane() const {
117 0 : std::stringstream ss;
118 0 : if (myCurrentLaneIndex != -1) {
119 0 : ss << "lane " << myCurrentLaneIndex << " of ";
120 : }
121 0 : ss << "edge '" << myActiveEdge->getID() << "'";
122 0 : return ss.str();
123 0 : }
124 :
125 :
126 : void
127 1020 : NLEdgeControlBuilder::updateCurrentLaneStopOffset(const StopOffset& stopOffset) {
128 1020 : if (myLaneStorage->size() == 0) {
129 0 : throw ProcessError("myLaneStorage cannot be empty");
130 : }
131 1020 : if (stopOffset.isDefined()) {
132 1020 : if (myLaneStorage->back()->getLaneStopOffsets().isDefined()) {
133 0 : WRITE_WARNING("Duplicate stopOffset definition for lane " + toString(myLaneStorage->back()->getIndex()) +
134 : " on edge " + myActiveEdge->getID() + "!")
135 : } else {
136 1020 : myLaneStorage->back()->setLaneStopOffset(stopOffset);
137 : }
138 : }
139 1020 : }
140 :
141 :
142 : void
143 1158 : NLEdgeControlBuilder::setDefaultStopOffset(const StopOffset& stopOffsets) {
144 1158 : if (myCurrentDefaultStopOffset.isDefined()) {
145 0 : WRITE_WARNING("Duplicate stopOffset definition for edge " + myActiveEdge->getID() + ". Ignoring duplicate specification.")
146 : } else {
147 1158 : myCurrentDefaultStopOffset = stopOffsets;
148 : }
149 1158 : }
150 :
151 :
152 : void
153 1720668 : NLEdgeControlBuilder::applyDefaultStopOffsetsToLanes() {
154 1720668 : if (myActiveEdge == nullptr) {
155 0 : throw ProcessError("myActiveEdge cannot be nullptr");
156 : }
157 1720668 : if (myCurrentDefaultStopOffset.isDefined()) {
158 3118 : for (const auto& l : *myLaneStorage) {
159 1960 : if (!l->getLaneStopOffsets().isDefined()) {
160 1132 : l->setLaneStopOffset(myCurrentDefaultStopOffset);
161 : }
162 : }
163 : }
164 1720668 : }
165 :
166 :
167 : void
168 8861 : NLEdgeControlBuilder::addNeigh(const std::string id) {
169 8861 : myOppositeLanes.push_back({myLaneStorage->back(), id});
170 8861 : }
171 :
172 :
173 : MSEdge*
174 1720668 : NLEdgeControlBuilder::closeEdge() {
175 1720668 : applyDefaultStopOffsetsToLanes();
176 1720668 : std::vector<MSLane*>* lanes = new std::vector<MSLane*>();
177 1720668 : lanes->reserve(myLaneStorage->size());
178 1720668 : copy(myLaneStorage->begin(), myLaneStorage->end(), back_inserter(*lanes));
179 1720668 : myLaneStorage->clear();
180 1720668 : myActiveEdge->initialize(lanes);
181 1720668 : myCurrentDefaultStopOffset.reset();
182 1720668 : MSEdge* result = myActiveEdge;
183 1720668 : myActiveEdge = nullptr;
184 1720668 : return result;
185 : }
186 :
187 :
188 : void
189 2087666 : NLEdgeControlBuilder::closeLane() {
190 2087666 : myCurrentLaneIndex = -1;
191 2087666 : }
192 :
193 :
194 : MSEdgeControl*
195 41180 : NLEdgeControlBuilder::build(const MMVersion& networkVersion) {
196 47528 : if (MSGlobals::gUseMesoSim && !OptionsCont::getOptions().getBool("meso-lane-queue")) {
197 12616 : MSEdge::setMesoIgnoredVClasses(parseVehicleClasses(OptionsCont::getOptions().getStringVector("meso-ignore-lanes-by-vclass")));
198 : }
199 : // connecting opposite lanes must happen before MSEdge::closeBuilding
200 50041 : for (auto item : myOppositeLanes) {
201 8861 : MSLane* oppo = MSLane::dictionary(item.second);
202 8861 : if (oppo == nullptr) {
203 0 : WRITE_ERRORF("Unknown neigh lane '%' for lane '%'", item.second, item.first->getID());
204 : } else {
205 8861 : item.first->setOpposite(oppo);
206 : }
207 : }
208 : // consistency check
209 : std::set<const MSLane*> checked;
210 50026 : for (auto item : myOppositeLanes) {
211 8856 : if (item.first->getOpposite() != nullptr) {
212 8856 : if (item.first->getOpposite()->getOpposite() != item.first) {
213 15 : if (checked.count(item.first->getOpposite()) == 0) {
214 15 : WRITE_WARNINGF(TL("Asymmetrical neigh lane '%' for lane '%'"), item.second, item.first->getID());
215 5 : item.first->getOpposite()->setOpposite(item.first);
216 : } else {
217 50 : throw ProcessError(TLF("Mutually inconsistent neigh lane definitions for lanes '%', '%' and '%'",
218 30 : item.first->getID(), item.first->getOpposite()->getID(), Named::getIDSecure(item.first->getOpposite()->getOpposite())));
219 : }
220 : }
221 : checked.insert(item.first);
222 8856 : checked.insert(item.first->getOpposite());
223 : }
224 : }
225 1759322 : for (MSEdge* const edge : myEdges) {
226 1718152 : edge->closeBuilding();
227 : }
228 1759322 : for (MSEdge* const edge : myEdges) {
229 1718152 : edge->rebuildAllowedTargets(false);
230 : }
231 : // mark internal edges belonging to a roundabout (after all edges are build)
232 41170 : if (MSGlobals::gUsingInternalLanes) {
233 1446340 : for (MSEdge* const edge : myEdges) {
234 1411572 : if (edge->isInternal()) {
235 740212 : if (edge->getNumSuccessors() != 1 || edge->getNumPredecessors() != 1) {
236 0 : throw ProcessError(TLF("Internal edge '%' is not properly connected (probably a manually modified net.xml).", edge->getID()));
237 : }
238 740212 : if (edge->getSuccessors()[0]->isRoundabout() || edge->getPredecessors()[0]->isRoundabout()) {
239 : edge->markAsRoundabout();
240 : }
241 : }
242 : }
243 : }
244 41170 : if (!deprecatedVehicleClassesSeen.empty()) {
245 0 : WRITE_WARNINGF(TL("Deprecated vehicle classes '%' in input network."), toString(deprecatedVehicleClassesSeen));
246 : deprecatedVehicleClassesSeen.clear();
247 : }
248 : // check for bi-directional edges (this are edges in opposing direction and superposable/congruent shapes)
249 41170 : if (myBidiEdges.size() > 0 || networkVersion > MMVersion(1, 0)) {
250 48545 : for (auto& item : myBidiEdges) {
251 29008 : item.first->checkAndRegisterBiDirEdge(item.second);
252 : }
253 : //WRITE_MESSAGEF(TL("Loaded % bidirectional edges"), toString(myBidiEdges.size()));
254 : } else {
255 : // legacy network
256 898284 : for (MSEdge* e : myEdges) {
257 1753312 : e->checkAndRegisterBiDirEdge();
258 : }
259 : }
260 : // take into account bidi lanes when deciding on whether an edge allows changing
261 1759322 : for (MSEdge* const edge : myEdges) {
262 1718152 : edge->buildLaneChanger();
263 : }
264 82340 : return new MSEdgeControl(myEdges);
265 : }
266 :
267 :
268 : MSEdge*
269 1418941 : NLEdgeControlBuilder::buildEdge(const std::string& id, const SumoXMLEdgeFunc function,
270 : const std::string& streetName, const std::string& edgeType, const std::string& routingType,
271 : const int priority, const double distance) {
272 1418941 : return new MSEdge(id, myCurrentNumericalEdgeID++, function, streetName, edgeType, routingType, priority, distance);
273 : }
274 :
275 17677 : void NLEdgeControlBuilder::addCrossingEdges(const std::vector<std::string>& crossingEdges) {
276 17677 : myActiveEdge->setCrossingEdges(crossingEdges);
277 17677 : }
278 :
279 :
280 : SumoXMLEdgeFunc
281 36 : NLEdgeControlBuilder::getCurrentEdgeFunction() const {
282 36 : if (myActiveEdge == nullptr) {
283 : return SumoXMLEdgeFunc::UNKNOWN;
284 : } else {
285 36 : return myActiveEdge->getFunction();
286 : }
287 : }
288 :
289 : /****************************************************************************/
|