Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2003-2024 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 NGNet.cpp
15 : /// @author Markus Hartinger
16 : /// @author Daniel Krajzewicz
17 : /// @author Michael Behrisch
18 : /// @author Jakob Erdmann
19 : /// @date Mar, 2003
20 : ///
21 : // The class storing the generated network
22 : /****************************************************************************/
23 : #include <config.h>
24 :
25 : #include <iostream>
26 : #include <stdlib.h>
27 : #include <stdio.h>
28 : #include <string.h>
29 : #include <cmath>
30 : #include <netbuild/NBNode.h>
31 : #include <netbuild/NBNodeCont.h>
32 : #include <netbuild/NBEdge.h>
33 : #include <netbuild/NBEdgeCont.h>
34 : #include <netbuild/NBNetBuilder.h>
35 : #include <utils/common/ToString.h>
36 : #include <utils/common/RandHelper.h>
37 : #include <utils/common/StringUtils.h>
38 : #include <utils/options/OptionsCont.h>
39 : #include <utils/distribution/Distribution_Parameterized.h>
40 : #include "NGNet.h"
41 :
42 :
43 : // ===========================================================================
44 : // method definitions
45 : // ===========================================================================
46 91 : NGNet::NGNet(NBNetBuilder& nb) :
47 91 : myLastID(0),
48 91 : myAlphaIDs(OptionsCont::getOptions().getBool("alphanumerical-ids")),
49 91 : myNetBuilder(nb) {
50 91 : }
51 :
52 :
53 91 : NGNet::~NGNet() {
54 8652 : for (NGEdgeList::iterator ni = myEdgeList.begin(); ni != myEdgeList.end(); ++ni) {
55 8561 : delete *ni;
56 : }
57 5608 : for (NGNodeList::iterator ni = myNodeList.begin(); ni != myNodeList.end(); ++ni) {
58 5517 : delete *ni;
59 : }
60 91 : }
61 :
62 :
63 : std::string
64 73355 : NGNet::getNextFreeID() {
65 73355 : return toString<int>(++myLastID);
66 : }
67 :
68 :
69 : NGNode*
70 0 : NGNet::findNode(int xID, int yID) {
71 166850 : for (NGNodeList::iterator ni = myNodeList.begin(); ni != myNodeList.end(); ++ni) {
72 166850 : if ((*ni)->samePos(xID, yID)) {
73 : return *ni;
74 : }
75 : }
76 : return nullptr;
77 : }
78 :
79 : std::string
80 350 : NGNet::alphabeticalCode(int i, int iMax) {
81 : // lazy mans 26th root to determine number of characters for x-label
82 : int xn = 1;
83 350 : for (; std::pow(26, xn) < iMax; xn++) {};
84 350 : std::string result = "";
85 700 : for (int j = 0; j < xn; j++) {
86 350 : result = char('A' + (i % 26)) + result;
87 350 : i /= 26;
88 : }
89 350 : return result;
90 : }
91 :
92 : void
93 47 : NGNet::createChequerBoard(int numX, int numY, double spaceX, double spaceY, double xAttachLength, double yAttachLength) {
94 :
95 320 : for (int ix = 0; ix < numX; ix++) {
96 273 : const std::string nodeIDStart = (myAlphaIDs ? alphabeticalCode(ix, numX) : toString<int>(ix) + "/");
97 2088 : for (int iy = 0; iy < numY; iy++) {
98 : // create Node
99 1815 : NGNode* node = new NGNode(nodeIDStart + toString(iy), ix, iy);
100 1815 : node->setX(ix * spaceX + xAttachLength);
101 1815 : node->setY(iy * spaceY + yAttachLength);
102 1815 : myNodeList.push_back(node);
103 : // create Links
104 1815 : if (ix > 0) {
105 3150 : connect(findNode(ix - 1, iy), node);
106 : }
107 1815 : if (iy > 0) {
108 3084 : connect(findNode(ix, iy - 1), node);
109 : }
110 : }
111 : }
112 47 : if (yAttachLength > 0.0) {
113 12 : for (int ix = 0; ix < numX; ix++) {
114 : // create nodes
115 8 : NGNode* topNode = new NGNode("top" + toString<int>(ix), ix, numY);
116 8 : NGNode* bottomNode = new NGNode("bottom" + toString<int>(ix), ix, numY + 1);
117 8 : topNode->setX(ix * spaceX + xAttachLength);
118 : bottomNode->setX(ix * spaceX + xAttachLength);
119 8 : topNode->setY((numY - 1) * spaceY + 2 * yAttachLength);
120 : bottomNode->setY(0);
121 : topNode->setFringe();
122 : bottomNode->setFringe();
123 8 : myNodeList.push_back(topNode);
124 : myNodeList.push_back(bottomNode);
125 : // create links
126 16 : connect(findNode(ix, numY - 1), topNode);
127 16 : connect(bottomNode, findNode(ix, 0));
128 : }
129 : }
130 47 : if (xAttachLength > 0.0) {
131 12 : for (int iy = 0; iy < numY; iy++) {
132 : // create nodes
133 8 : NGNode* leftNode = new NGNode("left" + toString<int>(iy), numX, iy);
134 8 : NGNode* rightNode = new NGNode("right" + toString<int>(iy), numX + 1, iy);
135 : leftNode->setX(0);
136 8 : rightNode->setX((numX - 1) * spaceX + 2 * xAttachLength);
137 8 : leftNode->setY(iy * spaceY + yAttachLength);
138 : rightNode->setY(iy * spaceY + yAttachLength);
139 : leftNode->setFringe();
140 : rightNode->setFringe();
141 8 : myNodeList.push_back(leftNode);
142 : myNodeList.push_back(rightNode);
143 : // create links
144 16 : connect(leftNode, findNode(0, iy));
145 16 : connect(findNode(numX - 1, iy), rightNode);
146 : }
147 : }
148 47 : }
149 :
150 :
151 : double
152 0 : NGNet::radialToX(double radius, double phi) {
153 631 : return cos(phi) * radius;
154 : }
155 :
156 :
157 : double
158 0 : NGNet::radialToY(double radius, double phi) {
159 631 : return sin(phi) * radius;
160 : }
161 :
162 :
163 : void
164 20 : NGNet::createSpiderWeb(int numRadDiv, int numCircles, double spaceRad, bool hasCenter, double attachLength) {
165 : if (numRadDiv < 3) {
166 : numRadDiv = 3;
167 : }
168 : if (numCircles < 1) {
169 : numCircles = 1;
170 : }
171 :
172 : int ir, ic;
173 20 : double angle = (double)(2 * M_PI / numRadDiv); // angle between radial divisions
174 : NGNode* node;
175 : int attachCircle = -1;
176 20 : if (attachLength > 0) {
177 1 : numCircles += 1;
178 : attachCircle = numCircles;
179 : }
180 100 : for (ic = 1; ic < numCircles + 1; ic++) {
181 80 : const std::string nodeIDStart = alphabeticalCode(ic, numCircles);
182 80 : if (ic == attachCircle) {
183 : spaceRad = attachLength;
184 : }
185 711 : for (ir = 1; ir < numRadDiv + 1; ir++) {
186 : // create Node
187 631 : const std::string nodeID = (myAlphaIDs ?
188 619 : nodeIDStart + toString<int>(ir) :
189 1286 : toString<int>(ir) + "/" + toString<int>(ic));
190 631 : node = new NGNode(nodeID, ir, ic);
191 631 : node->setX(radialToX((ic) * spaceRad, (ir - 1) * angle));
192 : node->setY(radialToY((ic) * spaceRad, (ir - 1) * angle));
193 631 : myNodeList.push_back(node);
194 : // create Links
195 631 : if (ir > 1 && ic != attachCircle) {
196 1090 : connect(findNode(ir - 1, ic), node);
197 : }
198 631 : if (ic > 1) {
199 1020 : connect(findNode(ir, ic - 1), node);
200 : }
201 631 : if (ir == numRadDiv && ic != attachCircle) {
202 79 : connect(node, findNode(1, ic));
203 : }
204 : }
205 : }
206 20 : if (hasCenter) {
207 : // node
208 18 : node = new NGNode(myAlphaIDs ? "A1" : "1", 0, 0, true);
209 : node->setX(0);
210 : node->setY(0);
211 17 : myNodeList.push_back(node);
212 : // links
213 117 : for (ir = 1; ir < numRadDiv + 1; ir++) {
214 100 : connect(node, findNode(ir, 1));
215 : }
216 : }
217 20 : }
218 :
219 :
220 : void
221 4383 : NGNet::connect(NGNode* node1, NGNode* node2) {
222 4419 : std::string id1 = node1->getID() + (myAlphaIDs ? "" : "to") + node2->getID();
223 4419 : std::string id2 = node2->getID() + (myAlphaIDs ? "" : "to") + node1->getID();
224 4383 : NGEdge* link1 = new NGEdge(id1, node1, node2, id2);
225 4383 : myEdgeList.push_back(link1);
226 4383 : }
227 :
228 : Distribution_Parameterized
229 273 : NGNet::getDistribution(const std::string& option) {
230 273 : const std::string& val = OptionsCont::getOptions().getString(option);
231 : try {
232 273 : return Distribution_Parameterized(option, 0, StringUtils::toDouble(val));
233 2 : } catch (NumberFormatException&) {
234 2 : return Distribution_Parameterized(val);
235 2 : }
236 : }
237 :
238 :
239 : void
240 91 : NGNet::toNB() const {
241 91 : Distribution_Parameterized perturbx = getDistribution("perturb-x");
242 91 : Distribution_Parameterized perturby = getDistribution("perturb-y");
243 182 : Distribution_Parameterized perturbz = getDistribution("perturb-z");
244 5608 : for (const NGNode* const ngNode : myNodeList) {
245 : // we need to sample in separate instructions because evaluation order is compiler dependent
246 5517 : Position perturb(perturbx.sample(), 0.);
247 5517 : perturb.sety(perturby.sample());
248 5517 : perturb.setz(perturbz.sample());
249 5517 : myNetBuilder.getNodeCont().insert(ngNode->buildNBNode(myNetBuilder, perturb));
250 : }
251 91 : const std::string type = OptionsCont::getOptions().getString("default.type");
252 182 : const double bidiProb = OptionsCont::getOptions().getFloat("bidi-probability");
253 8652 : for (const NGEdge* const ngEdge : myEdgeList) {
254 25683 : myNetBuilder.getEdgeCont().insert(ngEdge->buildNBEdge(myNetBuilder, type));
255 : // now, let's append the reverse directions...
256 8561 : if (!ngEdge->getEndNode()->connected(ngEdge->getStartNode(), true) && RandHelper::rand() <= bidiProb) {
257 21567 : myNetBuilder.getEdgeCont().insert(ngEdge->buildNBEdge(myNetBuilder, type, true));
258 : }
259 : }
260 : // add splits depending on turn-lane options
261 91 : const int turnLanes = OptionsCont::getOptions().getInt("turn-lanes");
262 91 : const bool lefthand = OptionsCont::getOptions().getBool("lefthand");
263 91 : if (turnLanes > 0) {
264 4 : const double turnLaneLength = OptionsCont::getOptions().getFloat("turn-lanes.length");
265 4 : NBEdgeCont& ec = myNetBuilder.getEdgeCont();
266 : EdgeVector allEdges;
267 142 : for (auto it = ec.begin(); it != ec.end(); ++it) {
268 138 : allEdges.push_back(it->second);
269 : }
270 142 : for (NBEdge* e : allEdges) {
271 138 : if (e->getToNode()->geometryLike()) {
272 31 : continue;
273 : }
274 : std::vector<NBEdgeCont::Split> splits;
275 : NBEdgeCont::Split split;
276 391 : for (int i = 0; i < e->getNumLanes() + turnLanes; ++i) {
277 284 : split.lanes.push_back(i);
278 : }
279 107 : split.pos = MAX2(0.0, e->getLength() - turnLaneLength);
280 107 : split.speed = e->getSpeed();
281 107 : split.node = new NBNode(e->getID() + "." + toString(split.pos), e->getGeometry().positionAtOffset(split.pos));
282 107 : split.idBefore = e->getID();
283 107 : split.idAfter = split.node->getID();
284 107 : split.offsetFactor = lefthand ? -1 : 1;
285 107 : if (turnLaneLength <= e->getLength() / 2) {
286 107 : split.offset = -0.5 * split.offsetFactor * turnLanes * e->getLaneWidth(0);
287 107 : if (e->getFromNode()->geometryLike()) {
288 : // shift the reverse direction explicitly as it will not get a turn lane
289 : NBEdge* reverse = nullptr;
290 88 : for (NBEdge* reverseCand : e->getFromNode()->getIncomingEdges()) {
291 57 : if (reverseCand->getFromNode() == e->getToNode()) {
292 : reverse = reverseCand;
293 : }
294 : }
295 31 : if (reverse != nullptr) {
296 : PositionVector g = reverse->getGeometry();
297 31 : g.move2side(-split.offset);
298 31 : reverse->setGeometry(g);
299 31 : }
300 : }
301 : }
302 107 : splits.push_back(split);
303 107 : ec.processSplits(e, splits,
304 : myNetBuilder.getNodeCont(),
305 : myNetBuilder.getDistrictCont(),
306 107 : myNetBuilder.getTLLogicCont());
307 107 : }
308 : }
309 91 : }
310 :
311 :
312 : void
313 3022 : NGNet::add(NGNode* node) {
314 3022 : myNodeList.push_back(node);
315 3022 : }
316 :
317 :
318 : void
319 4178 : NGNet::add(NGEdge* edge) {
320 4178 : myEdgeList.push_back(edge);
321 4178 : }
322 :
323 :
324 : int
325 11764 : NGNet::nodeNo() const {
326 11764 : return (int)myNodeList.size();
327 : }
328 :
329 :
330 : /****************************************************************************/
|