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 NBNetBuilder.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Thimor Bohn
18 : /// @author Sascha Krieg
19 : /// @author Michael Behrisch
20 : /// @author Walter Bamberger
21 : /// @date 20 Nov 2001
22 : ///
23 : // Instance responsible for building networks
24 : /****************************************************************************/
25 : #include <config.h>
26 :
27 : #include <string>
28 : #include <fstream>
29 : #include <utils/options/OptionsCont.h>
30 : #include <utils/common/MsgHandler.h>
31 : #include <utils/common/UtilExceptions.h>
32 : #include <utils/common/StringTokenizer.h>
33 : #include <utils/common/SUMOVehicleClass.h>
34 : #include <utils/common/SysUtils.h>
35 : #include <utils/common/ToString.h>
36 : #include <utils/geom/GeoConvHelper.h>
37 : #include "NBAlgorithms.h"
38 : #include "NBAlgorithms_Ramps.h"
39 : #include "NBAlgorithms_Railway.h"
40 : #include "NBHeightMapper.h"
41 : #include "NBNodeCont.h"
42 : #include "NBEdgeCont.h"
43 : #include "NBPTStop.h"
44 : #include "NBTrafficLightLogicCont.h"
45 : #include "NBDistrictCont.h"
46 : #include "NBDistrict.h"
47 : #include "NBRequest.h"
48 : #include "NBTypeCont.h"
49 : #include "NBNetBuilder.h"
50 :
51 :
52 : // ===========================================================================
53 : // method definitions
54 : // ===========================================================================
55 2483 : NBNetBuilder::NBNetBuilder() :
56 2483 : myEdgeCont(myTypeCont),
57 4966 : myNetworkHaveCrossings(false) {
58 2483 : }
59 :
60 :
61 4966 : NBNetBuilder::~NBNetBuilder() {}
62 :
63 :
64 : void
65 2483 : NBNetBuilder::applyOptions(OptionsCont& oc) {
66 : // apply options to type control
67 7449 : myTypeCont.setEdgeTypeDefaults(oc.getInt("default.lanenumber"), oc.getFloat("default.lanewidth"), oc.getFloat("default.speed"), oc.getFloat("default.friction"),
68 7449 : oc.getInt("default.priority"), parseVehicleClasses(oc.getString("default.allow"), oc.getString("default.disallow")),
69 2483 : SUMOXMLDefinitions::LaneSpreadFunctions.get(oc.getString("default.spreadtype")));
70 : // apply options to edge control
71 2483 : myEdgeCont.applyOptions(oc);
72 : // apply options to traffic light logics control
73 2473 : myTLLCont.applyOptions(oc);
74 2473 : NBEdge::setDefaultConnectionLength(oc.getFloat("default.connection-length"));
75 2473 : }
76 :
77 :
78 : void
79 2183 : NBNetBuilder::compute(OptionsCont& oc, const std::set<std::string>& explicitTurnarounds, bool mayAddOrRemove) {
80 : GeoConvHelper& geoConvHelper = GeoConvHelper::getProcessing();
81 :
82 2183 : const bool lefthand = oc.getBool("lefthand");
83 2183 : if (lefthand) {
84 23 : mirrorX();
85 : }
86 :
87 : // MODIFYING THE SETS OF NODES AND EDGES
88 : // Removes edges that are connecting the same node
89 4366 : long before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Removing self-loops"));
90 : int numRemovedEdges = 0;
91 2183 : numRemovedEdges += myNodeCont.removeSelfLoops(myDistrictCont, myEdgeCont, myTLLCont);
92 2183 : PROGRESS_TIME_MESSAGE(before);
93 6461 : if (mayAddOrRemove && oc.exists("remove-edges.isolated") && oc.getBool("remove-edges.isolated")) {
94 10 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Finding isolated roads"));
95 5 : numRemovedEdges += myNodeCont.removeIsolatedRoads(myDistrictCont, myEdgeCont);
96 5 : PROGRESS_TIME_MESSAGE(before);
97 : }
98 8533 : if (mayAddOrRemove && oc.exists("keep-edges.components") && oc.getInt("keep-edges.components") > 0) {
99 46 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Finding largest components"));
100 46 : const bool hasStops = oc.exists("ptstop-output") && oc.isSet("ptstop-output") && !myPTStopCont.getStops().empty();
101 23 : numRemovedEdges += myNodeCont.removeComponents(myDistrictCont, myEdgeCont, oc.getInt("keep-edges.components"), hasStops);
102 23 : PROGRESS_TIME_MESSAGE(before);
103 : }
104 6461 : if (mayAddOrRemove && oc.exists("keep-edges.postload") && oc.getBool("keep-edges.postload")) {
105 : // pre-process lines to set permissions
106 4 : if (!myPTLineCont.getLines().empty()) {
107 2 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Revising public transport stops based on pt lines"));
108 1 : myPTLineCont.process(myEdgeCont, myPTStopCont);
109 1 : PROGRESS_TIME_MESSAGE(before);
110 : }
111 7 : if (oc.isSet("keep-edges.explicit") || oc.isSet("keep-edges.input-file")) {
112 4 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Removing unwished edges"));
113 2 : numRemovedEdges += myEdgeCont.removeUnwishedEdges(myDistrictCont);
114 2 : PROGRESS_TIME_MESSAGE(before);
115 : }
116 4 : const int removed = myEdgeCont.removeEdgesBySpeed(myDistrictCont);
117 4 : if (removed > 0) {
118 0 : numRemovedEdges += removed;
119 0 : WRITE_MESSAGEF(TL(" Removed % edges because by minimum speed."), removed);
120 : }
121 4 : const int removed2 = myEdgeCont.removeEdgesByPermissions(myDistrictCont);
122 4 : if (removed2 > 0) {
123 1 : numRemovedEdges += removed2;
124 2 : WRITE_MESSAGEF(TL(" Removed % edges based on vClass."), removed2);
125 : }
126 : }
127 4366 : if (mayAddOrRemove && oc.getFloat("keep-lanes.min-width") > 0.) {
128 2183 : const int removed = myEdgeCont.removeLanesByWidth(myDistrictCont, oc.getFloat("keep-lanes.min-width"));
129 2183 : if (removed > 0) {
130 0 : numRemovedEdges += removed;
131 0 : WRITE_MESSAGEF(TL(" Removed % edges because of lane width."), removed);
132 : }
133 : }
134 8555 : if (mayAddOrRemove && oc.exists("junctions.attach-removed") && oc.getFloat("junctions.attach-removed") >= 0) {
135 1 : const int numSplit = myEdgeCont.attachRemoved(myNodeCont, myDistrictCont, oc.getFloat("junctions.attach-removed"));
136 1 : if (numSplit > 0) {
137 2 : WRITE_MESSAGEF(TL(" Split % edges to attach removed nodes"), numSplit);
138 : }
139 : }
140 : // Processing pt stops and lines
141 2183 : if (!myPTStopCont.getStops().empty()) {
142 352 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Processing public transport stops"));
143 528 : if (!(oc.exists("ptline-output") && oc.isSet("ptline-output"))
144 478 : && !oc.getBool("ptstop-output.no-bidi")) {
145 124 : myPTStopCont.localizePTStops(myEdgeCont);
146 : }
147 176 : myPTStopCont.assignEdgeForFloatingStops(myEdgeCont, 20);
148 176 : myPTStopCont.assignLanes(myEdgeCont);
149 176 : PROGRESS_TIME_MESSAGE(before);
150 : }
151 8533 : if (mayAddOrRemove && oc.exists("keep-edges.components") && oc.getInt("keep-edges.components") > 0) {
152 : // post process rail components unless they have stops
153 23 : numRemovedEdges += myNodeCont.removeRailComponents(myDistrictCont, myEdgeCont, myPTStopCont);
154 : }
155 : // removal is done, clean up roundabouts
156 2183 : if (numRemovedEdges > 0) {
157 18 : myEdgeCont.cleanupRoundabouts();
158 : }
159 :
160 2183 : if (!myPTLineCont.getLines().empty()) {
161 208 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Revising public transport stops based on pt lines"));
162 104 : myPTLineCont.process(myEdgeCont, myPTStopCont);
163 104 : PROGRESS_TIME_MESSAGE(before);
164 : }
165 :
166 2354 : if (!myPTStopCont.getStops().empty() && !oc.getBool("ptstop-output.no-bidi")) {
167 326 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Align pt stop id signs with corresponding edge id signs"));
168 163 : myPTStopCont.alignIdSigns();
169 163 : PROGRESS_TIME_MESSAGE(before);
170 : }
171 : // analyze and fix railway topology
172 : int numAddedBidi = 0;
173 4278 : if (oc.exists("railway.topology.all-bidi") && oc.getBool("railway.topology.all-bidi")) {
174 9 : NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false);
175 9 : numAddedBidi = NBRailwayTopologyAnalyzer::makeAllBidi(myEdgeCont);
176 4260 : } else if (oc.exists("railway.topology.repair") && oc.getBool("railway.topology.repair")) {
177 : // correct railway angles for angle-based connectivity heuristic
178 80 : myEdgeCont.checkGeometries(0, false,
179 : oc.getFloat("geometry.min-radius"), false,
180 40 : oc.getBool("geometry.min-radius.fix.railways"), true);
181 40 : NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false);
182 40 : numAddedBidi = NBRailwayTopologyAnalyzer::repairTopology(myEdgeCont, myPTStopCont, myPTLineCont);
183 : }
184 2183 : NBRailwaySignalGuesser::guessRailSignals(myNodeCont, myEdgeCont, myPTStopCont);
185 2183 : if (numAddedBidi > 0) {
186 : // update routes
187 44 : myPTLineCont.process(myEdgeCont, myPTStopCont, true);
188 : }
189 4278 : if (oc.exists("railway.topology.direction-priority") && oc.getBool("railway.topology.direction-priority")) {
190 1 : NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false); // recompute after new edges were added
191 1 : NBRailwayTopologyAnalyzer::extendDirectionPriority(myEdgeCont, true);
192 4364 : } else if (oc.exists("railway.topology.extend-priority")
193 6368 : && (oc.getBool("railway.topology.extend-priority") || oc.getStringVector("railway.topology.ptline-priority").size() > 0)) {
194 4 : NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false); // recompute after new edges were added
195 8 : if (oc.getStringVector("railway.topology.ptline-priority").size() > 0) {
196 1 : SVCPermissions vClasses = parseVehicleClasses(oc.getStringVector("railway.topology.ptline-priority"));
197 1 : NBRailwayTopologyAnalyzer::setPTLinePriority(myEdgeCont, myPTLineCont, vClasses);
198 : }
199 4 : NBRailwayTopologyAnalyzer::extendDirectionPriority(myEdgeCont, false);
200 : }
201 4278 : if (oc.exists("railway.topology.output") && oc.isSet("railway.topology.output")) {
202 29 : NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false); // recompute after new edges were added
203 29 : NBRailwayTopologyAnalyzer::analyzeTopology(myEdgeCont);
204 : }
205 4278 : if (oc.exists("railway.geometry.straighten") && oc.getBool("railway.geometry.straighten")) {
206 0 : NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false); // recompute after new edges were added
207 0 : NBRailwayGeometryHelper::straigthenCorrdidor(myEdgeCont, oc.getFloat("geometry.max-angle"));
208 : }
209 :
210 :
211 8549 : if (mayAddOrRemove && oc.exists("edges.join-tram-dist") && oc.getFloat("edges.join-tram-dist") >= 0) {
212 : // should come before joining junctions
213 14 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Joining tram edges"));
214 7 : int numJoinedTramEdges = myEdgeCont.joinTramEdges(myDistrictCont, myPTStopCont, myPTLineCont, oc.getFloat("edges.join-tram-dist"));
215 7 : PROGRESS_TIME_MESSAGE(before);
216 7 : if (numJoinedTramEdges > 0) {
217 14 : WRITE_MESSAGEF(TL(" Joined % tram edges into roads."), toString(numJoinedTramEdges));
218 : }
219 : }
220 4366 : if (oc.getBool("junctions.join")
221 6323 : || (oc.exists("ramps.guess") && oc.getBool("ramps.guess"))
222 4220 : || oc.getBool("tls.guess.joining")
223 8231 : || (oc.exists("tls.guess-signals") && oc.getBool("tls.guess-signals"))) {
224 : // preliminary geometry computations to determine the length of edges
225 : // This depends on turning directions and sorting of edge list
226 : // in case junctions are joined geometry computations have to be repeated
227 : // preliminary roundabout computations to avoid damaging roundabouts via junctions.join or ramps.guess
228 156 : NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false);
229 156 : NBNodesEdgesSorter::sortNodesEdges(myNodeCont);
230 156 : myEdgeCont.computeLaneShapes();
231 156 : myNodeCont.computeNodeShapes();
232 156 : myEdgeCont.computeEdgeShapes();
233 312 : if (oc.getBool("roundabouts.guess")) {
234 156 : myEdgeCont.guessRoundabouts();
235 : }
236 156 : const std::set<EdgeSet>& roundabouts = myEdgeCont.getRoundabouts();
237 156 : for (std::set<EdgeSet>::const_iterator it_round = roundabouts.begin();
238 170 : it_round != roundabouts.end(); ++it_round) {
239 : std::vector<std::string> nodeIDs;
240 111 : for (EdgeSet::const_iterator it_edge = it_round->begin(); it_edge != it_round->end(); ++it_edge) {
241 97 : nodeIDs.push_back((*it_edge)->getToNode()->getID());
242 : }
243 14 : myNodeCont.addJoinExclusion(nodeIDs);
244 14 : }
245 156 : NBNodeTypeComputer::validateRailCrossings(myNodeCont, myTLLCont);
246 4102 : } else if ((myEdgeCont.hasGuessedRoundabouts() || oc.getBool("crossings.guess")) && oc.getBool("roundabouts.guess")) {
247 48 : myEdgeCont.guessRoundabouts();
248 48 : myEdgeCont.markRoundabouts();
249 : }
250 : // join junctions (may create new "geometry"-nodes so it needs to come before removing these
251 6461 : if (mayAddOrRemove && oc.exists("junctions.join-exclude") && oc.isSet("junctions.join-exclude")) {
252 2 : myNodeCont.addJoinExclusion(oc.getStringVector("junctions.join-exclude"));
253 : }
254 2183 : int numJoined = myNodeCont.joinLoadedClusters(myDistrictCont, myEdgeCont, myTLLCont);
255 4366 : if (mayAddOrRemove && oc.getBool("junctions.join")) {
256 226 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Joining junction clusters"));
257 113 : numJoined += myNodeCont.joinJunctions(oc.getFloat("junctions.join-dist"), myDistrictCont, myEdgeCont, myTLLCont, myPTStopCont);
258 113 : PROGRESS_TIME_MESSAGE(before);
259 : }
260 2183 : if (numJoined > 0) {
261 200 : WRITE_MESSAGEF(TL(" Joined % junction cluster(s)."), toString(numJoined));
262 : }
263 6538 : if (mayAddOrRemove && oc.getFloat("junctions.join-same") >= 0) {
264 22 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Joining junctions with similar coordinates"));
265 11 : int numJoined2 = myNodeCont.joinSameJunctions(myDistrictCont, myEdgeCont, myTLLCont, oc.getFloat("junctions.join-same"));
266 11 : PROGRESS_TIME_MESSAGE(before);
267 11 : if (numJoined2 > 0) {
268 6 : WRITE_MESSAGEF(TL(" Joined % junctions."), toString(numJoined2));
269 : }
270 : }
271 : //
272 6461 : if (mayAddOrRemove && oc.exists("join-lanes") && oc.getBool("join-lanes")) {
273 2 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Joining lanes"));
274 1 : const int num = myEdgeCont.joinLanes(SVC_IGNORING) + myEdgeCont.joinLanes(SVC_PEDESTRIAN);
275 1 : PROGRESS_TIME_MESSAGE(before);
276 2 : WRITE_MESSAGEF(TL(" Joined lanes on % edges."), toString(num));
277 : }
278 : //
279 2183 : if (mayAddOrRemove) {
280 4366 : const bool removeGeometryNodes = oc.exists("geometry.remove") && oc.getBool("geometry.remove");
281 8621 : before = PROGRESS_BEGIN_TIME_MESSAGE("Removing empty nodes" + std::string(removeGeometryNodes ? " and geometry nodes" : ""));
282 : // removeUnwishedNodes needs turnDirections. @todo: try to call this less often
283 2183 : NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false);
284 2183 : const int numRemoved = myNodeCont.removeUnwishedNodes(myDistrictCont, myEdgeCont, myTLLCont, myPTStopCont, myPTLineCont, myParkingCont, removeGeometryNodes);
285 2183 : PROGRESS_TIME_MESSAGE(before);
286 4366 : WRITE_MESSAGEF(TL(" % nodes removed."), toString(numRemoved));
287 : }
288 :
289 : // MOVE TO ORIGIN
290 : // compute new boundary after network modifications have taken place
291 2183 : Boundary boundary;
292 61729 : for (std::map<std::string, NBNode*>::const_iterator it = myNodeCont.begin(); it != myNodeCont.end(); ++it) {
293 59546 : boundary.add(it->second->getPosition());
294 : }
295 108568 : for (std::map<std::string, NBEdge*>::const_iterator it = myEdgeCont.begin(); it != myEdgeCont.end(); ++it) {
296 106385 : boundary.add(it->second->getGeometry().getBoxBoundary());
297 : }
298 : geoConvHelper.setConvBoundary(boundary);
299 :
300 5483 : if (!oc.getBool("offset.disable-normalization") && oc.isDefault("offset.x") && oc.isDefault("offset.y")) {
301 1648 : moveToOrigin(geoConvHelper, lefthand);
302 : }
303 2183 : roundInputs();
304 2183 : geoConvHelper.computeFinal(lefthand); // information needed for location element fixed at this point
305 :
306 : // reset shapes and angles for stable re-computation
307 2183 : if (myNodeCont.resetNodeShapes()) {
308 156 : myEdgeCont.computeAngles();
309 : }
310 :
311 6370 : if (oc.exists("geometry.min-dist") && !oc.isDefault("geometry.min-dist")) {
312 6 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Reducing geometries"));
313 3 : myEdgeCont.reduceGeometries(oc.getFloat("geometry.min-dist"));
314 3 : PROGRESS_TIME_MESSAGE(before);
315 : }
316 : // @note: removing geometry can create similar edges so joinSimilarEdges must come afterwards
317 : // @note: likewise splitting can destroy similarities so joinSimilarEdges must come before
318 4366 : if (mayAddOrRemove && oc.getBool("edges.join")) {
319 38 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Joining similar edges"));
320 19 : const bool removeDuplicates = oc.getFloat("junctions.join-same") >= 0;
321 19 : myNodeCont.joinSimilarEdges(myDistrictCont, myEdgeCont, myTLLCont, removeDuplicates);
322 : // now we may have new chances to remove geometry if wished
323 38 : if (oc.exists("geometry.remove") && oc.getBool("geometry.remove")) {
324 1 : myNodeCont.removeUnwishedNodes(myDistrictCont, myEdgeCont, myTLLCont, myPTStopCont, myPTLineCont, myParkingCont, true);
325 : }
326 19 : PROGRESS_TIME_MESSAGE(before);
327 : }
328 4366 : if (oc.getBool("opposites.guess")) {
329 32 : PROGRESS_BEGIN_MESSAGE(TL("guessing opposite direction edges"));
330 16 : myEdgeCont.guessOpposites();
331 16 : PROGRESS_DONE_MESSAGE();
332 : }
333 : //
334 6549 : if (mayAddOrRemove && oc.exists("geometry.split") && oc.getBool("geometry.split")) {
335 8 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Splitting geometry edges"));
336 4 : myEdgeCont.splitGeometry(myDistrictCont, myNodeCont);
337 : // newly split junctions might also be joinable
338 4 : PROGRESS_TIME_MESSAGE(before);
339 8 : if (oc.getFloat("junctions.join-same") >= 0) {
340 2 : int numJoined3 = myNodeCont.joinSameJunctions(myDistrictCont, myEdgeCont, myTLLCont, oc.getFloat("junctions.join-same"));
341 2 : if (numJoined3 > 0) {
342 4 : WRITE_MESSAGEF(TL(" Joined % junctions after splitting geometry."), toString(numJoined3));
343 : }
344 : }
345 : }
346 : // turning direction
347 4366 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing turning directions"));
348 2183 : NBTurningDirectionsComputer::computeTurnDirections(myNodeCont);
349 2183 : PROGRESS_TIME_MESSAGE(before);
350 : // correct edge geometries to avoid overlap
351 4278 : if (oc.exists("geometry.avoid-overlap") && oc.getBool("geometry.avoid-overlap")) {
352 1590 : myNodeCont.avoidOverlap();
353 : }
354 :
355 : // GUESS TLS POSITIONS
356 4366 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Assigning nodes to traffic lights"));
357 4366 : if (oc.isSet("tls.set")) {
358 8 : std::vector<std::string> tlControlledNodes = oc.getStringVector("tls.set");
359 16 : TrafficLightType type = SUMOXMLDefinitions::TrafficLightTypes.get(oc.getString("tls.default-type"));
360 16 : for (std::vector<std::string>::const_iterator i = tlControlledNodes.begin(); i != tlControlledNodes.end(); ++i) {
361 8 : NBNode* node = myNodeCont.retrieve(*i);
362 8 : if (node == nullptr) {
363 0 : WRITE_WARNING("Building a tl-logic for junction '" + *i + "' is not possible." + "\n The junction '" + *i + "' is not known.");
364 : } else {
365 16 : myNodeCont.setAsTLControlled(node, myTLLCont, type);
366 : }
367 : }
368 8 : }
369 2183 : myNodeCont.guessTLs(oc, myTLLCont);
370 2183 : PROGRESS_TIME_MESSAGE(before);
371 :
372 : // guess ramps (after guessing tls because ramps should not be build at traffic lights)
373 4366 : const bool modifyRamps = mayAddOrRemove && (
374 6461 : (oc.exists("ramps.guess") && oc.getBool("ramps.guess"))
375 6359 : || (oc.exists("ramps.set") && oc.isSet("ramps.set")));
376 6343 : if (modifyRamps || (oc.exists("ramps.guess-acceleration-lanes") && oc.getBool("ramps.guess-acceleration-lanes"))) {
377 2095 : before = SysUtils::getCurrentMillis();
378 2095 : if (modifyRamps) {
379 177 : PROGRESS_BEGIN_MESSAGE(TL("Guessing and setting on-/off-ramps"));
380 : }
381 2095 : NBNodesEdgesSorter::sortNodesEdges(myNodeCont);
382 2095 : NBRampsComputer rc;
383 2095 : rc.computeRamps(*this, oc, mayAddOrRemove);
384 :
385 2095 : if (modifyRamps) {
386 59 : PROGRESS_TIME_MESSAGE(before);
387 : }
388 : }
389 : // guess bike lanes
390 6543 : if (mayAddOrRemove && ((oc.getBool("bikelanes.guess") || oc.getBool("bikelanes.guess.from-permissions")))) {
391 21 : const int bikelanes = myEdgeCont.guessSpecialLanes(SVC_BICYCLE, oc.getFloat("default.bikelane-width"),
392 : oc.getFloat("bikelanes.guess.min-speed"),
393 : oc.getFloat("bikelanes.guess.max-speed"),
394 14 : oc.getBool("bikelanes.guess.from-permissions"),
395 : "bikelanes.guess.exclude",
396 7 : myTLLCont);
397 14 : WRITE_MESSAGEF(TL("Guessed % bike lanes."), toString(bikelanes));
398 : }
399 :
400 : // guess sidewalks
401 6377 : if (mayAddOrRemove && ((oc.getBool("sidewalks.guess") || oc.getBool("sidewalks.guess.from-permissions")))) {
402 522 : const int sidewalks = myEdgeCont.guessSpecialLanes(SVC_PEDESTRIAN, oc.getFloat("default.sidewalk-width"),
403 : oc.getFloat("sidewalks.guess.min-speed"),
404 : oc.getFloat("sidewalks.guess.max-speed"),
405 348 : oc.getBool("sidewalks.guess.from-permissions"),
406 : "sidewalks.guess.exclude",
407 174 : myTLLCont);
408 348 : WRITE_MESSAGEF(TL("Guessed % sidewalks."), toString(sidewalks));
409 : }
410 : // check whether any not previously setable connections may be set now
411 2183 : myEdgeCont.recheckPostProcessConnections();
412 :
413 : // remap ids if wished
414 2183 : if (mayAddOrRemove) {
415 2183 : const bool numericalIDs = oc.getBool("numerical-ids");
416 2183 : const bool reservedIDs = oc.isSet("reserved-ids");
417 2183 : const bool keptIDs = oc.isSet("kept-ids");
418 2183 : int numChangedEdges = myEdgeCont.remapIDs(numericalIDs, reservedIDs, keptIDs, oc.getString("prefix.edge"), myPTStopCont);
419 2183 : int numChangedNodes = myNodeCont.remapIDs(numericalIDs, reservedIDs, keptIDs, oc.getString("prefix.junction"), myTLLCont);
420 2183 : if (numChangedEdges + numChangedNodes > 0) {
421 88 : WRITE_MESSAGEF(TL("Remapped % edge IDs and % node IDs."), toString(numChangedEdges), toString(numChangedNodes));
422 : }
423 : }
424 :
425 : //
426 4366 : if (oc.exists("geometry.max-angle")) {
427 4190 : myEdgeCont.checkGeometries(
428 4190 : DEG2RAD(oc.getFloat("geometry.max-angle")),
429 4190 : oc.getBool("geometry.max-angle.fix"),
430 : oc.getFloat("geometry.min-radius"),
431 4190 : oc.getBool("geometry.min-radius.fix"),
432 4190 : oc.getBool("geometry.min-radius.fix.railways"));
433 : }
434 :
435 : // GEOMETRY COMPUTATION
436 : //
437 4366 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Sorting nodes' edges"));
438 2183 : NBNodesEdgesSorter::sortNodesEdges(myNodeCont);
439 2183 : PROGRESS_TIME_MESSAGE(before);
440 2183 : myEdgeCont.computeLaneShapes();
441 : //
442 4366 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing node shapes"));
443 4366 : if (oc.exists("geometry.junction-mismatch-threshold")) {
444 4190 : myNodeCont.computeNodeShapes(oc.getFloat("geometry.junction-mismatch-threshold"));
445 : } else {
446 88 : myNodeCont.computeNodeShapes();
447 : }
448 2183 : PROGRESS_TIME_MESSAGE(before);
449 : //
450 4366 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing edge shapes"));
451 3839 : myEdgeCont.computeEdgeShapes(oc.getBool("geometry.max-grade.fix") ? oc.getFloat("geometry.max-grade") / 100 : -1);
452 2183 : PROGRESS_TIME_MESSAGE(before);
453 : // resort edges based on the node and edge shapes
454 2183 : NBNodesEdgesSorter::sortNodesEdges(myNodeCont, true);
455 2183 : NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false);
456 :
457 : // APPLY SPEED MODIFICATIONS
458 4366 : if (oc.exists("speed.offset")) {
459 2095 : const double speedOffset = oc.getFloat("speed.offset");
460 2095 : const double speedFactor = oc.getFloat("speed.factor");
461 2095 : const double speedMin = oc.getFloat("speed.minimum");
462 2095 : if (speedOffset != 0 || speedFactor != 1 || speedMin > 0) {
463 6 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Applying speed modifications"));
464 7 : for (const auto& it : myEdgeCont) {
465 5 : NBEdge* const e = it.second;
466 10 : for (int i = 0; i < e->getNumLanes(); i++) {
467 6 : e->setSpeed(i, MAX2(e->getLaneSpeed(i) * speedFactor + speedOffset, speedMin));
468 : }
469 : }
470 2 : PROGRESS_TIME_MESSAGE(before);
471 : }
472 : }
473 :
474 : // CONNECTIONS COMPUTATION
475 : //
476 4366 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing node types"));
477 2183 : NBNode::initRailSignalClasses(myNodeCont);
478 2183 : NBNodeTypeComputer::computeNodeTypes(myNodeCont, myTLLCont);
479 2183 : PROGRESS_TIME_MESSAGE(before);
480 : //
481 2183 : myNetworkHaveCrossings = oc.getBool("walkingareas");
482 4366 : if (mayAddOrRemove && oc.getBool("crossings.guess")) {
483 58 : myNetworkHaveCrossings = true;
484 58 : int crossings = 0;
485 3446 : for (std::map<std::string, NBNode*>::const_iterator i = myNodeCont.begin(); i != myNodeCont.end(); ++i) {
486 3388 : crossings += (*i).second->guessCrossings();
487 : }
488 116 : WRITE_MESSAGEF(TL("Guessed % pedestrian crossings."), toString(crossings));
489 : }
490 2183 : if (!myNetworkHaveCrossings) {
491 : bool haveValidCrossings = false;
492 : // recheck whether we had crossings in the input
493 57710 : for (std::map<std::string, NBNode*>::const_iterator i = myNodeCont.begin(); i != myNodeCont.end(); ++i) {
494 55705 : if (i->second->getCrossings().size() > 0) {
495 108 : myNetworkHaveCrossings = true;
496 : haveValidCrossings = true;
497 108 : break;
498 55597 : } else if (i->second->getCrossingsIncludingInvalid().size() > 0) {
499 0 : myNetworkHaveCrossings = true;
500 : }
501 : }
502 2113 : if (myNetworkHaveCrossings && !haveValidCrossings) {
503 : // initial crossings removed or invalidated, keep walkingareas
504 0 : oc.resetWritable();
505 0 : oc.set("walkingareas", "true");
506 : }
507 : }
508 :
509 2183 : if (!mayAddOrRemove && myNetworkHaveCrossings) {
510 : // crossings added via netedit
511 0 : oc.resetWritable();
512 0 : oc.set("no-internal-links", "false");
513 : }
514 :
515 : //
516 4366 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing priorities"));
517 2183 : NBEdgePriorityComputer::computeEdgePriorities(myNodeCont);
518 2183 : PROGRESS_TIME_MESSAGE(before);
519 : //
520 4366 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing approached edges"));
521 2183 : myEdgeCont.computeEdge2Edges(oc.getBool("no-left-connections"));
522 2183 : PROGRESS_TIME_MESSAGE(before);
523 : //
524 4366 : if (oc.getBool("roundabouts.guess")) {
525 4304 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Guessing and setting roundabouts"));
526 2152 : const int numGuessed = myEdgeCont.guessRoundabouts();
527 2152 : if (numGuessed > 0) {
528 30 : WRITE_MESSAGEF(TL(" Guessed % roundabout(s)."), toString(numGuessed));
529 : }
530 2152 : PROGRESS_TIME_MESSAGE(before);
531 : }
532 2183 : myEdgeCont.markRoundabouts();
533 : //
534 4366 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing approaching lanes"));
535 2183 : myEdgeCont.computeLanes2Edges();
536 2183 : PROGRESS_TIME_MESSAGE(before);
537 : //
538 4366 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Dividing of lanes on approached lanes"));
539 2183 : myNodeCont.computeLanes2Lanes();
540 2183 : myEdgeCont.sortOutgoingLanesConnections();
541 2183 : PROGRESS_TIME_MESSAGE(before);
542 : //
543 4366 : if (oc.getBool("fringe.guess")) {
544 4 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Guessing Network fringe"));
545 2 : const int numGuessed = myNodeCont.guessFringe();
546 2 : if (numGuessed > 0) {
547 4 : WRITE_MESSAGEF(TL(" Guessed % fringe nodes."), toString(numGuessed));
548 : }
549 2 : PROGRESS_TIME_MESSAGE(before);
550 : }
551 : //
552 4366 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Processing turnarounds"));
553 4366 : if (!oc.getBool("no-turnarounds")) {
554 1410 : myEdgeCont.appendTurnarounds(
555 2820 : oc.getBool("no-turnarounds.tls"),
556 2820 : oc.getBool("no-turnarounds.fringe"),
557 2820 : oc.getBool("no-turnarounds.except-deadend"),
558 2820 : oc.getBool("no-turnarounds.except-turnlane"),
559 2820 : oc.getBool("no-turnarounds.geometry"));
560 : } else {
561 1546 : myEdgeCont.appendTurnarounds(explicitTurnarounds, oc.getBool("no-turnarounds.tls"));
562 : }
563 6498 : if (oc.exists("railway.topology.repair.stop-turn") && oc.getBool("railway.topology.repair.stop-turn")
564 2220 : && myPTStopCont.getStops().size() > 0) {
565 : // allow direction reversal at all bidi-edges with stops
566 29 : myEdgeCont.appendRailwayTurnarounds(myPTStopCont);
567 : }
568 2183 : PROGRESS_TIME_MESSAGE(before);
569 : //
570 4366 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Rechecking of lane endings"));
571 2183 : myEdgeCont.recheckLanes();
572 2183 : PROGRESS_TIME_MESSAGE(before);
573 :
574 2363 : if (myNetworkHaveCrossings && !oc.getBool("no-internal-links")) {
575 4387 : for (std::map<std::string, NBNode*>::const_iterator i = myNodeCont.begin(); i != myNodeCont.end(); ++i) {
576 4211 : i->second->buildCrossingsAndWalkingAreas();
577 : }
578 : } else {
579 57544 : for (std::map<std::string, NBNode*>::const_iterator i = myNodeCont.begin(); i != myNodeCont.end(); ++i) {
580 : // needed by netedit if the last crossings was deleted from the network
581 : // and walkingareas have been invalidated since the last call to compute()
582 55537 : i->second->discardWalkingareas();
583 : }
584 4014 : if (oc.getBool("no-internal-links")) {
585 31903 : for (std::map<std::string, NBNode*>::const_iterator i = myNodeCont.begin(); i != myNodeCont.end(); ++i) {
586 31262 : i->second->discardAllCrossings(false);
587 : }
588 : }
589 : }
590 : // join traffic lights (after building connections)
591 4366 : if (oc.getBool("tls.join")) {
592 72 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Joining traffic light nodes"));
593 36 : myNodeCont.joinTLS(myTLLCont, oc.getFloat("tls.join-dist"));
594 36 : PROGRESS_TIME_MESSAGE(before);
595 : }
596 :
597 : // COMPUTING RIGHT-OF-WAY AND TRAFFIC LIGHT PROGRAMS
598 : //
599 4366 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing traffic light control information"));
600 2183 : myTLLCont.setTLControllingInformation(myEdgeCont, myNodeCont);
601 4276 : if (oc.exists("opendrive-files") && oc.isSet("opendrive-files")) {
602 23 : myTLLCont.setOpenDriveSignalParameters();
603 : }
604 2182 : PROGRESS_TIME_MESSAGE(before);
605 : //
606 4364 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing node logics"));
607 2182 : myNodeCont.computeLogics(myEdgeCont);
608 2182 : PROGRESS_TIME_MESSAGE(before);
609 :
610 : //
611 4364 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing traffic light logics"));
612 2182 : std::pair<int, int> numbers = myTLLCont.computeLogics(oc);
613 2182 : PROGRESS_TIME_MESSAGE(before);
614 2182 : std::string progCount = "";
615 2182 : if (numbers.first != numbers.second) {
616 27 : progCount = "(" + toString(numbers.second) + " programs) ";
617 : }
618 6546 : WRITE_MESSAGEF(TL(" % traffic light(s) %computed."), toString(numbers.first), progCount);
619 4299 : if (oc.exists("opendrive-files") && oc.isSet("opendrive-files") && oc.getBool("opendrive.signal-groups")) {
620 0 : myTLLCont.applyOpenDriveControllers(oc);
621 : }
622 :
623 108806 : for (std::map<std::string, NBEdge*>::const_iterator i = myEdgeCont.begin(); i != myEdgeCont.end(); ++i) {
624 106624 : (*i).second->sortOutgoingConnectionsByIndex();
625 : }
626 : // FINISHING INNER EDGES
627 : std::set<NBTrafficLightDefinition*> largeNodeTLS;
628 4364 : if (!oc.getBool("no-internal-links")) {
629 4623 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Building inner edges"));
630 : // walking areas shall only be built if crossings are wished as well
631 30023 : for (const auto& item : myNodeCont) {
632 28482 : if (item.second->buildInnerEdges() > NBTrafficLightDefinition::MIN_YELLOW_SECONDS) {
633 4808 : const std::set<NBTrafficLightDefinition*>& tlDefs = item.second->getControllingTLS();
634 4808 : largeNodeTLS.insert(tlDefs.begin(), tlDefs.end());
635 : }
636 : }
637 1541 : PROGRESS_TIME_MESSAGE(before);
638 : }
639 : // PATCH NODE SHAPES
640 4364 : if (oc.getFloat("junctions.scurve-stretch") > 0) {
641 : // @note: nodes have collected correction hints in buildInnerEdges()
642 4 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("stretching junctions to smooth geometries"));
643 2 : myEdgeCont.computeLaneShapes();
644 2 : myNodeCont.computeNodeShapes();
645 3 : myEdgeCont.computeEdgeShapes(oc.getBool("geometry.max-grade.fix") ? oc.getFloat("geometry.max-grade") / 100 : -1);
646 15 : for (const auto& item : myNodeCont) {
647 13 : item.second->buildInnerEdges();
648 : }
649 2 : PROGRESS_TIME_MESSAGE(before);
650 : }
651 2384 : if (myEdgeCont.getNumEdgeSplits() > 0 && !oc.getBool("no-internal-links")) {
652 : // edges with custom lengths were split, this has to take into account
653 : // internal edge lengths (after geometry computation)
654 86 : myEdgeCont.fixSplitCustomLength();
655 : }
656 : // recheck phases for large junctions
657 2914 : for (NBTrafficLightDefinition* def : largeNodeTLS) {
658 732 : myTLLCont.computeSingleLogic(oc, def);
659 : }
660 : // compute lane-to-lane node logics (require traffic lights and inner edges to be done)
661 2182 : myNodeCont.computeLogics2(myEdgeCont, oc);
662 :
663 : // remove guessed traffic lights at junctions without conflicts (requires computeLogics2)
664 2182 : myNodeCont.recheckGuessedTLS(myTLLCont);
665 :
666 : // compute keepClear status (requires computeLogics2)
667 2182 : myNodeCont.computeKeepClear();
668 :
669 : //
670 4364 : if (oc.isSet("street-sign-output")) {
671 2 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Generating street signs"));
672 1 : myEdgeCont.generateStreetSigns();
673 1 : PROGRESS_TIME_MESSAGE(before);
674 : }
675 :
676 :
677 4364 : if (lefthand != oc.getBool("flip-y-axis")) {
678 23 : mirrorX();
679 : }
680 :
681 6370 : if (oc.exists("geometry.check-overlap") && oc.getFloat("geometry.check-overlap") > 0) {
682 0 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Checking overlapping edges"));
683 0 : myEdgeCont.checkOverlap(oc.getFloat("geometry.check-overlap"), oc.getFloat("geometry.check-overlap.vertical-threshold"));
684 0 : PROGRESS_TIME_MESSAGE(before);
685 : }
686 2207 : if (geoConvHelper.getConvBoundary().getZRange() > 0 && oc.getFloat("geometry.max-grade") > 0) {
687 50 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Checking edge grade"));
688 : // user input is in %
689 25 : myEdgeCont.checkGrade(oc.getFloat("geometry.max-grade") / 100);
690 25 : PROGRESS_TIME_MESSAGE(before);
691 : }
692 :
693 : // find accesses for pt rail stops and add bidi-stops
694 2182 : if (!myPTStopCont.getStops().empty()) {
695 : // re-adapt stop lanes after adding special lanes and cutting edge shapes at junction
696 167 : myPTStopCont.assignLanes(myEdgeCont);
697 167 : before = SysUtils::getCurrentMillis();
698 : int numBidiStops = 0;
699 334 : if (!oc.getBool("ptstop-output.no-bidi")) {
700 163 : numBidiStops = myPTStopCont.generateBidiStops(myEdgeCont);
701 : } else {
702 4 : numBidiStops = myPTStopCont.countBidiStops(myEdgeCont);
703 : }
704 334 : PROGRESS_BEGIN_MESSAGE(TL("Find accesses for pt rail stops"));
705 167 : double maxRadius = oc.getFloat("railway.access-distance");
706 167 : double accessFactor = oc.getFloat("railway.access-factor");
707 167 : int maxCount = oc.getInt("railway.max-accesses");
708 167 : myPTStopCont.findAccessEdgesForRailStops(myEdgeCont, maxRadius, maxCount, accessFactor);
709 167 : PROGRESS_TIME_MESSAGE(before);
710 167 : if (numBidiStops > 0) {
711 77 : myPTLineCont.fixBidiStops(myEdgeCont);
712 : }
713 : }
714 2182 : myPTLineCont.removeInvalidEdges(myEdgeCont);
715 : // ensure that all turning lanes have sufficient permissions
716 2182 : myPTLineCont.fixPermissions();
717 4276 : if (oc.exists("ptline-clean-up") && oc.getBool("ptline-clean-up")) {
718 8 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Cleaning up public transport stops that are not served by any line"));
719 4 : std::set<std::string> usedStops = myPTLineCont.getServedPTStops();
720 4 : myPTStopCont.postprocess(usedStops);
721 4 : PROGRESS_TIME_MESSAGE(before);
722 : } else {
723 2178 : int numDeletedStops = myPTStopCont.cleanupDeleted(myEdgeCont);
724 2178 : if (numDeletedStops > 0) {
725 48 : WRITE_WARNINGF(TL("Removed % pt stops because they could not be assigned to the network"), toString(numDeletedStops));
726 : }
727 : }
728 :
729 6367 : if (oc.exists("ignore-change-restrictions") && !oc.isDefault("ignore-change-restrictions")) {
730 3 : SVCPermissions ignoring = parseVehicleClasses(oc.getStringVector("ignore-change-restrictions"));
731 3 : myEdgeCont.updateAllChangeRestrictions(ignoring);
732 : }
733 :
734 2182 : NBRequest::reportWarnings();
735 : // report on very large networks
736 6543 : if (MAX2(geoConvHelper.getConvBoundary().xmax(), geoConvHelper.getConvBoundary().ymax()) > 1000000 ||
737 2179 : MIN2(geoConvHelper.getConvBoundary().xmin(), geoConvHelper.getConvBoundary().ymin()) < -1000000) {
738 6 : WRITE_WARNING(TL("Network contains very large coordinates and will probably flicker in the GUI. Check for outlying nodes and make sure the network is shifted to the coordinate origin"));
739 : }
740 :
741 : // clean up OSM processing params
742 4276 : if (oc.exists("osm-files") && oc.isSet("osm-files")) {
743 35534 : for (auto item : myEdgeCont) {
744 35024 : item.second->unsetParameter(NBTrafficLightDefinition::OSM_DIRECTION);
745 : }
746 : }
747 2182 : }
748 :
749 :
750 : void
751 1648 : NBNetBuilder::moveToOrigin(GeoConvHelper& geoConvHelper, bool lefthand) {
752 3296 : long before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Moving network to origin"));
753 1648 : Boundary boundary = geoConvHelper.getConvBoundary();
754 1648 : const double x = -boundary.xmin();
755 1648 : const double y = -(lefthand ? boundary.ymax() : boundary.ymin());
756 : //if (lefthand) {
757 : // y = boundary.ymax();
758 : //}
759 40091 : for (std::map<std::string, NBNode*>::const_iterator i = myNodeCont.begin(); i != myNodeCont.end(); ++i) {
760 38443 : (*i).second->reshiftPosition(x, y);
761 : }
762 70676 : for (std::map<std::string, NBEdge*>::const_iterator i = myEdgeCont.begin(); i != myEdgeCont.end(); ++i) {
763 69028 : (*i).second->reshiftPosition(x, y);
764 : }
765 1719 : for (std::map<std::string, NBDistrict*>::const_iterator i = myDistrictCont.begin(); i != myDistrictCont.end(); ++i) {
766 71 : (*i).second->reshiftPosition(x, y);
767 : }
768 3511 : for (const auto& stopIt : myPTStopCont.getStops()) {
769 1863 : stopIt.second->reshiftPosition(x, y);
770 : }
771 1648 : geoConvHelper.moveConvertedBy(x, y);
772 1648 : PROGRESS_TIME_MESSAGE(before);
773 1648 : }
774 :
775 :
776 : void
777 2183 : NBNetBuilder::roundInputs() {
778 : // ensure that computed speeds (using during right-of-way computation) are the same as the written speeds
779 108568 : for (auto item : myEdgeCont) {
780 106385 : item.second->roundSpeed();
781 : }
782 : // ensure that derived junction and lane geometry computation is done with the same input
783 : // edge coordinates as those that will be written to the output.
784 61729 : for (std::map<std::string, NBNode*>::const_iterator i = myNodeCont.begin(); i != myNodeCont.end(); ++i) {
785 59546 : (*i).second->roundGeometry();
786 : }
787 108568 : for (std::map<std::string, NBEdge*>::const_iterator i = myEdgeCont.begin(); i != myEdgeCont.end(); ++i) {
788 106385 : (*i).second->roundGeometry();
789 : }
790 2183 : }
791 :
792 :
793 : void
794 46 : NBNetBuilder::mirrorX() {
795 : // mirror the network along the X-axis
796 875 : for (std::map<std::string, NBNode*>::const_iterator i = myNodeCont.begin(); i != myNodeCont.end(); ++i) {
797 829 : (*i).second->mirrorX();
798 : }
799 1180 : for (std::map<std::string, NBEdge*>::const_iterator i = myEdgeCont.begin(); i != myEdgeCont.end(); ++i) {
800 1134 : (*i).second->mirrorX();
801 : }
802 46 : for (std::map<std::string, NBDistrict*>::const_iterator i = myDistrictCont.begin(); i != myDistrictCont.end(); ++i) {
803 0 : (*i).second->mirrorX();
804 : }
805 105 : for (const auto& stopIt : myPTStopCont.getStops()) {
806 59 : stopIt.second->mirrorX();
807 : }
808 46 : }
809 :
810 :
811 : bool
812 593681 : NBNetBuilder::transformCoordinate(Position& from, bool includeInBoundary, GeoConvHelper* from_srs) {
813 593681 : Position orig(from);
814 : bool ok = true;
815 : if (GeoConvHelper::getNumLoaded() > 1
816 2786 : && GeoConvHelper::getLoaded().usingGeoProjection()
817 2612 : && from_srs != nullptr
818 2612 : && from_srs->usingGeoProjection()
819 596293 : && *from_srs != GeoConvHelper::getLoaded()) {
820 2612 : from_srs->cartesian2geo(from);
821 2612 : ok &= GeoConvHelper::getLoaded().x2cartesian(from, false);
822 : }
823 593681 : if (from_srs == nullptr || !GeoConvHelper::getProcessing().usingGeoProjection()) {
824 : // if getProcessing is not a geo-projection, assume it a cartesian transformation (i.e. shift)
825 593566 : ok &= GeoConvHelper::getProcessing().x2cartesian(from, includeInBoundary);
826 :
827 198619 : if (from_srs == nullptr && GeoConvHelper::getProcessing().usingGeoProjection()
828 165822 : && GeoConvHelper::getNumLoaded() > 0
829 593602 : && GeoConvHelper::getLoaded().usingGeoProjection()) {
830 : // apply geo patch to loaded geo-network (offset must match)
831 36 : from = from + GeoConvHelper::getLoaded().getOffset();
832 : }
833 : }
834 593681 : if (ok) {
835 593681 : const NBHeightMapper& hm = NBHeightMapper::get();
836 593681 : if (hm.ready()) {
837 1532 : if (from_srs != nullptr && from_srs->usingGeoProjection()) {
838 1528 : from_srs->cartesian2geo(orig);
839 : }
840 1532 : from.setz(hm.getZ(orig) + GeoConvHelper::getProcessing().getOffset().z());
841 : }
842 : }
843 : const double eps = 1e-6;
844 593681 : from.set(std::round(from.x() / eps) * eps, std::round(from.y() / eps) * eps, std::round(from.z() / eps) * eps);
845 593681 : return ok;
846 : }
847 :
848 :
849 : bool
850 204569 : NBNetBuilder::transformCoordinates(PositionVector& from, bool includeInBoundary, GeoConvHelper* from_srs) {
851 204569 : const double maxLength = OptionsCont::getOptions().getFloat("geometry.max-segment-length");
852 204569 : if (maxLength > 0 && from.size() > 1) {
853 : // transformation to cartesian coordinates must happen before we can check segment length
854 : PositionVector copy = from;
855 118 : for (int i = 0; i < (int) from.size(); i++) {
856 85 : transformCoordinate(copy[i], false);
857 : }
858 33 : addGeometrySegments(from, copy, maxLength);
859 33 : }
860 : bool ok = true;
861 720432 : for (int i = 0; i < (int) from.size(); i++) {
862 515863 : ok = ok && transformCoordinate(from[i], includeInBoundary, from_srs);
863 : }
864 204569 : return ok;
865 : }
866 :
867 :
868 : int
869 71 : NBNetBuilder::addGeometrySegments(PositionVector& from, const PositionVector& cartesian, const double maxLength) {
870 : // check lengths and insert new points where needed (in the original
871 : // coordinate system)
872 : int inserted = 0;
873 161 : for (int i = 0; i < (int)cartesian.size() - 1; i++) {
874 90 : Position start = from[i + inserted];
875 90 : Position end = from[i + inserted + 1];
876 90 : double length = cartesian[i].distanceTo(cartesian[i + 1]);
877 90 : const Position step = (end - start) * (maxLength / length);
878 : int steps = 0;
879 312 : while (length > maxLength) {
880 222 : length -= maxLength;
881 222 : steps++;
882 222 : from.insert(from.begin() + i + inserted + 1, start + (step * steps));
883 222 : inserted++;
884 : }
885 : }
886 71 : return inserted;
887 : }
888 :
889 :
890 : bool
891 894 : NBNetBuilder::runningNetedit() {
892 : // see GNELoadThread::fillOptions
893 1788 : return OptionsCont::getOptions().exists("new");
894 : }
895 :
896 :
897 : /****************************************************************************/
|